// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// @author BoringCrypto (https://github.com/boringcrypto/BoringSolidity/blob/master/contracts/libraries/BoringERC20.sol)
// solhint-disable avoid-low-level-calls
library BoringERC20 {
bytes4 private constant _SIG_SYMBOL = 0x95d89b41; // symbol()
bytes4 private constant _SIG_NAME = 0x06fdde03; // name()
bytes4 private constant _SIG_DECIMALS = 0x313ce567; // decimals()
bytes4 private constant _SIG_BALANCE_OF = 0x70a08231; // balanceOf(address)
bytes4 private constant _SIG_TRANSFER = 0xa9059cbb; // transfer(address,uint256)
bytes4 private constant _SIG_TRANSFER_FROM = 0x23b872dd; // transferFrom(address,address,uint256)
function returnDataToString(bytes memory data) internal pure returns (string memory) {
if (data.length >= 64) {
return abi.decode(data, (string));
} else if (data.length == 32) {
uint8 i = 0;
while (i < 32 && data[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && data[i] != 0; i++) {
bytesArray[i] = data[i];
}
return string(bytesArray);
} else {
return "???";
}
}
/// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string.
/// @param token The address of the ERC-20 token contract.
/// @return (string) Token symbol.
function safeSymbol(IERC20 token) internal view returns (string memory) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(_SIG_SYMBOL));
return success ? returnDataToString(data) : "???";
}
/// @notice Provides a safe ERC20.name version which returns '???' as fallback string.
/// @param token The address of the ERC-20 token contract.
/// @return (string) Token name.
function safeName(IERC20 token) internal view returns (string memory) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(_SIG_NAME));
return success ? returnDataToString(data) : "???";
}
/// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.
/// @param token The address of the ERC-20 token contract.
/// @return (uint8) Token decimals.
function safeDecimals(IERC20 token) internal view returns (uint8) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(_SIG_DECIMALS));
return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
}
/// @notice Provides a gas-optimized balance check to avoid a redundant ext code size check in addition to the
/// return data size check.
/// @param token The address of the ERC-20 token.
/// @param to The address of the user to check.
/// @return amount The token amount.
function safeBalanceOf(IERC20 token, address to) internal view returns (uint256 amount) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(_SIG_BALANCE_OF, to));
require(success && data.length >= 32, "BoringERC20: BalanceOf failed");
amount = abi.decode(data, (uint256));
}
/// @notice Provides a safe ERC20.transfer version for different ERC-20 implementations.
/// Reverts on a failed transfer.
/// @param token The address of the ERC-20 token.
/// @param to Transfer tokens to.
/// @param amount The token amount.
function safeTransfer(
IERC20 token,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(_SIG_TRANSFER, to, amount));
require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: Transfer failed");
}
/// @notice Provides a safe ERC20.transferFrom version for different ERC-20 implementations.
/// Reverts on a failed transfer.
/// @param token The address of the ERC-20 token.
/// @param from Transfer tokens from.
/// @param to Transfer tokens to.
/// @param amount The token amount.
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = address(token).call(
abi.encodeWithSelector(_SIG_TRANSFER_FROM, from, to, amount)
);
require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: TransferFrom failed");
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
interface CloberMarketDeployer {
/**
* @notice Emitted when a new market is deployed.
* @param market The address of the generated market.
*/
event Deploy(address indexed market);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
interface CloberMarketFactory {
/**
* @notice Emitted when a new volatile market is created.
* @param market The address of the new market.
* @param orderToken The address of the new market's order token.
* @param quoteToken The address of the new market's quote token.
* @param baseToken The address of the new market's base token.
* @param quoteUnit The amount that one raw amount represents in quote tokens.
* @param nonce The nonce for this market.
* @param makerFee The maker fee.
* Paid to the maker when negative, paid by the maker when positive.
* Every 10000 represents a 1% fee on trade volume.
* @param takerFee The taker fee.
* Paid by the taker.
* Every 10000 represents a 1% fee on trade volume.
* @param a The scale factor of the price points.
* @param r The common ratio between price points.
*/
event CreateVolatileMarket(
address indexed market,
address orderToken,
address quoteToken,
address baseToken,
uint256 quoteUnit,
uint256 nonce,
int24 makerFee,
uint24 takerFee,
uint128 a,
uint128 r
);
/**
* @notice Emitted when a new stable market is created.
* @param market The address of the new market.
* @param orderToken The address of the new market's order token.
* @param quoteToken The address of the new market's quote token.
* @param baseToken The address of the new market's base token.
* @param quoteUnit The amount that one raw amount represents in quote tokens.
* @param nonce The nonce for this market.
* @param makerFee The maker fee.
* Paid to the maker when negative, paid by the maker when positive.
* Every 10000 represents a 1% fee on trade volume.
* @param takerFee The taker fee.
* Paid by the taker.
* Every 10000 represents a 1% fee on trade volume.
* @param a The starting price point.
* @param d The common difference between price points.
*/
event CreateStableMarket(
address indexed market,
address orderToken,
address quoteToken,
address baseToken,
uint256 quoteUnit,
uint256 nonce,
int24 makerFee,
uint24 takerFee,
uint128 a,
uint128 d
);
/**
* @notice Emitted when the address of the owner has changed.
* @param previousOwner The address of the previous owner.
* @param newOwner The address of the new owner.
*/
event ChangeOwner(address previousOwner, address newOwner);
/**
* @notice Emitted when the DAO Treasury address has changed.
* @param previousTreasury The address of the previous DAO Treasury.
* @param newTreasury The address of the new DAO Treasury.
*/
event ChangeDaoTreasury(address previousTreasury, address newTreasury);
/**
* @notice Emitted when the host address has changed.
* @param market The address of the market that had a change of hosts.
* @param previousHost The address of the previous host.
* @param newHost The address of a new host.
*/
event ChangeHost(address indexed market, address previousHost, address newHost);
/**
* @notice Returns the address of the VolatileMarketDeployer.
* @return The address of the VolatileMarketDeployer.
*/
function volatileMarketDeployer() external view returns (address);
/**
* @notice Returns the address of the StableMarketDeployer.
* @return The address of the StableMarketDeployer.
*/
function stableMarketDeployer() external view returns (address);
/**
* @notice Returns the address of the OrderCanceler.
* @return The address of the OrderCanceler.
*/
function canceler() external view returns (address);
/**
* @notice Returns whether the specified token address has been registered as a quote token.
* @param token The address of the token to check.
* @return bool Whether the token is registered as a quote token.
*/
function registeredQuoteTokens(address token) external view returns (bool);
/**
* @notice Returns the address of the factory owner
* @return The address of the factory owner
*/
function owner() external view returns (address);
/**
* @notice Returns the address of the factory owner candidate
* @return The address of the factory owner candidate
*/
function futureOwner() external view returns (address);
/**
* @notice Returns the address of the DAO Treasury
* @return The address of the DAO Treasury
*/
function daoTreasury() external view returns (address);
/**
* @notice Returns the current nonce
* @return The current nonce
*/
function nonce() external view returns (uint256);
/**
* @notice Creates a new market with a VolatilePriceBook.
* @param host The address of the new market's host.
* @param quoteToken The address of the new market's quote token.
* @param baseToken The address of the new market's base token.
* @param quoteUnit The amount that one raw amount represents in quote tokens.
* @param makerFee The maker fee.
* Paid to the maker when negative, paid by the maker when positive.
* Every 10000 represents a 1% fee on trade volume.
* @param takerFee The taker fee.
* Paid by the taker.
* Every 10000 represents a 1% fee on trade volume.
* @param a The scale factor of the price points.
* @param r The common ratio between price points.
* @return The address of the created market.
*/
function createVolatileMarket(
address host,
address quoteToken,
address baseToken,
uint96 quoteUnit,
int24 makerFee,
uint24 takerFee,
uint128 a,
uint128 r
) external returns (address);
/**
* @notice Creates a new market with a StablePriceBook
* @param host The address of the new market's host
* @param quoteToken The address of the new market's quote token
* @param baseToken The address of the new market's base token
* @param quoteUnit The amount that one raw amount represents in quote tokens
* @param makerFee The maker fee.
* Paid to the maker when negative, paid by the maker when positive.
* Every 10000 represents a 1% fee on trade volume.
* @param takerFee The taker fee.
* Paid by the taker.
* Every 10000 represents a 1% fee on trade volume.
* @param a The starting price point.
* @param d The common difference between price points.
* @return The address of the created market.
*/
function createStableMarket(
address host,
address quoteToken,
address baseToken,
uint96 quoteUnit,
int24 makerFee,
uint24 takerFee,
uint128 a,
uint128 d
) external returns (address);
/**
* @notice Change the DAO Treasury address.
* @dev Only the factory owner can call this function.
* @param treasury The new address of the DAO Treasury.
*/
function changeDaoTreasury(address treasury) external;
/**
* @notice Sets the new owner address for this contract.
* @dev Only the factory owner can call this function.
* @param newOwner The new owner address for this contract.
*/
function prepareChangeOwner(address newOwner) external;
/**
* @notice Changes the owner of this contract to the address set by `prepareChangeOwner`.
* @dev Only the future owner can call this function.
*/
function executeChangeOwner() external;
/**
* @notice Returns the host address of the given market.
* @param market The address of the target market.
* @return The host address of the market.
*/
function getMarketHost(address market) external view returns (address);
/**
* @notice Prepares to set a new host address for the given market address.
* @dev Only the market host can call this function.
* @param market The market address for which the host will be changed.
* @param newHost The new host address for the given market.
*/
function prepareHandOverHost(address market, address newHost) external;
/**
* @notice Changes the host address of the given market to the address set by `prepareHandOverHost`.
* @dev Only the future market host can call this function.
* @param market The market address for which the host will be changed.
*/
function executeHandOverHost(address market) external;
/**
* @notice Computes the OrderNFT contract address.
* @param marketNonce The nonce to compute the OrderNFT contract address via CREATE2.
*/
function computeTokenAddress(uint256 marketNonce) external view returns (address);
enum MarketType {
NONE,
VOLATILE,
STABLE
}
/**
* @notice MarketInfo struct that contains information about a market.
* @param host The address of the market host.
* @param marketType The market type, either VOLATILE or STABLE.
* @param a The starting price point.
* @param factor The either the common ratio or common difference between price points.
* @param futureHost The address set by `prepareHandOverHost` to change the market host.
*/
struct MarketInfo {
address host;
MarketType marketType;
uint128 a;
uint128 factor;
address futureHost;
}
/**
* @notice Returns key information about the market.
* @param market The address of the market.
* @return marketInfo The MarketInfo structure of the given market.
*/
function getMarketInfo(address market) external view returns (MarketInfo memory marketInfo);
/**
* @notice Allows the specified token to be used as the quote token.
* @dev Only the factory owner can call this function.
* @param token The address of the token to register.
*/
function registerQuoteToken(address token) external;
/**
* @notice Revokes the token's right to be used as a quote token.
* @dev Only the factory owner can call this function.
* @param token The address of the token to unregister.
*/
function unregisterQuoteToken(address token) external;
/**
* @notice Returns the order token name.
* @param quoteToken The address of the market's quote token.
* @param baseToken The address of the market's base token.
* @param marketNonce The market nonce.
* @return The order token name.
*/
function formatOrderTokenName(
address quoteToken,
address baseToken,
uint256 marketNonce
) external view returns (string memory);
/**
* @notice Returns the order token symbol.
* @param quoteToken The address of a new market's quote token.
* @param baseToken The address of a new market's base token.
* @param marketNonce The market nonce.
* @return The order token symbol.
*/
function formatOrderTokenSymbol(
address quoteToken,
address baseToken,
uint256 marketNonce
) external view returns (string memory);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "./CloberOrderKey.sol";
interface CloberOrderBook {
/**
* @notice Emitted when an order is created.
* @param sender The address who sent the tokens to make the order.
* @param user The address with the rights to claim the proceeds of the order.
* @param rawAmount The ordered raw amount.
* @param orderIndex The order index.
* @param priceIndex The price book index.
* @param options LSB: 0 - Ask, 1 - Bid.
*/
event MakeOrder(
address indexed sender,
address indexed user,
uint64 rawAmount,
uint32 claimBounty,
uint256 orderIndex,
uint16 priceIndex,
uint8 options
);
/**
* @notice Emitted when an order takes from the order book.
* @param sender The address who sent the tokens to take the order.
* @param user The recipient address of the traded token.
* @param priceIndex The price book index.
* @param rawAmount The ordered raw amount.
* @param options MSB: 0 - Limit, 1 - Market / LSB: 0 - Ask, 1 - Bid.
*/
event TakeOrder(address indexed sender, address indexed user, uint16 priceIndex, uint64 rawAmount, uint8 options);
/**
* @notice Emitted when an order is canceled.
* @param user The owner of the order.
* @param rawAmount The raw amount remaining that was canceled.
* @param orderIndex The order index.
* @param priceIndex The price book index.
* @param isBid The flag indicating whether it's a bid order or an ask order.
*/
event CancelOrder(address indexed user, uint64 rawAmount, uint256 orderIndex, uint16 priceIndex, bool isBid);
/**
* @notice Emitted when the proceeds of an order is claimed.
* @param claimer The address that initiated the claim.
* @param user The owner of the order.
* @param rawAmount The ordered raw amount.
* @param bountyAmount The size of the claim bounty.
* @param orderIndex The order index.
* @param priceIndex The price book index.
* @param isBase The flag indicating whether the user receives the base token or the quote token.
*/
event ClaimOrder(
address indexed claimer,
address indexed user,
uint64 rawAmount,
uint256 bountyAmount,
uint256 orderIndex,
uint16 priceIndex,
bool isBase
);
/**
* @notice Emitted when a flash-loan is taken.
* @param caller The caller address of the flash-loan.
* @param borrower The address of the flash loan token receiver.
* @param quoteAmount The amount of quote tokens the user has borrowed.
* @param baseAmount The amount of base tokens the user has borrowed.
* @param earnedQuote The amount of quote tokens the protocol earned in quote tokens.
* @param earnedBase The amount of base tokens the protocol earned in base tokens.
*/
event Flash(
address indexed caller,
address indexed borrower,
uint256 quoteAmount,
uint256 baseAmount,
uint256 earnedQuote,
uint256 earnedBase
);
/**
* @notice A struct that represents an order.
* @param amount The raw amount not filled yet. In case of a stale order, the amount not claimed yet.
* @param claimBounty The bounty amount in gwei that can be collected by the party that fully claims the order.
* @param owner The address of the order owner.
*/
struct Order {
uint64 amount;
uint32 claimBounty;
address owner;
}
/**
* @notice Take orders better or equal to the given priceIndex and make an order with the remaining tokens.
* @dev `msg.value` will be used as the claimBounty.
* @param user The taker/maker address.
* @param priceIndex The price book index.
* @param rawAmount The raw quote amount to trade, utilized by bids.
* @param baseAmount The base token amount to trade, utilized by asks.
* @param options LSB: 0 - Ask, 1 - Bid. Second bit: 1 - Post only.
* @param data Custom callback data
* @return The order index. If an order is not made `type(uint256).max` is returned instead.
*/
function limitOrder(
address user,
uint16 priceIndex,
uint64 rawAmount,
uint256 baseAmount,
uint8 options,
bytes calldata data
) external payable returns (uint256);
/**
* @notice Returns the expected input amount and output amount.
* @param limitPriceIndex The price index to take until.
* @param rawAmount The raw amount to trade.
* Bid & expendInput => Used as input amount.
* Bid & !expendInput => Not used.
* Ask & expendInput => Not used.
* Ask & !expendInput => Used as output amount.
* @param baseAmount The base token amount to trade.
* Bid & expendInput => Not used.
* Bid & !expendInput => Used as output amount.
* Ask & expendInput => Used as input amount.
* Ask & !expendInput => Not used.
* @param options LSB: 0 - Ask, 1 - Bid. Second bit: 1 - expend input.
*/
function getExpectedAmount(
uint16 limitPriceIndex,
uint64 rawAmount,
uint256 baseAmount,
uint8 options
) external view returns (uint256, uint256);
/**
* @notice Take opens orders until certain conditions are met.
* @param user The taker address.
* @param limitPriceIndex The price index to take until.
* @param rawAmount The raw amount to trade.
* This value is used as the maximum input amount by bids and minimum output amount by asks.
* @param baseAmount The base token amount to trade.
* This value is used as the maximum input amount by asks and minimum output amount by bids.
* @param options LSB: 0 - Ask, 1 - Bid. Second bit: 1 - expend input.
* @param data Custom callback data.
*/
function marketOrder(
address user,
uint16 limitPriceIndex,
uint64 rawAmount,
uint256 baseAmount,
uint8 options,
bytes calldata data
) external;
/**
* @notice Cancel orders.
* @dev The length of orderKeys must be controlled by the caller to avoid block gas limit exceeds.
* @param receiver The address to receive canceled tokens.
* @param orderKeys The order keys of the orders to cancel.
*/
function cancel(address receiver, OrderKey[] calldata orderKeys) external;
/**
* @notice Claim the proceeds of orders.
* @dev The length of orderKeys must be controlled by the caller to avoid block gas limit exceeds.
* @param claimer The address to receive the claim bounties.
* @param orderKeys The order keys of the orders to claim.
*/
function claim(address claimer, OrderKey[] calldata orderKeys) external;
/**
* @notice Flash loan the tokens in the OrderBook.
* @param borrower The address to receive the loan.
* @param quoteAmount The quote token amount to borrow.
* @param baseAmount The base token amount to borrow.
* @param data The user's custom callback data.
*/
function flash(
address borrower,
uint256 quoteAmount,
uint256 baseAmount,
bytes calldata data
) external;
/**
* @notice Returns the quote unit amount.
* @return The amount that one raw amount represent in quote tokens.
*/
function quoteUnit() external view returns (uint256);
/**
* @notice Returns the maker fee.
* Paid to the maker when negative, paid by the maker when positive.
* Every 10000 represents a 1% fee on trade volume.
* @return The maker fee. 100 = 1bp.
*/
function makerFee() external view returns (int24);
/**
* @notice Returns the take fee
* Paid by the taker.
* Every 10000 represents a 1% fee on trade volume.
* @return The taker fee. 100 = 1bps.
*/
function takerFee() external view returns (uint24);
/**
* @notice Returns the address of the order NFT contract.
* @return The address of the order NFT contract.
*/
function orderToken() external view returns (address);
/**
* @notice Returns the address of the quote token.
* @return The address of the quote token.
*/
function quoteToken() external view returns (address);
/**
* @notice Returns the address of the base token.
* @return The address of the base token.
*/
function baseToken() external view returns (address);
/**
* @notice Returns the current total open amount at the given price.
* @param isBid The flag to choose which side to check the depth for.
* @param priceIndex The price book index.
* @return The total open amount.
*/
function getDepth(bool isBid, uint16 priceIndex) external view returns (uint64);
/**
* @notice Returns the fee balance that has not been collected yet.
* @return quote The current fee balance for the quote token.
* @return base The current fee balance for the base token.
*/
function getFeeBalance() external view returns (uint128 quote, uint128 base);
/**
* @notice Returns the amount of tokens that can be collected by the host.
* @param token The address of the token to be collected.
* @return The amount of tokens that can be collected by the host.
*/
function uncollectedHostFees(address token) external view returns (uint256);
/**
* @notice Returns the amount of tokens that can be collected by the dao treasury.
* @param token The address of the token to be collected.
* @return The amount of tokens that can be collected by the dao treasury.
*/
function uncollectedProtocolFees(address token) external view returns (uint256);
/**
* @notice Returns whether the order book is empty or not.
* @param isBid The flag to choose which side to check the emptiness of.
* @return Whether the order book is empty or not on that side.
*/
function isEmpty(bool isBid) external view returns (bool);
/**
* @notice Returns the order information.
* @param orderKey The order key of the order.
* @return The order struct of the given order key.
*/
function getOrder(OrderKey calldata orderKey) external view returns (Order memory);
/**
* @notice Returns the lowest ask price index or the highest bid price index.
* @param isBid Returns the lowest ask price if false, highest bid price if true.
* @return The current price index. If the order book is empty, it will revert.
*/
function bestPriceIndex(bool isBid) external view returns (uint16);
/**
* @notice Converts a raw amount to its corresponding base amount using a given price index.
* @param rawAmount The raw amount to be converted.
* @param priceIndex The index of the price to be used for the conversion.
* @param roundingUp Specifies whether the result should be rounded up or down.
* @return The converted base amount.
*/
function rawToBase(
uint64 rawAmount,
uint16 priceIndex,
bool roundingUp
) external view returns (uint256);
/**
* @notice Converts a raw amount to its corresponding quote amount.
* @param rawAmount The raw amount to be converted.
* @return The converted quote amount.
*/
function rawToQuote(uint64 rawAmount) external view returns (uint256);
/**
* @notice Converts a base amount to its corresponding raw amount using a given price index.
* @param baseAmount The base amount to be converted.
* @param priceIndex The index of the price to be used for the conversion.
* @param roundingUp Specifies whether the result should be rounded up or down.
* @return The converted raw amount.
*/
function baseToRaw(
uint256 baseAmount,
uint16 priceIndex,
bool roundingUp
) external view returns (uint64);
/**
* @notice Converts a quote amount to its corresponding raw amount.
* @param quoteAmount The quote amount to be converted.
* @param roundingUp Specifies whether the result should be rounded up or down.
* @return The converted raw amount.
*/
function quoteToRaw(uint256 quoteAmount, bool roundingUp) external view returns (uint64);
/**
* @notice Collects fees for either the protocol or host.
* @param token The token address to collect. It should be the quote token or the base token.
* @param destination The destination address to transfer fees.
* It should be the dao treasury address or the host address.
*/
function collectFees(address token, address destination) external;
/**
* @notice Change the owner of the order.
* @dev Only the OrderToken contract can call this function.
* @param orderKey The order key of the order.
* @param newOwner The new owner address.
*/
function changeOrderOwner(OrderKey calldata orderKey, address newOwner) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/**
* @notice A struct that represents a unique key for an order.
* @param isBid The flag indicating whether it's a bid order or an ask order.
* @param priceIndex The price book index.
* @param orderIndex The order index.
*/
struct OrderKey {
bool isBid;
uint16 priceIndex;
uint256 orderIndex;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "./CloberOrderKey.sol";
interface CloberOrderNFT is IERC721, IERC721Metadata {
/**
* @notice Returns the base URI for the metadata of this NFT collection.
* @return The base URI for the metadata of this NFT collection.
*/
function baseURI() external view returns (string memory);
/**
* @notice Returns the address of the market contract that manages this token.
* @return The address of the market contract that manages this token.
*/
function market() external view returns (address);
/**
* @notice Returns the address of contract owner.
* @return The address of the contract owner.
*/
function owner() external view returns (address);
/**
* @notice Called when a new token is minted.
* @param to The receiver address of the minted token.
* @param tokenId The id of the token minted.
*/
function onMint(address to, uint256 tokenId) external;
/**
* @notice Called when a token is burned.
* @param tokenId The id of the token burned.
*/
function onBurn(uint256 tokenId) external;
/**
* @notice Changes the base URI for the metadata of this NFT collection.
* @param newBaseURI The new base URI for the metadata of this NFT collection.
*/
function changeBaseURI(string memory newBaseURI) external;
/**
* @notice Decodes a token id into an order key.
* @param id The id to decode.
* @return The order key corresponding to the given id.
*/
function decodeId(uint256 id) external pure returns (OrderKey memory);
/**
* @notice Encodes an order key to a token id.
* @param orderKey The order key to encode.
* @return The id corresponding to the given order key.
*/
function encodeId(OrderKey memory orderKey) external pure returns (uint256);
/**
* @notice Cancels orders with token ids.
* @dev Only the OrderCanceler can call this function.
* @param from The address of the owner of the tokens.
* @param tokenIds The ids of the tokens to cancel.
* @param receiver The address to send the underlying assets to.
*/
function cancel(
address from,
uint256[] calldata tokenIds,
address receiver
) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "./CloberMarketDeployer.sol";
interface CloberStableMarketDeployer is CloberMarketDeployer {
/**
* @notice Deploys a new stable market.
* @dev Only the market factory can call this function.
* @param orderToken The address of the new market's order token.
* @param quoteToken The quote token address.
* @param baseToken The base token address.
* @param salt The salt used to compute the address of the contract.
* @param quoteUnit The amount that one raw amount represents in quote tokens.
* @param makerFee The maker fee.
* Paid to the maker when negative, paid by the maker when positive.
* Every 10000 represents a 1% fee on trade volume.
* @param takerFee The taker fee.
* Paid by the taker.
* Every 10000 represents a 1% fee on trade volume.
* @param a The starting price point.
* @param d The common difference between price points.
* @return The address of the deployed stable market.
*/
function deploy(
address orderToken,
address quoteToken,
address baseToken,
bytes32 salt,
uint96 quoteUnit,
int24 makerFee,
uint24 takerFee,
uint128 a,
uint128 d
) external returns (address);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "./CloberMarketDeployer.sol";
interface CloberVolatileMarketDeployer is CloberMarketDeployer {
/**
* @notice Deploy a new volatile market.
* @dev Only the market factory can call this function.
* @param orderToken The address of the new market's order token.
* @param quoteToken The quote token address.
* @param baseToken The base token address.
* @param salt The salt used to compute the address of the contract.
* @param quoteUnit The amount that one raw amount represents in quote tokens.
* @param makerFee The maker fee.
* Paid to the maker when negative, paid by the maker when positive.
* Every 10000 represents a 1% fee on trade volume.
* @param takerFee The taker fee.
* Paid by the taker.
* Every 10000 represents a 1% fee on trade volume.
* @param a The scale factor of the price points.
* @param r The common ratio between price points.
* @return The address of the deployed volatile market.
*/
function deploy(
address orderToken,
address quoteToken,
address baseToken,
bytes32 salt,
uint96 quoteUnit,
int24 makerFee,
uint24 takerFee,
uint128 a,
uint128 r
) external returns (address);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Create2.sol)
pragma solidity ^0.8.0;
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/
library Create2 {
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/
function deploy(
uint256 amount,
bytes32 salt,
bytes memory bytecode
) internal returns (address) {
address addr;
require(address(this).balance >= amount, "Create2: insufficient balance");
require(bytecode.length != 0, "Create2: bytecode length is zero");
/// @solidity memory-safe-assembly
assembly {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
}
require(addr != address(0), "Create2: Failed on deploy");
return addr;
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(
bytes32 salt,
bytes32 bytecodeHash,
address deployer
) internal pure returns (address) {
bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash));
return address(uint160(uint256(_data)));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// SPDX-License-Identifier: -
// License: https://license.clober.io/LICENSE.pdf
pragma solidity ^0.8.0;
library Errors {
error CloberError(uint256 errorCode); // 0x1d25260a
uint256 public constant ACCESS = 0;
uint256 public constant FAILED_TO_SEND_VALUE = 1;
uint256 public constant INSUFFICIENT_BALANCE = 2;
uint256 public constant OVERFLOW_UNDERFLOW = 3;
uint256 public constant EMPTY_INPUT = 4;
uint256 public constant DELEGATE_CALL = 5;
uint256 public constant DEADLINE = 6;
uint256 public constant NOT_IMPLEMENTED_INTERFACE = 7;
uint256 public constant INVALID_FEE = 8;
uint256 public constant REENTRANCY = 9;
uint256 public constant POST_ONLY = 10;
uint256 public constant SLIPPAGE = 11;
uint256 public constant QUEUE_REPLACE_FAILED = 12;
uint256 public constant INVALID_COEFFICIENTS = 13;
uint256 public constant INVALID_ID = 14;
uint256 public constant INVALID_QUOTE_TOKEN = 15;
uint256 public constant INVALID_PRICE = 16;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
// SPDX-License-Identifier: -
// License: https://license.clober.io/LICENSE.pdf
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/CloberMarketFactory.sol";
import "./interfaces/CloberVolatileMarketDeployer.sol";
import "./interfaces/CloberStableMarketDeployer.sol";
import "./Errors.sol";
import "./utils/RevertOnDelegateCall.sol";
import "./utils/ReentrancyGuard.sol";
import "./OrderNFT.sol";
import "./utils/BoringERC20.sol";
contract MarketFactory is CloberMarketFactory, ReentrancyGuard, RevertOnDelegateCall {
using BoringERC20 for IERC20;
uint24 private constant _MAX_FEE = 500000; // 50%
int24 private constant _MIN_FEE = -500000; // -50%
uint24 private constant _VOLATILE_MIN_NET_FEE = 400; // 0.04%
uint24 private constant _STABLE_MIN_NET_FEE = 80; // 0.008%
uint256 private immutable _cachedChainId;
address public immutable override volatileMarketDeployer;
address public immutable override stableMarketDeployer;
address public immutable override canceler;
bytes32 private immutable _orderTokenBytecodeHash;
mapping(address => bool) public override registeredQuoteTokens;
address public override owner;
address public override futureOwner;
address public override daoTreasury;
uint256 public override nonce;
mapping(address => MarketInfo) private _marketInfos;
modifier onlyOwner() {
_checkOwner();
_;
}
function _checkOwner() internal view {
if (msg.sender != owner) {
revert Errors.CloberError(Errors.ACCESS);
}
}
modifier onlyRegisteredQuoteToken(address token) {
if (!registeredQuoteTokens[token]) {
revert Errors.CloberError(Errors.INVALID_QUOTE_TOKEN);
}
_;
}
constructor(
address volatileMarketDeployer_,
address stableMarketDeployer_,
address initialDaoTreasury,
address canceler_,
address[] memory initialQuoteTokenRegistrations_
) {
_cachedChainId = block.chainid;
owner = msg.sender;
emit ChangeOwner(address(0), msg.sender);
volatileMarketDeployer = volatileMarketDeployer_;
stableMarketDeployer = stableMarketDeployer_;
daoTreasury = initialDaoTreasury;
emit ChangeDaoTreasury(address(0), initialDaoTreasury);
_orderTokenBytecodeHash = keccak256(
abi.encodePacked(type(OrderNFT).creationCode, abi.encode(address(this), canceler_))
);
canceler = canceler_;
for (uint256 i = 0; i < initialQuoteTokenRegistrations_.length; ++i) {
registeredQuoteTokens[initialQuoteTokenRegistrations_[i]] = true;
}
}
function createVolatileMarket(
address marketHost,
address quoteToken,
address baseToken,
uint96 quoteUnit,
int24 makerFee,
uint24 takerFee,
uint128 a,
uint128 r
) external revertOnDelegateCall onlyRegisteredQuoteToken(quoteToken) returns (address market) {
_checkFee(marketHost, makerFee, takerFee, _VOLATILE_MIN_NET_FEE);
bytes32 salt = _calculateSalt(nonce);
address orderToken = _deployToken(salt);
if (quoteUnit == 0) {
revert Errors.CloberError(Errors.EMPTY_INPUT);
}
market = CloberVolatileMarketDeployer(volatileMarketDeployer).deploy(
orderToken,
quoteToken,
baseToken,
salt,
quoteUnit,
makerFee,
takerFee,
a,
r
);
emit CreateVolatileMarket(
market,
orderToken,
quoteToken,
baseToken,
quoteUnit,
nonce,
makerFee,
takerFee,
a,
r
);
_storeMarketInfo(market, marketHost, MarketType.VOLATILE, a, r);
_initToken(orderToken, quoteToken, baseToken, nonce, market);
nonce++;
}
function createStableMarket(
address marketHost,
address quoteToken,
address baseToken,
uint96 quoteUnit,
int24 makerFee,
uint24 takerFee,
uint128 a,
uint128 d
) external revertOnDelegateCall onlyRegisteredQuoteToken(quoteToken) returns (address market) {
_checkFee(marketHost, makerFee, takerFee, _STABLE_MIN_NET_FEE);
bytes32 salt = _calculateSalt(nonce);
address orderToken = _deployToken(salt);
if (quoteUnit == 0) {
revert Errors.CloberError(Errors.EMPTY_INPUT);
}
market = CloberStableMarketDeployer(stableMarketDeployer).deploy(
orderToken,
quoteToken,
baseToken,
salt,
quoteUnit,
makerFee,
takerFee,
a,
d
);
emit CreateStableMarket(market, orderToken, quoteToken, baseToken, quoteUnit, nonce, makerFee, takerFee, a, d);
_storeMarketInfo(market, marketHost, MarketType.STABLE, a, d);
_initToken(orderToken, quoteToken, baseToken, nonce, market);
nonce++;
}
function changeDaoTreasury(address treasury) external onlyOwner {
emit ChangeDaoTreasury(daoTreasury, treasury);
daoTreasury = treasury;
}
function prepareChangeOwner(address newOwner) external onlyOwner {
futureOwner = newOwner;
}
function executeChangeOwner() external {
address newOwner = futureOwner;
if (msg.sender != newOwner) {
revert Errors.CloberError(Errors.ACCESS);
}
emit ChangeOwner(owner, newOwner);
owner = newOwner;
delete futureOwner;
}
function getMarketHost(address market) external view returns (address) {
return _marketInfos[market].host;
}
function prepareHandOverHost(address market, address newHost) external {
address previousHost = _marketInfos[market].host;
if (previousHost != msg.sender) {
revert Errors.CloberError(Errors.ACCESS);
}
_marketInfos[market].futureHost = newHost;
}
function executeHandOverHost(address market) external {
MarketInfo storage info = _marketInfos[market];
address previousHost = info.host;
address newHost = info.futureHost;
if (newHost != msg.sender) {
revert Errors.CloberError(Errors.ACCESS);
}
info.host = newHost;
delete info.futureHost;
emit ChangeHost(market, previousHost, newHost);
}
function _checkFee(
address marketHost,
int24 makerFee,
uint24 takerFee,
uint24 minNetFee
) internal view {
// check makerFee
if (makerFee < _MIN_FEE || int24(_MAX_FEE) < makerFee) {
revert Errors.CloberError(Errors.INVALID_FEE);
}
// check takerFee
// takerFee is always positive
if (_MAX_FEE < takerFee) {
revert Errors.CloberError(Errors.INVALID_FEE);
}
// check net fee
if (marketHost != owner && int256(uint256(takerFee)) + makerFee < int256(uint256(minNetFee))) {
revert Errors.CloberError(Errors.INVALID_FEE);
} else if (makerFee < 0 && int256(uint256(takerFee)) + makerFee < 0) {
revert Errors.CloberError(Errors.INVALID_FEE);
}
}
function _deployToken(bytes32 salt) internal returns (address) {
return address(new OrderNFT{salt: salt}(address(this), canceler));
}
function _initToken(
address token,
address quoteToken,
address baseToken,
uint256 marketNonce,
address market
) internal {
OrderNFT(token).init(
formatOrderTokenName(quoteToken, baseToken, marketNonce),
formatOrderTokenSymbol(quoteToken, baseToken, marketNonce),
market
);
}
function _storeMarketInfo(
address market,
address host,
MarketType marketType,
uint128 a,
uint128 factor
) internal {
if (host == address(0)) {
revert Errors.CloberError(Errors.EMPTY_INPUT);
}
_marketInfos[market] = MarketInfo({
host: host,
marketType: marketType,
a: a,
factor: factor,
futureHost: address(0)
});
emit ChangeHost(market, address(0), host);
}
function computeTokenAddress(uint256 marketNonce) external view returns (address) {
return Create2.computeAddress(_calculateSalt(marketNonce), _orderTokenBytecodeHash);
}
function getMarketInfo(address market) external view returns (MarketInfo memory) {
return _marketInfos[market];
}
function registerQuoteToken(address token) external onlyOwner {
registeredQuoteTokens[token] = true;
}
function unregisterQuoteToken(address token) external onlyOwner {
registeredQuoteTokens[token] = false;
}
function _calculateSalt(uint256 marketNonce) internal view returns (bytes32) {
return keccak256(abi.encode(_cachedChainId, marketNonce));
}
function formatOrderTokenName(
address quoteToken,
address baseToken,
uint256 marketNonce
) public view returns (string memory) {
return
string(
abi.encodePacked(
"Clober Order: ",
IERC20(baseToken).safeSymbol(),
"/",
IERC20(quoteToken).safeSymbol(),
"(",
Strings.toString(marketNonce),
")"
)
);
}
function formatOrderTokenSymbol(
address quoteToken,
address baseToken,
uint256 marketNonce
) public view returns (string memory) {
return
string(
abi.encodePacked(
"CLOB-",
IERC20(baseToken).safeSymbol(),
"/",
IERC20(quoteToken).safeSymbol(),
"(",
Strings.toString(marketNonce),
")"
)
);
}
}
// SPDX-License-Identifier: -
// License: https://license.clober.io/LICENSE.pdf
pragma solidity ^0.8.0;
import "../Errors.sol";
import "../interfaces/CloberOrderKey.sol";
library OrderKeyUtils {
function encode(OrderKey memory orderKey) internal pure returns (uint256) {
return encode(orderKey.isBid, orderKey.priceIndex, orderKey.orderIndex);
}
function encode(
bool isBid,
uint16 priceIndex,
uint256 orderIndex
) internal pure returns (uint256 id) {
if (orderIndex > type(uint232).max) {
revert Errors.CloberError(Errors.INVALID_ID);
}
assembly {
id := add(orderIndex, add(shl(232, priceIndex), shl(248, isBid)))
}
}
function decode(uint256 id) internal pure returns (OrderKey memory) {
uint8 isBid;
uint16 priceIndex;
uint232 orderIndex;
assembly {
orderIndex := id
priceIndex := shr(232, id)
isBid := shr(248, id)
}
if (isBid > 1) {
revert Errors.CloberError(Errors.INVALID_ID);
}
return OrderKey({isBid: isBid == 1, priceIndex: priceIndex, orderIndex: orderIndex});
}
}
// SPDX-License-Identifier: -
// License: https://license.clober.io/LICENSE.pdf
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "./interfaces/CloberMarketFactory.sol";
import "./interfaces/CloberOrderBook.sol";
import "./interfaces/CloberOrderNFT.sol";
import "./Errors.sol";
import "./utils/OrderKeyUtils.sol";
contract OrderNFT is ERC165, CloberOrderNFT {
using Address for address;
using Strings for uint256;
using OrderKeyUtils for OrderKey;
CloberMarketFactory private immutable _factory;
address private immutable _canceler;
string public override name;
string public override symbol;
string public override baseURI;
address public override market;
mapping(address => uint256) private _balances;
mapping(uint256 => address) private _tokenApprovals;
mapping(address => mapping(address => bool)) private _operatorApprovals;
constructor(address factory, address canceler) {
_factory = CloberMarketFactory(factory);
_canceler = canceler;
}
function init(
string memory name_,
string memory symbol_,
address market_
) external {
if (market != address(0)) {
revert Errors.CloberError(Errors.ACCESS);
}
if (market_ == address(0)) {
revert Errors.CloberError(Errors.EMPTY_INPUT);
}
name = name_;
symbol = symbol_;
market = market_;
}
modifier onlyMarket() {
if (msg.sender != market) {
revert Errors.CloberError(Errors.ACCESS);
}
_;
}
modifier onlyExists(uint256 tokenId) {
if (_getOrderOwner(tokenId) == address(0)) {
revert Errors.CloberError(Errors.INVALID_ID);
}
_;
}
function changeBaseURI(string memory newBaseURI) external {
if (_getHost() != msg.sender) {
revert Errors.CloberError(Errors.ACCESS);
}
baseURI = newBaseURI;
}
function supportsInterface(bytes4 interfaceId) public view override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
function balanceOf(address user) public view returns (uint256) {
if (user == address(0)) {
revert Errors.CloberError(Errors.EMPTY_INPUT);
}
uint256 balance = _balances[user];
return balance > 0 ? balance - 1 : balance;
}
function ownerOf(uint256 tokenId) public view returns (address) {
address tokenOwner = _getOrderOwner(tokenId);
if (tokenOwner == address(0)) {
revert Errors.CloberError(Errors.ACCESS);
}
return tokenOwner;
}
function tokenURI(uint256 tokenId) public view onlyExists(tokenId) returns (string memory) {
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
function approve(address to, uint256 tokenId) public {
address tokenOwner = ownerOf(tokenId);
if (to == tokenOwner) {
revert Errors.CloberError(Errors.ACCESS);
}
if (msg.sender != tokenOwner && !isApprovedForAll(tokenOwner, msg.sender)) {
revert Errors.CloberError(Errors.ACCESS);
}
_approve(tokenOwner, to, tokenId);
}
function getApproved(uint256 tokenId) public view onlyExists(tokenId) returns (address) {
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address operator, bool approved) public {
if (msg.sender == operator) {
revert Errors.CloberError(Errors.ACCESS);
}
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function isApprovedForAll(address tokenOwner, address operator) public view returns (bool) {
return _operatorApprovals[tokenOwner][operator];
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public {
if (!_isApprovedOrOwner(msg.sender, tokenId)) {
revert Errors.CloberError(Errors.ACCESS);
}
_transfer(from, to, tokenId);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public {
if (!_isApprovedOrOwner(msg.sender, tokenId)) {
revert Errors.CloberError(Errors.ACCESS);
}
_safeTransfer(from, to, tokenId, data);
}
function _safeTransfer(
address from,
address to,
uint256 tokenId,
bytes memory data
) internal {
_transfer(from, to, tokenId);
if (!_checkOnERC721Received(from, to, tokenId, data)) {
revert Errors.CloberError(Errors.NOT_IMPLEMENTED_INTERFACE);
}
}
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
address tokenOwner = ownerOf(tokenId);
return (spender == tokenOwner || isApprovedForAll(tokenOwner, spender) || getApproved(tokenId) == spender);
}
function _increaseBalance(address to) internal {
_balances[to] += _balances[to] > 0 ? 1 : 2;
}
function _decreaseBalance(address to) internal {
_balances[to] -= 1;
}
function onMint(address to, uint256 tokenId) external onlyMarket {
if (to == address(0)) {
revert Errors.CloberError(Errors.EMPTY_INPUT);
}
_increaseBalance(to);
emit Transfer(address(0), to, tokenId);
}
function onBurn(uint256 tokenId) external onlyMarket {
address tokenOwner = ownerOf(tokenId);
// Clear approvals
_approve(tokenOwner, address(0), tokenId);
_decreaseBalance(tokenOwner);
emit Transfer(tokenOwner, address(0), tokenId);
}
function cancel(
address from,
uint256[] calldata tokenIds,
address receiver
) external {
if (msg.sender != _canceler) {
revert Errors.CloberError(Errors.ACCESS);
}
OrderKey[] memory orderKeys = new OrderKey[](tokenIds.length);
for (uint256 i = 0; i < tokenIds.length; ++i) {
if (!_isApprovedOrOwner(from, tokenIds[i])) {
revert Errors.CloberError(Errors.ACCESS);
}
orderKeys[i] = decodeId(tokenIds[i]);
}
CloberOrderBook(market).cancel(receiver, orderKeys);
}
function _transfer(
address from,
address to,
uint256 tokenId
) internal {
if (ownerOf(tokenId) != from) {
revert Errors.CloberError(Errors.ACCESS);
}
if (to == address(0)) {
revert Errors.CloberError(Errors.EMPTY_INPUT);
}
// Clear approvals from the previous owner
_approve(from, address(0), tokenId);
_decreaseBalance(from);
_increaseBalance(to);
CloberOrderBook(market).changeOrderOwner(decodeId(tokenId), to);
emit Transfer(from, to, tokenId);
}
function _approve(
address tokenOwner,
address to,
uint256 tokenId
) internal {
_tokenApprovals[tokenId] = to;
emit Approval(tokenOwner, to, tokenId);
}
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory data
) private returns (bool) {
if (to.isContract()) {
try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (bytes4 retval) {
return retval == IERC721Receiver.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert Errors.CloberError(Errors.NOT_IMPLEMENTED_INTERFACE);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
function decodeId(uint256 id) public pure returns (OrderKey memory) {
return OrderKeyUtils.decode(id);
}
function encodeId(OrderKey memory orderKey) external pure returns (uint256 id) {
return orderKey.encode();
}
function owner() external view returns (address) {
return _getHost();
}
function _getHost() internal view returns (address) {
return _factory.getMarketHost(market);
}
function _getOrderOwner(uint256 tokenId) internal view returns (address) {
return CloberOrderBook(market).getOrder(decodeId(tokenId)).owner;
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import "../Errors.sol";
/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Clober (https://github.com/clober-dex/core/blob/main/contracts/utils/ReentrancyGuard.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
uint256 internal _locked = 1;
modifier nonReentrant() virtual {
if (_locked != 1) {
revert Errors.CloberError(Errors.REENTRANCY);
}
_locked = 2;
_;
_locked = 1;
}
}
// SPDX-License-Identifier: -
// License: https://license.clober.io/LICENSE.pdf
pragma solidity ^0.8.0;
import "../Errors.sol";
contract RevertOnDelegateCall {
address private immutable _thisAddress;
modifier revertOnDelegateCall() {
_revertOnDelegateCall();
_;
}
function _revertOnDelegateCall() internal view {
// revert when calling this contract via DELEGATECALL
if (address(this) != _thisAddress) {
revert Errors.CloberError(Errors.DELEGATE_CALL);
}
}
constructor() {
_thisAddress = address(this);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
{
"compilationTarget": {
"contracts/MarketFactory.sol": "MarketFactory"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"volatileMarketDeployer_","type":"address"},{"internalType":"address","name":"stableMarketDeployer_","type":"address"},{"internalType":"address","name":"initialDaoTreasury","type":"address"},{"internalType":"address","name":"canceler_","type":"address"},{"internalType":"address[]","name":"initialQuoteTokenRegistrations_","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"CloberError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousTreasury","type":"address"},{"indexed":false,"internalType":"address","name":"newTreasury","type":"address"}],"name":"ChangeDaoTreasury","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"market","type":"address"},{"indexed":false,"internalType":"address","name":"previousHost","type":"address"},{"indexed":false,"internalType":"address","name":"newHost","type":"address"}],"name":"ChangeHost","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"ChangeOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"market","type":"address"},{"indexed":false,"internalType":"address","name":"orderToken","type":"address"},{"indexed":false,"internalType":"address","name":"quoteToken","type":"address"},{"indexed":false,"internalType":"address","name":"baseToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"quoteUnit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"int24","name":"makerFee","type":"int24"},{"indexed":false,"internalType":"uint24","name":"takerFee","type":"uint24"},{"indexed":false,"internalType":"uint128","name":"a","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"d","type":"uint128"}],"name":"CreateStableMarket","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"market","type":"address"},{"indexed":false,"internalType":"address","name":"orderToken","type":"address"},{"indexed":false,"internalType":"address","name":"quoteToken","type":"address"},{"indexed":false,"internalType":"address","name":"baseToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"quoteUnit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"int24","name":"makerFee","type":"int24"},{"indexed":false,"internalType":"uint24","name":"takerFee","type":"uint24"},{"indexed":false,"internalType":"uint128","name":"a","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"r","type":"uint128"}],"name":"CreateVolatileMarket","type":"event"},{"inputs":[],"name":"canceler","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"treasury","type":"address"}],"name":"changeDaoTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"marketNonce","type":"uint256"}],"name":"computeTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"marketHost","type":"address"},{"internalType":"address","name":"quoteToken","type":"address"},{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint96","name":"quoteUnit","type":"uint96"},{"internalType":"int24","name":"makerFee","type":"int24"},{"internalType":"uint24","name":"takerFee","type":"uint24"},{"internalType":"uint128","name":"a","type":"uint128"},{"internalType":"uint128","name":"d","type":"uint128"}],"name":"createStableMarket","outputs":[{"internalType":"address","name":"market","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"marketHost","type":"address"},{"internalType":"address","name":"quoteToken","type":"address"},{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint96","name":"quoteUnit","type":"uint96"},{"internalType":"int24","name":"makerFee","type":"int24"},{"internalType":"uint24","name":"takerFee","type":"uint24"},{"internalType":"uint128","name":"a","type":"uint128"},{"internalType":"uint128","name":"r","type":"uint128"}],"name":"createVolatileMarket","outputs":[{"internalType":"address","name":"market","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"daoTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"executeChangeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"executeHandOverHost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"quoteToken","type":"address"},{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint256","name":"marketNonce","type":"uint256"}],"name":"formatOrderTokenName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"quoteToken","type":"address"},{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint256","name":"marketNonce","type":"uint256"}],"name":"formatOrderTokenSymbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"futureOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"getMarketHost","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"getMarketInfo","outputs":[{"components":[{"internalType":"address","name":"host","type":"address"},{"internalType":"enum CloberMarketFactory.MarketType","name":"marketType","type":"uint8"},{"internalType":"uint128","name":"a","type":"uint128"},{"internalType":"uint128","name":"factor","type":"uint128"},{"internalType":"address","name":"futureHost","type":"address"}],"internalType":"struct CloberMarketFactory.MarketInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"prepareChangeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"address","name":"newHost","type":"address"}],"name":"prepareHandOverHost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"registerQuoteToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"registeredQuoteTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stableMarketDeployer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"unregisterQuoteToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"volatileMarketDeployer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]