// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)pragmasolidity ^0.8.1;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @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.
* ====
*/functionisContract(address account) internalviewreturns (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].
*/functionsendValue(addresspayable 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._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, "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._
*/functionfunctionCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
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._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value
) internalreturns (bytesmemory) {
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._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
(bool success, bytesmemory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
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._
*/functionfunctionStaticCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
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._
*/functionfunctionDelegateCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/functionverifyCallResultFromTarget(address target,
bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
if (success) {
if (returndata.length==0) {
// only check isContract if the call was successful and the return data is empty// otherwise we already know that it was a contractrequire(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/functionverifyCallResult(bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalpurereturns (bytesmemory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function_revert(bytesmemory returndata, stringmemory errorMessage) privatepure{
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly/// @solidity memory-safe-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Contract Source Code
File 2 of 44: AuxLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"./StructsLib.sol";
import"../common/Structs.sol";
import"../common/MathLib.sol";
import"../market/MarketAuxLib.sol";
import"../common/StrippedInterfaces.sol";
import"../assetregistry/interfaces/IAssetRegistry.sol";
libraryAuxLib{
usingMarketAuxLibforStructs.Asset;
/// @dev Checks if an address of a token can share in fees/// @param state2 State2 object/// @param addr The address to check/// @return bool True if the token can share in feesfunctionisShareable(StructsLib.State2 storage state2, address addr) externalviewreturns (bool) {
return state2.assetReg.get(addr).sharable;
}
/// Checks if an address is a verified token/// @param state2 State2 object/// @param addr The address to check/// @return bool True if the token is verifiedfunctionisVerified(StructsLib.State2 storage state2, address addr) externalviewreturns (bool) {
return state2.assetReg.get(addr).verified;
}
/// @dev Checks if an address is marked as a stable token/// @param state2 State2 object/// @param addr The address to check/// @return bool True if the token is stable assetfunctionisStable(StructsLib.State2 storage state2, address addr) externalviewreturns (bool) {
return state2.assetReg.get(addr).stable;
}
/// @dev Checks if a market is composed of two stable tokens/// @param assetReg Asset registry contract/// @param m Market object/// @return bool True if both tokens are stablefunctionisStable(IAssetRegistry assetReg, Structs.Market memory m) externalviewreturns (bool) {
return assetReg.get(m.base.addr).stable && assetReg.get(m.quote.addr).stable;
}
/// @dev Checks if at order at index id exist/// @param id The order ID or index./// @return bool True if the order existfunctionhasOrder(StructsLib.State storage state, uint256 id) publicviewreturns (bool) {
return id < state.orders.length&& state.orders[id].marketId !=address(0);
}
/// @dev Checks whether addr can create a dispute./// Reverts if order is unknown./// Returns false if market has a market manager/// Returns false if order has been released./// Returns false if addr is not a provider or a taker./// Returns false if order has not be marked as `paid` and time to pay has not been reached./// Returns false if order dispute creation time is in the future/// @param state State object/// @param id The order ID./// @param addr The address to check if permitted to create dispute./// @return bool True if addr can create a disputefunctioncanCreateDispute(
StructsLib.State storage state,
StructsLib.State2 storage state2,
uint256 id,
address addr
) publicviewreturns (bool) {
require(hasOrder(state, id), "OC: UNKNOWN_ORDER");
Structs.Order memory o = state.orders[id];
if (state2.marketCore.getMarket(o.marketId).manager !=address(0)) returnfalse;
if (o.provider != addr && o.taker != addr) returnfalse;
if (o.released || o.cancelled ||!o.paid) returnfalse;
if (o.disputeFrom >block.timestamp) returnfalse;
returntrue;
}
/// @dev Checks whether a market is permissioned./// A market is permissioned if its PUT or SWAP permission is set to true./// @param market Market objectfunctionisPerm(Structs.Market memory market) publicpurereturns (bool) {
return market.perms.put || market.perms.swap;
}
/// @dev Checks whether a liquidity is empty/// @param liq Liquidity objectfunctionisLiquidityEmpty(Structs.Liquidity memory liq) publicpurereturns (bool) {
return liq.amount ==0&& liq.ids.length==0;
}
/// @dev Check whether a liquidity exists/// @param state State object/// @param mid The market ID/// @param provider The address of the liquidity provider/// @param lid The liquidity ID/// @return bool True if the liquidity existsfunctionisLiquidityExist(StructsLib.State storage state, address mid, address provider, uint256 lid) publicviewreturns (bool) {
return state.liquidity[mid][provider].length> lid;
}
/// @dev Checks whether a liquidity exists and has non-zero (not empty) liquidity/// @param state State object/// @param mid The market ID/// @param provider The address of the liquidity provider/// @param lid The liquidity ID/// @return bool True if the liquidity exists and has non-zero liquidityfunctionhasNonZeroLiquidity(StructsLib.State storage state, address mid, address provider, uint256 lid) publicviewreturns (bool) {
if (!isLiquidityExist(state, mid, provider, lid)) returnfalse;
Structs.Liquidity memory liq = state.liquidity[mid][provider][lid];
return!isLiquidityEmpty(liq);
}
/// @dev Checks if an uint256 array has unique values/// @param arr The array to check/// @return bool True if the array has unique valuesfunctionisUnique(uint256[] memory arr) publicpurereturns (bool) {
if (arr.length==0) returntrue;
for (uint256 i =0; i < arr.length-1; i++) {
for (uint256 j = i +1; j < arr.length; j++) {
if (arr[i] == arr[j]) returnfalse;
}
}
returntrue;
}
/// @dev Checks whether arr length is not more than 16/// @param arr The array to check/// @return bool True if the array length is not more than 16functionisMaxLen16(uint256[] memory arr) publicpurereturns (bool) {
return arr.length<=16;
}
/// @dev Checks whether addr is zero/// @param addr The address to check/// @return bool True if the address is zerofunctionisZero(address addr) publicpurereturns (bool) {
return addr ==address(0);
}
/// @dev Return _default if _target is zero, otherwise _target is returned/// @param _target The preferred value if not zero/// @param _default The fallback value if _target is zerofunctionsetOrDef(address _target, address _default) publicpurereturns (address) {
return (_target !=address(0)) ? _target : _default;
}
/// @dev Get fee to be charged from the given asset/// @param assetReg Asset registry contract/// @param asset Asset object/// @param defaultBp Default fee basis point if asset registry does not have fee info/// for the given asset/// @return feeBp Fee basis point/// @return fixedFee Fee basis point/// @return curRate Fee basis pointfunctiongetFeeInfo(
IAssetRegistry assetReg,
Structs.Asset memory asset,
uint256 defaultBp
) publicviewreturns (uint256 feeBp, uint256 fixedFee, uint256 curRate) {
Info memory inf = assetReg.get(asset.addr);
if (inf.feeBp ==0) inf.feeBp = defaultBp;
return (inf.feeBp, inf.feeFixed, inf.curRate);
}
/// @dev Check if addr owns a native NFT/// @param nft The address of the NFT/// @param addr The address to check/// @return bool True if addr owns a native NFTfunctionhasNativeNFT(address nft, address addr) publicviewreturns (bool) {
return nft !=address(0) && IERC721Strip(nft).balanceOf(addr) >0;
}
/// Check parameters for a SWAP operation involving a fungible base asset/// @param sp SwapSlot object/// @param mkt Market object/// @param liq Liquidity objectfunctioncheckFungibleSwapParams(StructsLib.SwapSlot memory sp, Structs.Market memory mkt, Structs.Liquidity storage liq) publicview{
require(mkt.quote.isNFT() || sp.amount >0, "OC: AMOUNT_REQ");
require(!mkt.quote.isNFT() || sp.amount ==0, "OC: AMOUNT_NREQ");
require((sp.offerId >0&& mkt.perms.allowZeroOfferPrice) || mkt.quote.isNFT() || sp.maxPrice >0, "OC: MAX_PRICE_REQ");
require(!mkt.quote.isNFT() || sp.maxPrice ==0, "OC: MAX_PRICE_NREQ");
require(!mkt.quote.isNFT() || sp.ids.length>0, "OC: IDS_REQ");
require(mkt.quote.isNFT() || sp.ids.length==0, "OC: IDS_NREQ");
require(isMaxLen16(sp.ids), "OC: IDS_HSIZE");
require(isUnique(sp.ids), "OC: IDS_NUNIQ");
require(!liq.paused, "OC: LIQ_PAUSED");
require(sp.amount <= liq.amount, "OC: LOW_LIQ");
require(sp.offerId >0|| sp.amount >= liq.minSwap, "OC: AMOUNT_BELOW_MIN_SWAP");
require(sp.offerId >0|| liq.maxSwap ==0|| sp.amount <= liq.maxSwap, "OC: AMOUNT_ABOVE_MAX_SWAP");
require(sp.offerId >0|| sp.maxPrice ==0|| sp.maxPrice >= liq.price, "OC: MAX_PRICE_TOO_LOW");
}
/// @dev Check parameters for a SWAP operation involving a non-fungible base asset/// @param sp SwapSlot object/// @param mkt Market object/// @param liq Liquidity objectfunctioncheckNonFungibleSwapParams(
StructsLib.SwapSlot memory sp,
Structs.Market memory mkt,
Structs.Liquidity storage liq
) publicview{
require(sp.amount ==0, "OC: AMOUNT_NREQ");
require(!liq.paused, "OC: LIQ_PAUSED");
require((sp.offerId >0&& mkt.perms.allowZeroOfferPrice) || mkt.quote.isNFT() || sp.maxPrice >0, "OC: MAX_PRICE_REQ");
require(!mkt.quote.isNFT() || sp.maxPrice ==0, "OC: MAX_PRICE_NREQ");
require(sp.offerId >0|| sp.maxPrice ==0|| sp.maxPrice >= liq.price, "OC: MAX_PRICE_TOO_LOW");
require(mkt.quote.isNFT() || sp.ids.length==0, "OC: IDS_NREQ");
require((sp.offerId >0&& mkt.perms.allowZeroOfferPrice) ||!mkt.quote.isNFT() || sp.ids.length>0, "OC: IDS_REQ");
}
}
Contract Source Code
File 3 of 44: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 4 of 44: Core.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"../common/Ownable.sol";
import"../common/Structs.sol";
import"./StructsLib.sol";
import"../market/interfaces/IMarketCore.sol";
import"@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import"../pools/interfaces/IFeePool.sol";
import"./WithdrawLib.sol";
import"./AuxLib.sol";
import"./FeeLib.sol";
import"./LiquidityLib.sol";
import"./OfferLib.sol";
import"./SwapLib.sol";
import"../assetregistry/interfaces/IAssetRegistry.sol";
import"../token/interfaces/IERC20.sol";
import"./FungibleLiquidityLib.sol";
import"./NonFungibleLiquidityLib.sol";
/// Core manages market orderscontractCoreisOwnable, ERC1155Holder{
StructsLib.State public state;
StructsLib.State2 public state2;
eventSwap(addressindexed mid,
addressindexed provider,
addressindexed swapper,
address recipient,
address releaser,
uint256[6] numTypeInfo,
bool byDispute,
uint256[] idsFromProvider,
uint256[] idsFromTaker
);
eventSwapOrder(addressindexed mid,
addressindexed provider,
addressindexed swapper,
address recipient,
uint256[7] numTypeInfo,
uint256[] idsFromProvider,
uint256[] idsFromTaker
);
eventSwapOrderPaid(uint256indexed orderID, address payer, uint256 disputeDelayDur);
eventOrderCancelled(uint256indexed orderID, addressindexed canceller, boolindexed byDispute);
eventTransferredToFeePool(addressindexed token, uint256 amount, address convAsset, uint256 convAmount);
eventFinalizedByManagerSig(address manager, address finalizer, uint256 orderId, uint action);
eventProtocolFeeUpdated(uint256 instantSwapFeeBp,
uint256 synthSwapFeeBp,
uint256 stableSwapFeeBp,
uint256 fixedSynthSwapFee,
uint256 fixedNFTSwapFee,
uint256 instantNFTSwapFeeBp
);
eventProtocolFeeSplitUpdated(uint256 inventor, uint256 shared, uint256 operator);
eventTransferInventorBalance(address owner, address token, uint256 amount);
eventTransferOperatorBalance(address owner, address token, uint256 amount);
eventUpdatedFungibleLiquidity(addressindexed provider,
addressindexed marketId,
address target,
bool deduct,
bool paused,
uint256 liq,
uint256 amount,
uint256 price,
uint256[] wantOrIds,
uint256[] wantAndIds,
uint256 minSwap,
uint256 maxSwap,
uint256 timeToPay
);
eventUpdatedNonFungibleLiquidity(addressindexed provider,
addressindexed marketId,
address target,
bool deduct,
bool paused,
uint256 lid,
uint256[] ids,
uint256 price,
uint256[] wantOrIds,
uint256[] wantAndIds,
uint256 timeToPay
);
eventUpdatedPrice(addressindexed marketId, addressindexed provider, addressindexed manager, uint256 lid, uint256 price);
eventTogglePause(addressindexed marketId, addressindexed provider, uint256 lid, bool paused);
eventUpdatedLiquidityManager(addressindexed marketId, addressindexed provider, addressindexed manager, address oldManager);
eventNFTDiscountUpdated(uint256 bp);
eventNewOffer(address marketId,
addressindexed provider,
uint256 lid,
addressindexed offerer,
uint256 offerId,
uint256 amount,
uint256 price,
uint256[] ids,
uint256 expireDuration
);
eventOfferUpdated(uint256indexed offerId,
addressindexed offerer,
uint256 amount,
uint256 price,
uint256[] ids,
uint256 execDelayDur
);
eventOfferAccepted(uint256indexed offerId, addressindexed acceptor, uint256 executeAt);
eventOfferCancelled(uint256indexed offerId, addressindexed canceller);
eventOfferExecuted(uint256indexed offerId, addressindexed executor);
eventOfferUnaccepted(uint256indexed offerId, addressindexed unacceptor);
eventUpdatedLiquiditySwapBadges(address market, address provider, uint256 liq, string[2][] badges);
eventUpdatedFeePurse(address addr);
eventUpdatedBadge(address addr);
eventUpdatedMarketCore(address addr);
eventUpdatedFeePool(address addr);
eventUpdatedInventor(address addr);
eventUpdatedFeePoolConfig(uint256 interval);
eventUpdatedAssetRegistry(address addr);
eventUpdatedMinTimeToPay(uint256 val);
eventUpdatedOrderGraceDur(uint256 val);
eventUpdatedMaxOpenOrders(uint256 val);
eventUpdatedMaxOpenDisputes(uint256 val);
eventUpdatedDisputeManager(address addr);
eventUpdatedNFT(address addr);
eventUpdatedOperator(address addr);
eventUpdatedMinerRegistry(address addr);
eventUpdatedAMMInfo(address swapRouter, address ethErc20Addr, uint256 poolFee);
eventUpdatedAMMPoolInfo(address input, address output, uint256 poolFee);
/// @dev Modifier to ensure a market exist/// @param id The market IDmodifiermarketMustExist(address id) {
require(IMarketCore(state2.marketCore).getMarket(id).createdAt >0, "OC: UNKNOWN_MARKET");
_;
}
/// @dev Modifier to sender is deployermodifieronlyDisputeManager() {
require(msg.sender== state2.disputeManager, "OC: NOT_DISPUTE_MGR");
_;
}
/// @dev Modifier to ensure an expiry time has not elapsed./// If expireAt = 0, it means not expired./// @param expireAt The expiry timemodifiernotExpired(uint256 expireAt) {
require(expireAt ==0|| expireAt >block.timestamp, "OC: EXPIRED_TX");
_;
}
/**
* @dev Throws if called by any account other than the inventor.
*/modifieronlyInventor() {
require(state.inventor ==msg.sender, "OC: NOT_INVENTOR");
_;
}
/// @notice Constructor/// @param _marketCore The address of the market core contract/// @param _token The address of the native token contract/// @param _minTimeToPay The minimum time off-chain payers have to make payment/// @param _orderGraceDur The grace period after which an order can be cancelled off-chain payment is not made/// @param _maxOpenOrders The maximum number of open orders allowed per liquidity provider/// @param _maxOpenDisputes The maximum number of open disputes allowed per liquidity providerconstructor(address _marketCore,
address _token,
uint256 _minTimeToPay,
uint256 _orderGraceDur,
uint256 _maxOpenOrders,
uint256 _maxOpenDisputes
) {
state.minTimeToPay = _minTimeToPay;
state.orderGraceDur = _orderGraceDur;
state.ints.maxOpenOrders = _maxOpenOrders;
state.ints.maxOpenDisputes = _maxOpenDisputes;
state.feePoolTxInterval =12hours;
state.inventor =msg.sender;
state.feeBPs[0] =5000;
state.feeBPs[1] =5000;
state.feeBPs[2] =0;
state2.token = IERC20(_token);
state2.marketCore = IMarketCore(_marketCore);
state2.ints.nftDiscountBp =2500;
}
/// @notice Set asset registry/// @param addr The address of the asset registry contractfunctionsetAssetRegistry(address addr) externalonlyOwneronlyOwner{
state2.assetReg = IAssetRegistry(addr);
emit UpdatedAssetRegistry(addr);
}
/// @notice Set default time to pay/// @param val The new valuefunctionsetMinTimeToPay(uint256 val) externalonlyOwner{
state.minTimeToPay = val;
emit UpdatedMinTimeToPay(val);
}
/// @notice Set order grace period duration/// @param val The new valuefunctionsetOrderGraceDur(uint256 val) externalonlyOwner{
state.orderGraceDur = val;
emit UpdatedOrderGraceDur(val);
}
/// @notice Set max open orders/// @param val The new valuefunctionsetMaxOpenOrders(uint256 val) externalonlyOwner{
state.ints.maxOpenOrders = val;
emit UpdatedMaxOpenOrders(val);
}
/// @notice Set max open disputes/// @param val The new valuefunctionsetMaxOpenDisputes(uint256 val) externalonlyOwner{
state.ints.maxOpenDisputes = val;
emit UpdatedMaxOpenDisputes(val);
}
/// @notice Updated the dispute manager contract/// @param addr The new addressfunctionsetDisputeManager(address addr) externalonlyOwner{
state2.disputeManager = addr;
emit UpdatedDisputeManager(addr);
}
/// @notice Updated the Operator contract/// @param addr The new addressfunctionsetOperator(address addr) externalonlyOwner{
state2.operatorMgr = IOperator(addr);
emit UpdatedOperator(addr);
}
/// @notice Updated the MinerRegistry contract/// @param addr The new addressfunctionsetMinerRegistry(address addr) externalonlyOwner{
state2.minerReg = IMinerRegistry(addr);
emit UpdatedMinerRegistry(addr);
}
/// @notice Updated the NFT contract/// @param addr The new addressfunctionsetNFT(address addr) externalonlyOwner{
state2.nft = addr;
emit UpdatedNFT(addr);
}
/// @notice Set native AMM information/// @param swapRouter The address of the swap router/// @param ethErc20Addr The address of the ETH ERC20 token/// @param poolFee The fee tier of ETH pool on the native AMM DEXfunctionsetAmmInfo(address swapRouter, address ethErc20Addr, uint256 poolFee) externalonlyOwner{
state2.ammEth = ethErc20Addr;
state2.ints.ammEthPoolFee = poolFee;
state.ammSwapRouter = swapRouter;
emit UpdatedAMMInfo(swapRouter, ethErc20Addr, poolFee);
}
/// @notice Set AMM pool info a given input address/// @param input The input token address/// @param out The output token address/// @param poolFee The fee tier of the poolfunctionsetAmmPoolInfo(address input, address out, uint256 poolFee) externalonlyOwner{
state2.ammSwapPool[input] = StructsLib.AmmSwapPoolInfo(out, poolFee);
emit UpdatedAMMPoolInfo(input, out, poolFee);
}
/// @notice Updated the FeePurse contract/// @param val The new addressfunctionsetFeePurse(address val) externalonlyOwner{
state2.feePurse = IFeePurse(val);
emit UpdatedFeePurse(val);
}
/// @notice Updated the Badge contract/// @param val The new addressfunctionsetBadgeMgr(address val) externalonlyOwner{
state2.badge = IBadge(val);
emit UpdatedBadge(val);
}
/// @notice Set market core address/// @param addr The new addressfunctionsetMarketCore(address addr) externalonlyOwner{
state2.marketCore = IMarketCore(addr);
emit UpdatedMarketCore(addr);
}
/// @notice Set fee rewarder contract address/// @param addr The new addressfunctionsetFeePool(address addr) externalonlyOwner{
state2.feePool = IFeePool(addr);
emit UpdatedFeePool(addr);
}
/// @notice Updated the protocol inventor/// @param addr The new addressfunctionsetInventor(address addr) externalonlyInventor{
state.inventor = addr;
emit UpdatedInventor(addr);
}
/// @notice Set the reward per fee sharer transfer/// @param interval The call intervalfunctionconfigureFeePool(uint256 interval) externalonlyOwner{
state.feePoolTxInterval = interval;
emit UpdatedFeePoolConfig(interval);
}
/// @notice Set NFT holder discount/// @param bp - The new base pointfunctionsetDiscountForNFTHolder(uint256 bp) externalonlyOwner{
state2.ints.nftDiscountBp = bp;
emit NFTDiscountUpdated(bp);
}
/// @notice Set protocol fee split/// @param inventorBP The base point for inventor/// @param sharedBP The base point for shared with holders/// @param operatorBP The base point for operatorsfunctionsetProtoFeeSplit(uint256 inventorBP, uint256 sharedBP, uint256 operatorBP) publiconlyOwner{
require(inventorBP >=3000, "OC: INV_BP_TOO_LOW");
require(inventorBP + sharedBP + operatorBP ==10000, "OC: UNBALANCED");
state.feeBPs[0] = inventorBP;
state.feeBPs[1] = sharedBP;
state.feeBPs[2] = operatorBP;
emit ProtocolFeeSplitUpdated(inventorBP, sharedBP, operatorBP);
}
/// @notice Get protocol fee basis pointsfunctiongetFeeBPs() publicviewreturns (uint256[3] memory) {
return state.feeBPs;
}
/// Checks if an address is a verified token/// @param addr The address to checkfunctionisVerified(address addr) publicviewreturns (bool) {
return AuxLib.isVerified(state2, addr);
}
/// @notice Updated the protocol fee/// @param _instantSwapFeeBp Fee for ERC20/ERC20 swap/// @param _synthSwapFeeBp Fee for ERC20/SYNTH swap/// @param _stableSwapFeeBp Fee for ERC20(stablecoin)/ERC20(stablecoin)/// @param _fixedSynthSwapFee Fee for SYNTH/SYNTH swap (in NATIVE_TOKEN)/// @param _fixedNFTSwapFee Fee for NFT/NFT swap (in NATIVE_TOKEN)/// @param _instantNFTSwapFeeBp Fee for NFT/ERC20 swapfunctionsetProtoFee(uint256 _instantSwapFeeBp,
uint256 _synthSwapFeeBp,
uint256 _stableSwapFeeBp,
uint256 _fixedSynthSwapFee,
uint256 _fixedNFTSwapFee,
uint256 _instantNFTSwapFeeBp
) public{
state.ints.instantSwapFeeBp = _instantSwapFeeBp;
state.ints.synthSwapFeeBp = _synthSwapFeeBp;
state.ints.stableSwapFeeBp = _stableSwapFeeBp;
state.ints.instantNFTSwapFeeBp = _instantNFTSwapFeeBp;
state.ints.fixedSynthSwapFee = _fixedSynthSwapFee;
state.ints.fixedNFTSwapFee = _fixedNFTSwapFee;
emit ProtocolFeeUpdated(
_instantSwapFeeBp,
_synthSwapFeeBp,
_stableSwapFeeBp,
_fixedSynthSwapFee,
_fixedNFTSwapFee,
_instantNFTSwapFeeBp
);
}
/// @notice Returns the balance of fees earned by an address./// @param _owner The owner address/// @param _asset The asset whose balance is returnedfunctionfeeBalanceOf(address _owner, address _asset) externalviewreturns (uint256) {
return state.feeBalance[_owner][_asset];
}
/// @notice Returns the balance of fees earned by the protocol/// @param asset The target assetfunctioninventorBalanceOf(address asset) externalviewreturns (uint256) {
return state.inventorBalance[asset];
}
/// @notice Returns the sharable fee balance of an asset/// @param asset The target assetfunctionshareablesBalanceOf(address asset) externalviewreturns (uint256) {
return state.shareablesBalance[asset];
}
/// @notice Returns the operator balance fee balance of an asset/// @param asset The target assetfunctionoperatorBalanceOf(address operator, address asset) externalviewreturns (uint256) {
return state.operatorsBalance[operator][asset];
}
/// @notice Withdraw fees earned by sender/// @param asset The address of the asset the fee is denominated in.functionwithdrawFeeBalance(address asset) external{
uint256 bal = state.feeBalance[msg.sender][asset];
state.feeBalance[msg.sender][asset] =0;
IERC20(asset).transfer(msg.sender, bal);
}
/// @notice Swap quote asset for base asset./// @param marketId The unique ID of the market./// @param provider The address of the base asset liquidity provider./// @param provider The address of the recipient (optional)/// @param lid The liquidity ID/// @param amount The quantity of base asset to be swapped./// @param maxPrice The maximum price to pay for amount./// maxPrice protects the swapper from paying an unexpected cost/// for amount in case the price was updated before the order was placed./// @param ids The ids of non-fungible quote assets (optional if quote is fungible)/// @param txDeadline The transaction is reverted if current time >= expireAt/// @param operator The ID of the interface operatorfunctionswapQuoteForBase(address marketId,
address provider,
address recipient,
uint256 lid,
uint256 amount,
uint256 maxPrice,
uint256[] memory ids,
uint256 txDeadline,
uint256 operator
) externalpayablenotExpired(txDeadline) marketMustExist(marketId) {
StructsLib.SwapSlotAddresses memory addrs = StructsLib.SwapSlotAddresses(marketId, provider, recipient);
SwapLib.swapQuoteForBase(state, state2, StructsLib.SwapSlot(lid, amount, maxPrice, ids, 0, operator, addrs));
}
/// @notice Compute swap fee/// @param mid The market ID/// @param provider The address of the base asset liquidity provider./// @param lid The liquidity ID/// @param taker The address of the recipient/// @param orderAmt The quantity of base asset to be swapped./// @param orderPrice The maximum price to pay for amount.functioncomputeSwapFee(address mid,
address provider,
uint256 lid,
address taker,
uint256 orderAmt,
uint256 orderPrice
) publicviewreturns (uint256, uint256, uint256, uint256) {
Structs.Market memory mkt = state2.marketCore.getMarket(mid);
Structs.Liquidity memory liq = state.liquidity[mid][provider][lid];
FeeInfo memory info = FeeLib.computeFee(
ComputeFeeParams(
mkt,
liq,
state2.assetReg,
state2.feePurse,
state.ints.synthSwapFeeBp,
state.ints.instantNFTSwapFeeBp,
state.ints.instantSwapFeeBp,
state.ints.stableSwapFeeBp,
state2.ints.nftDiscountBp,
lid,
orderAmt,
orderPrice,
state2.nft,
provider,
taker
)
);
return (info.protoFee, info.mktCreatorFee, info.feeBp, info.mktFeeBp);
}
/// @notice Withdraw fees accrued for the protocol inventor/// @param asset The address of the asset the fee is denominated in.functionwithdrawInventorBalance(address asset) externalonlyInventor{
WithdrawLib.withdrawInventorBalance(state, state2, asset);
}
/// @notice Release an order/// @param id The order IDfunctionreleaseOrder(uint256 id) external{
require(id <= state.orders.length-1, "OC: UNKNOWN_ORDER");
SwapLib.releaseOrder(state, state2, owner(), id, false);
}
/// @notice Release an order using a market manager's signature/// @param id The order ID/// @param expireAt The expire time of the signature/// @param signature The signature created by the managerfunctionreleaseOrderByManagerSig(uint256 id, uint256 expireAt, bytescalldata signature) external{
require(id <= state.orders.length-1, "OC: UNKNOWN_ORDER");
SwapLib.releaseOrderByManagerSig(state, state2, owner(), id, expireAt, signature);
}
/// @notice Allow the dispute manager to release an order./// @param id The order ID.functionreleaseOrderByDispute(uint256 id) externalonlyDisputeManager{
SwapLib.releaseOrder(state, state2, owner(), id, true);
}
/// @notice Cancel an order/// @param id The order IDfunctioncancelOrder(uint256 id) external{
require(hasOrder(id), "OC: UNKNOWN_ORDER");
SwapLib.cancelOrder(state, state2, owner(), id, false);
}
/// @notice Cancel an order using a market manager's signature/// @param id The order ID/// @param expireAt The expire time of the signature/// @param signature The signature created by the managerfunctioncancelOrderByManagerSig(uint256 id, uint256 expireAt, bytescalldata signature) external{
require(id <= state.orders.length-1, "OC: UNKNOWN_ORDER");
SwapLib.cancelOrderByManagerSig(state, state2, owner(), id, expireAt, signature);
}
/// @notice Allow the dispute manager to cancel an order./// @param id The order ID.functioncancelOrderByDispute(uint256 id) externalonlyDisputeManager{
SwapLib.cancelOrder(state, state2, owner(), id, true);
}
/// @notice Mark a swap order as paid./// Order must be have been released or cancelled./// When the base asset is ERC20, the sender must be the taker./// When the base asset is synthetic,the sender must be the provider./// @param id The order ID.functionmarkOrderAsPaid(uint256 id) external{
SwapLib.markOrderAsPaid(state, state2.marketCore, id, false);
}
/// @notice Mark a swap order as paid using a market manager's signature./// @param id The order ID./// @param expireAt The expire time of the signature/// @param signature The signature created by the managerfunctionmarkOrderAsPaidByManagerSig(uint256 id, uint256 expireAt, bytescalldata signature) external{
require(id <= state.orders.length-1, "OC: UNKNOWN_ORDER");
SwapLib.markOrderAsPaidByManagerSig(state, state2, id, expireAt, signature);
}
/// @notice Checks if at order at index id exist/// @param id The order ID or index.functionhasOrder(uint256 id) publicviewreturns (bool) {
return AuxLib.hasOrder(state, id);
}
/// @notice Checks whether addr can create a dispute./// It will revert if order is unknown./// It will return false if order has been released./// It will return false if addr is not a provider or a taker./// It will return false if order has not be marked as `paid` and time to pay has not been reached./// @param id The order ID./// @param addr The address to check if permitted to create dispute.functioncanCreateDispute(uint256 id, address addr) externalviewreturns (bool) {
return AuxLib.canCreateDispute(state, state2, id, addr);
}
/// @notice Marks an order as `disputed`/// @param id The order ID or index.functionmarkAsDisputed(uint256 id) externalonlyDisputeManager{
SwapLib.markAsDisputed(state, state2.marketCore, id);
}
/// @notice Get an order by ID/// @param id The order IDfunctiongetOrder(uint256 id) externalviewreturns (Structs.Order memory) {
require(hasOrder(id), "OC: UNKNOWN_ORDER");
return state.orders[id];
}
/// @notice Get an offer by ID/// @param id The offer IDfunctiongetOffer(uint256 id) externalviewreturns (Structs.Offer memory) {
require(id < state2.offers.length, "OC: UNKNOWN_OFFER");
return state2.offers[id];
}
/// @notice Get a market by ID/// @param id The market IDfunctiongetMarket(address id) externalviewmarketMustExist(id) returns (Structs.Market memory) {
return state2.marketCore.getMarket(id);
}
/// @notice Transfer fees earned on shareable assets to the fee reward contract/// @param _token The address of a shareable tokenfunctiontransferToFeePool(address _token) external{
WithdrawLib.transferToFeePool(state, state2, _token);
}
/// @notice Withdraw fee earned by an operator for a given asset/// @param _token The address of a shareable tokenfunctionwithdrawOperatorBalance(address _token) external{
WithdrawLib.withdrawOperatorBalance(state, state2, _token);
}
/// @notice Return liquidity object of an address in a market/// @param _mid The market ID/// @param _owner The address of the owner of the liquidity/// @param _lid The liquidity IDfunctiongetLiquidity(address _mid, address _owner, uint256 _lid) externalviewreturns (Structs.Liquidity memory) {
return state.liquidity[_mid][_owner][_lid];
}
/// @notice Return liquidity of an address in a market/// @param _mid The market ID/// @param _owner The address of the owner of the liquidity/// @param _lid The liquidity IDfunctionhasLiquidity(address _mid, address _owner, uint256 _lid) externalviewreturns (bool) {
return AuxLib.isLiquidityExist(state, _mid, _owner, _lid);
}
/// @notice Allow an address to manage sender's liquidity/// @param marketId The market ID/// @param manager The address of the managerfunctionsetLiquidityManager(address marketId, address manager) external{
LiquidityLib.setLiquidityManager(state, marketId, manager);
}
/// @notice Get the liquidity manager address of a liquidity provider/// @param marketId The market ID/// @param provider The liquidity provider addressfunctiongetLiquidityManager(address marketId, address provider) externalviewreturns (address) {
return state.liqManagers[provider][marketId];
}
/// @notice Update fungible market (ERC20, SYNTH) liquidity/// @param marketId The unique ID of the market./// @param lid The liquidity ID/// @param deduct Indicate whether the liquidity should be deducted./// @param target Specify an address solely allowed to swap the liquidity/// @param amountAndPrice The amount of base asset and quote price per base asset./// 0: amount: The amount of base asset to add as liquidity/// 1: price: The quote price per base asset/// @param wantOrIds: List of token IDs where at least one token must match (req: when quote is non-fungible)./// @param wantAndIds: List of tokens IDs where all tokens must match (req: when quote is non-fungible)/// @param limits The swap and transaction limits/// 0: minSwap: The minimum swap amount for the sender's liquidity. Must be less than maxSwap./// 1: maxSwap: The maximum swap amount for the sender's liquidity. Must be greater than minSwap./// 2: timeToPay: The maximum seconds within which payer must make payment./// 3: expireAt: The transaction deadline/// 4: pause: Indicate whether to pause the liquidityfunctionupdateFungibleLiquidity(address marketId,
uint256 lid,
bool deduct,
address target,
uint256[2] memory amountAndPrice,
uint256[] memory wantOrIds,
uint256[] memory wantAndIds,
uint256[5] memory limits
) externalpayablenotExpired(limits[3]) marketMustExist(marketId) {
StructsLib.UpdateLiquiditySlot memory slot = StructsLib.UpdateLiquiditySlot(
marketId,
lid,
deduct,
limits[4] >0,
amountAndPrice[0],
newuint256[](0),
wantOrIds,
wantAndIds,
amountAndPrice[1],
limits[0],
limits[1],
limits[2],
target
);
FungibleLiquidityLib.updateFungibleLiquidity(state, state2, slot);
}
/// @notice Update non-fungible market (ERC721, ERC1155) liquidity/// @param marketId The unique ID of the market./// @param lid The liquidity ID/// @param deduct Indicate whether the liquidity should be deducted./// @param target Specify an address solely allowed to swap the liquidity/// @param ids Indicate the token ids to use as liquidity/// @param price The quote price to accept for all tokens listed in <ids> (not required if quote asset is non-fungible)/// @param wantOrIds: List of token IDs where at least one token must match (req: when quote is non-fungible)./// @param wantAndIds: List of tokens IDs where all tokens must match (req: when quote is non-fungible)/// @param limits The swap and transaction limits/// 0: timeToPay: The maximum seconds within which payer must make payment./// 1: expireAt: The transaction deadline/// 2: pause: Indicate whether to pause the liquidityfunctionupdateNonFungibleLiquidity(address marketId,
uint256 lid,
bool deduct,
address target,
uint256[] memory ids,
uint256 price,
uint256[] memory wantOrIds,
uint256[] memory wantAndIds,
uint256[3] memory limits
) externalnotExpired(limits[1]) marketMustExist(marketId) {
StructsLib.UpdateLiquiditySlot memory slot = StructsLib.UpdateLiquiditySlot(
marketId,
lid,
deduct,
limits[2] >0,
0,
ids,
wantOrIds,
wantAndIds,
price,
0,
0,
limits[0],
target
);
NonFungibleLiquidityLib.updateNonFungibleLiquidity(state, state2, slot);
}
/// @notice Set required swap badges/// @param marketId The market ID/// @param lid The liquidity ID/// @param badgeIds The badge IDsfunctionsetSwapBadgeForLiquidity(address marketId, uint256 lid, string[2][] memory badgeIds) external{
LiquidityLib.setSwapBadgeForLiquidity(state, state2, marketId, lid, badgeIds);
}
/// @notice Get required swap badges of an address/// @param marketId The market ID/// @param lid The liquidity ID/// @param addr The addressfunctiongetReqSwapBadges(address marketId, uint256 lid, address addr) externalviewreturns (string[2][] memory) {
return state2.reqSwapBadges[marketId][addr][lid];
}
/// @notice Increment open dispute count of a provider's liquidity/// @param marketId The market ID/// @param provider The provider address/// @param lid The liquidity ID/// @param incr If true, count is incremented, else decrementedfunctionupdateOpenDisputesCount(address marketId, address provider, uint256 lid, bool incr) publiconlyDisputeManager{
LiquidityLib.updateOpenDisputesCount(state, marketId, provider, lid, incr);
}
/// @notice Allows liquidity manager to update a provider's liquidity/// @param _marketId The market ID/// @param _owner The owner of the liquidity/// @param _lid The liquidity ID/// @param _price The new _price of the liquidity/// @param _expireAt The transaction is reverted if current time >= _expireAtfunctionupdateLiquidityPrice(address _marketId,
address _owner,
uint256 _lid,
uint256 _price,
uint256 _expireAt
) externalnotExpired(_expireAt) marketMustExist(_marketId) {
LiquidityLib.updateLiquidityPrice(state, state2, _marketId, _owner, _lid, _price);
}
/// @notice Create an offer/// @param marketId The market ID/// @param provider The provider's address/// @param lid The liquidity ID/// @param amount The offer amount/// @param price The offer price/// @param ids The IDs of non-fungible assets/// @param deadline The time when the offer expires.functioncreateOffer(address marketId,
address provider,
address recipient,
uint256 lid,
uint256 amount,
uint256 price,
uint256[] memory ids,
uint256 deadline,
uint256 operator
) public{
OfferLib.CreateOfferAddressParams memory offerAddrs = OfferLib.CreateOfferAddressParams(marketId, provider, recipient);
OfferLib.CreateOfferIntParams memory offerInts = OfferLib.CreateOfferIntParams(lid, amount, price, deadline, operator);
OfferLib.createOffer(state, state2, offerAddrs, offerInts, ids);
}
/// @notice Create an offer/// @param offerId The offer ID/// @param amount The offer amount/// @param price The offer price/// @param ids The IDs of non-fungible assets/// @param deadline The time when the offer expires.functionupdateOffer(uint256 offerId, uint256 amount, uint256 price, uint256[] memory ids, uint256 deadline) public{
OfferLib.updateOffer(state, state2, offerId, amount, price, ids, deadline);
}
/// @notice Accept an offer/// @param offerId The offer ID/// @param delay The number of seconds that must pass before offer can be executedfunctionacceptOffer(uint256 offerId, uint256 delay) public{
OfferLib.acceptOffer(state2, offerId, delay);
}
/// @notice Unaccept an offer/// @param offerId The offer IDfunctionunacceptOffer(uint256 offerId) public{
OfferLib.unacceptOffer(state2, offerId);
}
/// @notice Cancel an offer/// @param offerId The offer IDfunctioncancelOffer(uint256 offerId) public{
OfferLib.cancelOffer(state2, offerId);
}
/// @notice Execute an offer/// @param offerId The offer ID/// @param txDeadline The transaction is reverted if current time >= expireAtfunctionexecuteOffer(uint256 offerId, uint256 txDeadline) publicnotExpired(txDeadline) {
OfferLib.executeOffer(state, state2, offerId);
}
/// @notice Toggle liquidity pause state/// @param marketId The market ID/// @param lid The liquidity ID/// @param expireAt The transaction is reverted if current time >= expireAtfunctiontogglePause(address marketId, uint256 lid, uint256 expireAt) publicnotExpired(expireAt) {
LiquidityLib.togglePause(state, marketId, lid);
}
}
Contract Source Code
File 5 of 44: ECDSA.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)pragmasolidity ^0.8.0;import"../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/libraryECDSA{
enumRecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function_throwError(RecoverError error) privatepure{
if (error == RecoverError.NoError) {
return; // no error: do nothing
} elseif (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} elseif (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} elseif (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/functiontryRecover(bytes32 hash, bytesmemory signature) internalpurereturns (address, RecoverError) {
if (signature.length==65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them// currently is to use assembly./// @solidity memory-safe-assemblyassembly {
r :=mload(add(signature, 0x20))
s :=mload(add(signature, 0x40))
v :=byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/functionrecover(bytes32 hash, bytesmemory signature) internalpurereturns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/functiontryRecover(bytes32 hash,
bytes32 r,
bytes32 vs
) internalpurereturns (address, RecoverError) {
bytes32 s = vs &bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v =uint8((uint256(vs) >>255) +27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/functionrecover(bytes32 hash,
bytes32 r,
bytes32 vs
) internalpurereturns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/functiontryRecover(bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internalpurereturns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most// signatures from current libraries generate a unique signature with an s-value in the lower half order.//// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept// these malleable signatures as well.if (uint256(s) >0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer addressaddress signer =ecrecover(hash, v, r, s);
if (signer ==address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/functionrecover(bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internalpurereturns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/functiontoEthSignedMessageHash(bytes32 hash) internalpurereturns (bytes32) {
// 32 is the length in bytes of hash,// enforced by the type signature abovereturnkeccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/functiontoEthSignedMessageHash(bytesmemory s) internalpurereturns (bytes32) {
returnkeccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/functiontoTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internalpurereturns (bytes32) {
returnkeccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
Contract Source Code
File 6 of 44: ERC1155Holder.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol)pragmasolidity ^0.8.0;import"./ERC1155Receiver.sol";
/**
* Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
*
* IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
* stuck.
*
* @dev _Available since v3.1._
*/contractERC1155HolderisERC1155Receiver{
functiononERC1155Received(address,
address,
uint256,
uint256,
bytesmemory) publicvirtualoverridereturns (bytes4) {
returnthis.onERC1155Received.selector;
}
functiononERC1155BatchReceived(address,
address,
uint256[] memory,
uint256[] memory,
bytesmemory) publicvirtualoverridereturns (bytes4) {
returnthis.onERC1155BatchReceived.selector;
}
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)pragmasolidity ^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.
*/abstractcontractERC165isIERC165{
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IERC165).interfaceId;
}
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)pragmasolidity ^0.8.0;import"../../utils/introspection/IERC165.sol";
/**
* @dev _Available since v3.1._
*/interfaceIERC1155ReceiverisIERC165{
/**
* @dev Handles the receipt of a single ERC1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/functiononERC1155Received(address operator,
addressfrom,
uint256 id,
uint256 value,
bytescalldata data
) externalreturns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/functiononERC1155BatchReceived(address operator,
addressfrom,
uint256[] calldata ids,
uint256[] calldata values,
bytescalldata data
) externalreturns (bytes4);
}
Contract Source Code
File 15 of 44: IERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)pragmasolidity ^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}.
*/interfaceIERC165{
/**
* @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.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=0.7.5;pragmaabicoderv2;import'@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
/// @title Router token swapping functionality/// @notice Functions for swapping tokens via Uniswap V3interfaceISwapRouterisIUniswapV3SwapCallback{
structExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata/// @return amountOut The amount of the received tokenfunctionexactInputSingle(ExactInputSingleParams calldata params) externalpayablereturns (uint256 amountOut);
structExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata/// @return amountOut The amount of the received tokenfunctionexactInput(ExactInputParams calldata params) externalpayablereturns (uint256 amountOut);
structExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata/// @return amountIn The amount of the input tokenfunctionexactOutputSingle(ExactOutputSingleParams calldata params) externalpayablereturns (uint256 amountIn);
structExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata/// @return amountIn The amount of the input tokenfunctionexactOutput(ExactOutputParams calldata params) externalpayablereturns (uint256 amountIn);
}
Contract Source Code
File 24 of 44: IUniswapV3SwapCallback.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=0.5.0;/// @title Callback for IUniswapV3PoolActions#swap/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interfaceinterfaceIUniswapV3SwapCallback{
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap./// @dev In the implementation you must pay the pool tokens owed for the swap./// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory./// amount0Delta and amount1Delta can both be 0 if no tokens were swapped./// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by/// the end of the swap. If positive, the callback must send that amount of token0 to the pool./// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by/// the end of the swap. If positive, the callback must send that amount of token1 to the pool./// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap callfunctionuniswapV3SwapCallback(int256 amount0Delta,
int256 amount1Delta,
bytescalldata data
) external;
}
Contract Source Code
File 25 of 44: LiqAuxLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"../common/Structs.sol";
import"../market/interfaces/IMarketCore.sol";
import"../common/SafeERC20.sol";
import"../token/interfaces/IERC721.sol";
import"../token/interfaces/IERC1155.sol";
import"../common/StrippedInterfaces.sol";
import"../common/MathLib.sol";
import"./AuxLib.sol";
import"../market/MarketAuxLib.sol";
import"./StructsLib.sol";
import"../assetregistry/interfaces/IAssetRegistry.sol";
import"../feepurse/interfaces/IFeePurse.sol";
libraryLiqAuxLib{
usingMarketAuxLibforStructs.Asset;
usingMarketAuxLibforStructs.Market;
/// @dev Calculate and lock liquidity fee for protocol in non-ERC20 market/// @param s1 A state object/// @param s2 A state object/// @param mkt The market object/// @param liq The liquidity object/// @param amount The amount of liquidity to lock fee forfunctionlockFeeForProtocol(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
Structs.Market memory mkt,
Structs.Liquidity storage liq,
uint256 amount
) internal{
(, uint256 fixedFee, ) = AuxLib.getFeeInfo(s2.assetReg, mkt.base, 0);
uint256 fixedFeeInternal = mkt.base.isSynth() ? s1.ints.fixedSynthSwapFee : s1.ints.fixedNFTSwapFee;
uint256 fee = amount * ((fixedFee >0) ? fixedFee : fixedFeeInternal);
fee = mkt.base.isSynth() ? fee /10**18 : fee;
if (fee >0&& AuxLib.hasNativeNFT(s2.nft, msg.sender) && s2.ints.nftDiscountBp >0) {
fee = fee - (fee * s2.ints.nftDiscountBp) /10000;
}
if (fee >0) {
uint256 prevLockedFee = s2.feePurse.getLocked(liq.protoFeePurseId);
if (prevLockedFee >0) s2.feePurse.unlock(msg.sender, liq.protoFeePurseId);
liq.protoFeePurseId = s2.feePurse.lock(msg.sender, prevLockedFee + fee);
}
// Update fee rate of liquidityuint256 curLiqAmt = mkt.base.isSynth() ? liq.amount : liq.ids.length;
uint256 curLocked = s2.feePurse.getLocked(liq.protoFeePurseId);
liq.protoFeeRate = (mkt.base.isSynth() ? (curLocked *10**18) : curLocked) / curLiqAmt;
}
/// @dev Calculate and unlock liquidity fee for protocol in a non-ERC20 market/// @param s2 A state object/// @param mkt The market object/// @param liq The liquidity objectfunctionunlockFeeForProtocol(StructsLib.State2 storage s2, Structs.Market memory mkt, Structs.Liquidity storage liq) internal{
uint256 remaining = mkt.base.isSynth() ? liq.amount : liq.ids.length;
if (remaining ==0) {
s2.feePurse.unlock(msg.sender, liq.protoFeePurseId);
liq.protoFeeRate =0;
liq.protoFeePurseId =0;
return;
}
s2.feePurse.unlock(msg.sender, liq.protoFeePurseId);
uint256 newAmtToLck = liq.protoFeeRate * remaining;
newAmtToLck = mkt.base.isSynth() ? newAmtToLck /10**18 : newAmtToLck;
liq.protoFeePurseId = (newAmtToLck >0) ? s2.feePurse.lock(msg.sender, newAmtToLck) : 0;
// Update fee rate of liquidityuint256 curLocked = s2.feePurse.getLocked(liq.protoFeePurseId);
liq.protoFeeRate = (mkt.base.isSynth() ? (curLocked *10**18) : curLocked) / remaining;
}
/// @dev Calculate and lock liquidity fee for market creator in non-ERC20 market/// @param s2 A state object/// @param mkt The market object/// @param liq The liquidity object/// @param amount The amount of liquidity to lock fee forfunctionlockFeeForMarketCreator(
StructsLib.State2 storage s2,
Structs.Market memory mkt,
Structs.Liquidity storage liq,
uint256 amount
) internal{
uint256 fee = amount * mkt.commission;
fee = mkt.base.isSynth() ? fee /10**18 : fee;
if (fee >0&& AuxLib.hasNativeNFT(s2.nft, msg.sender) && s2.ints.nftDiscountBp >0) {
fee = fee - (fee * s2.ints.nftDiscountBp) /10000;
}
if (fee >0) {
uint256 prevLockedFee = s2.feePurse.getLocked(liq.mktCreatorFeePurseId);
if (prevLockedFee >0) s2.feePurse.unlock(msg.sender, liq.mktCreatorFeePurseId);
liq.mktCreatorFeePurseId = s2.feePurse.lock(msg.sender, prevLockedFee + fee);
}
// Update fee rate of liquidityuint256 curLiqAmt = mkt.base.isSynth() ? liq.amount : liq.ids.length;
uint256 curLocked = s2.feePurse.getLocked(liq.mktCreatorFeePurseId);
liq.mktCreatorFeeRate = (mkt.base.isSynth() ? (curLocked *10**18) : curLocked) / curLiqAmt;
}
/// @dev Calculate and unlock liquidity fee for market creator in a non-ERC20 market/// @param s2 A state object/// @param mkt The market object/// @param liq The liquidity objectfunctionunlockFeeForMarketCreator(StructsLib.State2 storage s2, Structs.Market memory mkt, Structs.Liquidity storage liq) internal{
uint256 remaining = mkt.base.isSynth() ? liq.amount : liq.ids.length;
if (remaining ==0) {
s2.feePurse.unlock(msg.sender, liq.mktCreatorFeePurseId);
liq.mktCreatorFeeRate =0;
liq.mktCreatorFeePurseId =0;
return;
}
s2.feePurse.unlock(msg.sender, liq.mktCreatorFeePurseId);
uint256 newAmtToLck = liq.mktCreatorFeeRate * remaining;
newAmtToLck = mkt.base.isSynth() ? newAmtToLck /10**18 : newAmtToLck;
liq.mktCreatorFeePurseId = (newAmtToLck >0) ? s2.feePurse.lock(msg.sender, newAmtToLck) : 0;
// Update fee rate of liquidityuint256 curLocked = s2.feePurse.getLocked(liq.mktCreatorFeePurseId);
liq.mktCreatorFeeRate = (mkt.base.isSynth() ? (curLocked *10**18) : curLocked) / remaining;
}
/// @dev Check if sender has the required badges for the market's liquidity providers/// @param s2 A state object/// @param marketId The market IDfunctioncheckMarketPutBadges(StructsLib.State2 storage s2, address marketId) internalview{
(string[2][] memory reqPutBadges, ) = s2.marketCore.getReqBadges(marketId);
if (reqPutBadges.length>0) {
uint256 len = reqPutBadges.length;
for (uint256 i =0; i < len; i++) {
require(s2.badge.hasBadge(msg.sender, reqPutBadges[i][0], reqPutBadges[i][1]), "OC: MKT_BADGE_REQ");
}
}
}
}
Contract Source Code
File 26 of 44: LiquidityLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"../common/Structs.sol";
import"../market/interfaces/IMarketCore.sol";
import"../common/SafeERC20.sol";
import"../token/interfaces/IERC721.sol";
import"../token/interfaces/IERC1155.sol";
import"../common/StrippedInterfaces.sol";
import"../common/MathLib.sol";
import"./AuxLib.sol";
import"../market/MarketAuxLib.sol";
import"./StructsLib.sol";
import"../assetregistry/interfaces/IAssetRegistry.sol";
import"../feepurse/interfaces/IFeePurse.sol";
libraryLiquidityLib{
usingSafeERC20forIERC20;
usingMarketAuxLibforStructs.Asset;
usingMarketAuxLibforStructs.Market;
usingAuxLibforuint256[];
usingAuxLibforStructs.Market;
usingAuxLibforStructs.Liquidity;
usingAuxLibforaddress;
eventUpdatedPrice(addressindexed marketId, addressindexed provider, addressindexed manager, uint256 lid, uint256 price);
eventTogglePause(addressindexed marketId, addressindexed provider, uint256 lid, bool paused);
eventUpdatedLiquidityManager(addressindexed marketId, addressindexed provider, addressindexed manager, address oldManager);
eventUpdatedLiquiditySwapBadges(address marketId, address provider, uint256 lid, string[2][] badges);
/// @dev Returns matching want IDs./// @param ids The IDs to search/// @param wantOrIds The IDs to match at least one of/// If wantOrIds is not empty, at least one entry must be included in ids./// @param wantAndIds The IDs to match all of/// If wantAndIds and wantOrIds are empty, return entries in ids./// If wantAndIds is not empty, all entries must be included in ids./// @return matchIds The matching IDsfunctionfindMatchInWantIds(uint256[] memory ids,
mapping(uint256 => bool) storage intIdx,
uint256[] memory wantOrIds,
uint256[] memory wantAndIds
) publicreturns (uint256[] memory) {
if (wantOrIds.length==0&& wantAndIds.length==0) return ids;
uint256[] memory matchIds =newuint256[](ids.length);
mapping(uint256=>bool) storage matchIdx = intIdx;
uint256 idx;
uint256 andFound;
for (uint256 i =0; i < wantAndIds.length; i++) {
for (uint256 j =0; j < ids.length; j++) {
if (wantAndIds[i] == ids[j] &&!matchIdx[ids[j]]) {
matchIds[idx] = ids[j];
matchIdx[ids[j]] =true;
idx++;
andFound++;
}
}
}
if (andFound != wantAndIds.length) returnnewuint256[](0);
bool orFound = wantOrIds.length==0;
for (uint256 i =0; i < wantOrIds.length; i++) {
for (uint256 j =0; j < ids.length; j++) {
if (wantOrIds[i] == ids[j]) {
if (!matchIdx[ids[j]]) matchIds[idx] = ids[j];
orFound =true;
break;
}
}
if (orFound) break;
}
return (orFound) ? matchIds : newuint256[](0);
}
/// @dev Update the count of open disputes for a liquidity/// @param state State object/// @param marketId The market ID/// @param provider The address of the liquidity provider/// @param lid The liquidity ID/// @param incr True to increment, false to decrementfunctionupdateOpenDisputesCount(StructsLib.State storage state, address marketId, address provider, uint256 lid, bool incr) external{
Structs.Liquidity storage liq = state.liquidity[marketId][provider][lid];
if (incr) liq.openDisputes++;
else liq.openDisputes--;
}
/// @dev Set an address as the liquidity manager for a market/// @param marketId The market ID/// @param manager The address of the managerfunctionsetLiquidityManager(StructsLib.State storage state, address marketId, address manager) external{
address old = state.liqManagers[msg.sender][marketId];
state.liqManagers[msg.sender][marketId] = manager;
emit UpdatedLiquidityManager(marketId, msg.sender, manager, old);
}
/// @dev Allows liquidity manager to update a provider's liquidity/// @param state State object/// @param state2 State object/// @param owner The address of the liquidity owner/// @param lid The liquidity ID/// @param price The new price for the liquidityfunctionupdateLiquidityPrice(
StructsLib.State storage state,
StructsLib.State2 storage state2,
address marketId,
address owner,
uint256 lid,
uint256 price
) external{
require(state.liqManagers[owner][marketId] ==msg.sender, "OC: NOT_MANAGER");
Structs.Liquidity storage liq = state.liquidity[marketId][owner][lid];
require(price >0, "OC: PRICE_REQ");
Structs.Market memory market = state2.marketCore.getMarket(marketId);
require(MarketAuxLib.isFungible(market.quote), "OC: PRICE_NREQ");
liq.price = price;
emit UpdatedPrice(marketId, owner, msg.sender, lid, price);
}
/// @dev Toggle liquidity pause state/// @param state The State object/// @param mid The market ID/// @param lid The liquidity IDfunctiontogglePause(StructsLib.State storage state, address mid, uint256 lid) external{
require(AuxLib.hasNonZeroLiquidity(state, mid, msg.sender, lid), "OC: ZERO_LIQ");
state.liquidity[mid][msg.sender][lid].paused =!state.liquidity[mid][msg.sender][lid].paused;
emit TogglePause(mid, msg.sender, lid, state.liquidity[mid][msg.sender][lid].paused);
}
/// @dev Set swap badges for a liquidity/// @param state The State object/// @param state2 The State2 object/// @param mid The market ID/// @param lid The liquidity ID/// @param badges The badges to setfunctionsetSwapBadgeForLiquidity(
StructsLib.State storage state,
StructsLib.State2 storage state2,
address mid,
uint256 lid,
string[2][] memory badges
) external{
require(AuxLib.hasNonZeroLiquidity(state, mid, msg.sender, lid), "OC: ZERO_LIQ");
require(badges.length<=8, "OC: TOO_MANY_BADGES");
state2.reqSwapBadges[mid][msg.sender][lid] = badges;
emit UpdatedLiquiditySwapBadges(mid, msg.sender, lid, badges);
}
}
Contract Source Code
File 27 of 44: MarketAuxLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"../common/Structs.sol";
libraryMarketAuxLib{
/// @dev Check if an asset is a native asset./// @param asset The asset object/// @return True if the asset is a native assetfunctionisNative(Structs.Asset memory asset) publicpurereturns (bool) {
return (asset.assetType == Structs.AssetType.NATIVE);
}
/// @dev Check if an asset is a synthetic asset./// @param asset The asset object/// @return True if the asset is a synthetic assetfunctionisSynth(Structs.Asset memory asset) publicpurereturns (bool) {
return (asset.assetType == Structs.AssetType.SYNTH);
}
/// @dev Check if an asset is an ERC20 asset./// @param asset The asset object/// @return True if the asset is an ERC20 assetfunctionisERC20(Structs.Asset memory asset) publicpurereturns (bool) {
return (asset.assetType == Structs.AssetType.ERC20);
}
/// @dev Check if an asset is an ERC721 asset./// @param asset The asset object/// @return True if the asset is an ERC721 assetfunctionisERC721(Structs.Asset memory asset) publicpurereturns (bool) {
return (asset.assetType == Structs.AssetType.ERC721);
}
/// @dev Check if an asset is an ERC1155 asset./// @param asset The asset object/// @return True if the asset is an ERC1155 assetfunctionisERC1155(Structs.Asset memory asset) publicpurereturns (bool) {
return (asset.assetType == Structs.AssetType.ERC1155);
}
/// @dev Check if an asset is either an ERC721 or ERC1155 asset./// @param asset The asset object/// @return True if the asset is one of the twofunctionisNFT(Structs.Asset memory asset) publicpurereturns (bool) {
return isERC721(asset) || isERC1155(asset);
}
/// @dev Check if an asset is either an ERC20, synthetic, or native asset./// @param asset The asset object/// @return True if the asset is one of the twofunctionisFungible(Structs.Asset memory asset) publicpurereturns (bool) {
return isSynth(asset) || isERC20(asset) || isNative(asset);
}
/// @dev Check if a market includes an ERC20, synthetic or native asset on the base or quote side./// @param mkt The market object/// @return True if the market includes an ERC20 or synthetic assetfunctionisFungible(Structs.Market memory mkt) publicpurereturns (bool) {
return
(isERC20(mkt.base) || isSynth(mkt.base) || isNative(mkt.base)) &&
(isERC20(mkt.quote) || isSynth(mkt.quote) || isNative(mkt.quote));
}
/// @dev Check if a market is an instant market (both assets are on-chain)./// @param mkt The market object/// @return True if the market is an instant marketfunctionisInstantMarket(Structs.Market memory mkt) publicpurereturns (bool) {
return
((isERC20(mkt.base) && isERC20(mkt.quote)) ||
(isERC20(mkt.base) && isNFT(mkt.quote)) ||
(isERC20(mkt.base) && isNative(mkt.quote)) ||
(isNFT(mkt.base) && isERC20(mkt.quote)) ||
(isNFT(mkt.base) && isNFT(mkt.quote))) ||
(isNFT(mkt.base) && isNative(mkt.quote)) ||
(isNative(mkt.base) && isNative(mkt.quote)) ||
(isNative(mkt.base) && isERC20(mkt.quote)) ||
(isNative(mkt.base) && isNFT(mkt.quote));
}
/// @dev Check if a market includes an ERC20 or native asset on the base or quote side./// @param mkt The market object/// @return True if the market includes an ERC20 assetfunctionhasERC20OrNative(Structs.Market memory mkt) publicpurereturns (bool) {
return isERC20(mkt.base) || isERC20(mkt.quote) || isNative(mkt.base) || isNative(mkt.quote);
}
/// @dev Check if an asset exist on-chain/// @param asset The asset object/// @return True if the asset exist on-chainfunctionisOnchain(Structs.Asset memory asset) publicpurereturns (bool) {
return isERC20(asset) || isNFT(asset) || isNative(asset);
}
/// @dev Determine the payer of the swap order./// @param mkt The market object/// @param o The order objectfunctiongetPayer(Structs.Market memory mkt, Structs.Order storage o) publicviewreturns (address) {
address payer;
if (isOnchain(mkt.base) && isSynth(mkt.quote)) {
payer = o.taker;
} elseif (isSynth(mkt.base) && isSynth(mkt.quote)) {
payer = o.taker;
} elseif (isSynth(mkt.base) && isOnchain(mkt.quote)) {
payer = o.provider;
}
return payer;
}
}
Contract Source Code
File 28 of 44: Math.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)pragmasolidity ^0.8.0;/**
* @dev Standard math utilities missing in the Solidity language.
*/libraryMath{
enumRounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/functionmax(uint256 a, uint256 b) internalpurereturns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/functionmin(uint256 a, uint256 b) internalpurereturns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/functionaverage(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b) / 2 can overflow.return (a & b) + (a ^ b) /2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/functionceilDiv(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.return a ==0 ? 0 : (a -1) / b +1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/functionmulDiv(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use// 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 productuint256 prod1; // Most significant 256 bits of the productassembly {
let mm :=mulmod(x, y, not(0))
prod0 :=mul(x, y)
prod1 :=sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.if (prod1 ==0) {
return prod0 / denominator;
}
// 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].uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder :=mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 :=sub(prod1, gt(remainder, prod0))
prod0 :=sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.// See https://cs.stackexchange.com/q/138556/92363.// Does not overflow because the denominator cannot be zero at this stage in the function.uint256 twos = denominator & (~denominator +1);
assembly {
// Divide denominator by twos.
denominator :=div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 :=div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos :=add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
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 for// four bits. That is, denominator * inv = 1 mod 2^4.uint256 inverse = (3* denominator) ^2;
// Use the 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.
inverse *=2- denominator * inverse; // inverse mod 2^8
inverse *=2- denominator * inverse; // inverse mod 2^16
inverse *=2- denominator * inverse; // inverse mod 2^32
inverse *=2- denominator * inverse; // inverse mod 2^64
inverse *=2- denominator * inverse; // inverse mod 2^128
inverse *=2- denominator * inverse; // 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 preconditions 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 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/functionmulDiv(uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internalpurereturns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up &&mulmod(x, y, denominator) >0) {
result +=1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/functionsqrt(uint256 a) internalpurereturns (uint256) {
if (a ==0) {
return0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.//// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.//// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`//// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.uint256 result =1<< (log2(a) >>1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision// into the expected uint128 result.unchecked {
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/functionsqrt(uint256 a, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/functionlog2(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=128;
}
if (value >>64>0) {
value >>=64;
result +=64;
}
if (value >>32>0) {
value >>=32;
result +=32;
}
if (value >>16>0) {
value >>=16;
result +=16;
}
if (value >>8>0) {
value >>=8;
result +=8;
}
if (value >>4>0) {
value >>=4;
result +=4;
}
if (value >>2>0) {
value >>=2;
result +=2;
}
if (value >>1>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog2(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result =log2(value);
return result + (rounding == Rounding.Up &&1<< result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/functionlog10(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >=10**64) {
value /=10**64;
result +=64;
}
if (value >=10**32) {
value /=10**32;
result +=32;
}
if (value >=10**16) {
value /=10**16;
result +=16;
}
if (value >=10**8) {
value /=10**8;
result +=8;
}
if (value >=10**4) {
value /=10**4;
result +=4;
}
if (value >=10**2) {
value /=10**2;
result +=2;
}
if (value >=10**1) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog10(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up &&10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/functionlog256(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=16;
}
if (value >>64>0) {
value >>=64;
result +=8;
}
if (value >>32>0) {
value >>=32;
result +=4;
}
if (value >>16>0) {
value >>=16;
result +=2;
}
if (value >>8>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog256(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up &&1<< (result *8) < value ? 1 : 0);
}
}
}
Contract Source Code
File 29 of 44: MathLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;libraryMathLib{
functionsqrt(uint256 x) internalpurereturns (uint256 y) {
uint256 z = (x +1) /2;
y = x;
while (z < y) {
y = z;
z = (x / z + z) /2;
}
}
/**
* @dev Returns the largest of two numbers.
*/functionmax(uint256 a, uint256 b) internalpurereturns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/functionmin(uint256 a, uint256 b) internalpurereturns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/functionaverage(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b) / 2 can overflow.return (a & b) + (a ^ b) /2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/functionceilDiv(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.return a / b + (a % b ==0 ? 0 : 1);
}
}
Contract Source Code
File 30 of 44: NonFungibleLiquidityLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"../common/Structs.sol";
import"../market/interfaces/IMarketCore.sol";
import"../common/SafeERC20.sol";
import"../token/interfaces/IERC721.sol";
import"../token/interfaces/IERC1155.sol";
import"../common/StrippedInterfaces.sol";
import"../common/MathLib.sol";
import"./AuxLib.sol";
import"../market/MarketAuxLib.sol";
import"./StructsLib.sol";
import"../assetregistry/interfaces/IAssetRegistry.sol";
import"../feepurse/interfaces/IFeePurse.sol";
import"./LiqAuxLib.sol";
libraryNonFungibleLiquidityLib{
usingMarketAuxLibforStructs.Asset;
usingMarketAuxLibforStructs.Market;
usingAuxLibforuint256[];
usingAuxLibforStructs.Market;
usingAuxLibforStructs.Liquidity;
usingAuxLibforaddress;
eventUpdatedNonFungibleLiquidity(addressindexed provider,
addressindexed marketId,
address target,
bool deduct,
bool paused,
uint256 lid,
uint256[] ids,
uint256 price,
uint256[] wantOrIds,
uint256[] wantAndIds,
uint256 timeToPay
);
/// @dev Create or update non-fungible liquidity./// Covers NFT/ERC20, NFT/SYNTH, NFT/NFT/// @param s1 Storage struct./// @param s2 Storage struct 2./// @param slot The parameters needed to create or update liquidity.functionupdateNonFungibleLiquidity(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
StructsLib.UpdateLiquiditySlot memory slot
) external{
require(slot.deduct || s2.marketCore.isPutPermitted(slot.marketId, msg.sender));
Structs.Market memory mkt = s2.marketCore.getMarket(slot.marketId);
require(mkt.base.isNFT(), "OC: UNEXP_BASE");
require(!mkt.isInstantMarket() || slot.timeToPay ==0, "OC: TTP_NREQ");
require(slot.timeToPay ==0|| slot.timeToPay >= s1.minTimeToPay, "OC: LOW_TTP");
require(mkt.quote.isFungible() || slot.price ==0, "OC: PRICE_NREQ");
require(!mkt.quote.isFungible() || slot.price >0, "OC: PRICE_REQ");
require(!mkt.quote.isFungible() || (slot.wantOrIds.length==0&& slot.wantAndIds.length==0), "OC: WANT_IDS_NREQ");
require(slot.wantOrIds.isUnique() && slot.wantAndIds.isUnique(), "OC: WANT_IDS_NUNIQ");
require(slot.wantOrIds.isMaxLen16() && slot.wantAndIds.isMaxLen16(), "OC: WANT_IDS_HSIZE");
require(slot.ids.isMaxLen16(), "OC: IDS_HSIZE");
require(slot.ids.isUnique(), "OC: IDS_NUNIQ");
// Check if the sender has the required market PUT badges.
LiqAuxLib.checkMarketPutBadges(s2, slot.marketId);
// Get the liquidity. If the liquidity does not exist, create a new one
Structs.Liquidity storage liq;
if (AuxLib.isLiquidityExist(s1, slot.marketId, msg.sender, slot.lid)) {
liq = s1.liquidity[slot.marketId][msg.sender][slot.lid];
} else {
uint256[] memory zero =newuint256[](0);
s1.liquidity[slot.marketId][msg.sender].push(
Structs.Liquidity(zero, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, zero, zero, false, slot.target)
);
slot.lid = s1.liquidity[slot.marketId][msg.sender].length-1;
liq = s1.liquidity[slot.marketId][msg.sender][slot.lid];
}
// For new liquidity, ensure ids is not emptyrequire(!liq.isLiquidityEmpty() || slot.ids.length>0, "OC: REQ_IDS");
// Handle increment// - Transfer the base asset into the contract// - Increment listing pool balance if BASE asset is verified and market is not permissionedif (!slot.deduct) {
uint256 numIds = slot.ids.length;
mkt.numLiquidity += numIds;
for (uint256 i =0; i < numIds; i++) {
liq.ids.push(slot.ids[i]);
if (mkt.base.isERC721()) {
IERC721(mkt.base.addr).transferFrom(msg.sender, address(this), slot.ids[i]);
} else {
IERC1155(mkt.base.addr).safeTransferFrom(msg.sender, address(this), slot.ids[i], 1, "");
}
}
if (!mkt.hasERC20OrNative()) {
LiqAuxLib.lockFeeForProtocol(s1, s2, mkt, liq, numIds);
LiqAuxLib.lockFeeForMarketCreator(s2, mkt, liq, numIds);
}
}
// Handle decrement// - Remove indexes provided in slot.ids// - Transfer asset back to owner// - Decrement listing pool balance if BASE is verified and market is not permissionedif (slot.deduct && slot.ids.length>0) {
uint256 popped =0;
for (uint256 i =0; i < slot.ids.length; i++) {
if (slot.ids[i] - popped >= liq.ids.length) continue;
uint256 id = liq.ids[slot.ids[i] - popped];
liq.ids[slot.ids[i] - popped] = liq.ids[liq.ids.length-1];
liq.ids.pop();
popped++;
if (mkt.base.isERC721()) {
IERC721(mkt.base.addr).transferFrom(address(this), msg.sender, id);
} else {
IERC1155(mkt.base.addr).safeTransferFrom(address(this), msg.sender, id, 1, "");
}
}
if (!mkt.hasERC20OrNative()) {
LiqAuxLib.unlockFeeForProtocol(s2, mkt, liq);
LiqAuxLib.unlockFeeForMarketCreator(s2, mkt, liq);
}
mkt.numLiquidity -= popped;
}
liq.timeToPay = slot.timeToPay;
liq.price = slot.price;
liq.wantAndIds = slot.wantAndIds;
liq.wantOrIds = slot.wantOrIds;
liq.target = slot.target;
liq.paused = slot.pause;
s2.marketCore.setCounters(slot.marketId, false, false, true, mkt.numLiquidity);
emit UpdatedNonFungibleLiquidity(
msg.sender,
slot.marketId,
slot.target,
slot.deduct,
slot.pause,
slot.lid,
slot.ids,
slot.price,
slot.wantOrIds,
slot.wantAndIds,
slot.timeToPay
);
}
}
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"../common/Structs.sol";
import"../market/interfaces/IMarketCore.sol";
import"../common/SafeERC20.sol";
import"../common/SigLib.sol";
import"./AuxLib.sol";
import"./StructsLib.sol";
import"../market/MarketAuxLib.sol";
import"./LiquidityLib.sol";
import {IERC721Strip, IERC1155Strip} from"../common/StrippedInterfaces.sol";
libraryOrderLib{
usingMarketAuxLibforStructs.Asset;
usingMarketAuxLibforStructs.Market;
usingAuxLibforaddress;
eventSwapOrder(addressindexed mid,
addressindexed provider,
addressindexed swapper,
address recipient,
uint256[7] numTypeInfo,
uint256[] idsFromProvider,
uint256[] idsFromTaker
);
eventOrderCancelled(uint256indexed orderID, addressindexed canceller, boolindexed byDispute);
eventSwapOrderPaid(uint256indexed orderID, address payer, uint256 disputeDelayDur);
eventFinalizedByManagerSig(address manager, address finalizer, uint256 orderId, uint action);
/// @notice Create order for ERC20/SYNTH, SYNTH/ERC20, SYNTH/SYNTH, SYNTH/NFT, NFT/SYNTH, NATIVE/SYNTH, SYNTH/NATIVE/// @param s1 The State object/// @param s2 The State2 object/// @param sp The swap parameters/// @param mkt The market object/// @param liq The liquidity objectfunctioncreateOrder(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
StructsLib.SwapSlot memory sp,
Structs.Market memory mkt,
Structs.Liquidity storage liq
) public{
// Ensure number of open orders and disputes are below limitrequire(liq.openOrders < s1.ints.maxOpenOrders, "OC: MAX_OPEN_ORDERS");
require(liq.openDisputes < s1.ints.maxOpenDisputes, "OC: MAX_OPEN_DISPUTES");
Structs.Order memory o;
o.offerId = sp.offerId;
o.amount = sp.amount;
o.price = (sp.offerId >0) ? sp.maxPrice : liq.price;
// When quote is NFT, transfer NFTs into contractif (mkt.quote.isNFT()) {
// Find the IDs that match the wants request.// If swap was initiated via offer, use offer supplied ids.
o.takerIds = (sp.offerId >0) ? sp.ids : LiquidityLib.findMatchInWantIds(sp.ids, s2.intIdx, liq.wantOrIds, liq.wantAndIds);
require(o.takerIds.length>0, "OC: NO_ID_MATCH");
for (uint256 i =0; i < o.takerIds.length; i++) {
if (o.takerIds[i] ==0) continue;
if (mkt.quote.isERC721()) IERC721Strip(mkt.quote.addr).transferFrom(msg.sender, address(this), o.takerIds[i]);
else IERC1155Strip(mkt.quote.addr).safeTransferFrom(msg.sender, address(this), o.takerIds[i], 1, "");
}
}
// When quote is ERC20, transfer quote into contract.// When quote is native, make sure the deposit sent is enough.if (mkt.quote.isERC20()) {
uint256 cost = (sp.amount * o.price) /10**18;
IERC20(mkt.quote.addr).transferFrom(msg.sender, address(this), cost);
} elseif (mkt.quote.isNative()) {
uint256 cost = (sp.amount * o.price) /10**18;
require(msg.value>= cost, "OC: LOW_DEPOSIT");
}
// Update liquidityuint256 deduction;
if (mkt.base.isNFT()) {
o.providerIds = liq.ids;
deduction = liq.ids.length;
delete liq.ids;
} elseif (mkt.quote.isNFT()) {
o.amount = liq.amount;
deduction = o.amount;
liq.amount =0;
} elseif (sp.amount >0) {
liq.amount -= sp.amount;
deduction = sp.amount;
}
// Create an order.uint256 timeToPay = (liq.timeToPay >0) ? liq.timeToPay : s1.minTimeToPay;
o.marketId = sp.addrs.marketId;
o.provider = sp.addrs.provider;
o.operator = sp.operator;
o.lid = sp.lid;
o.taker =msg.sender;
o.recipient = sp.addrs.recipient;
o.createdAt =block.timestamp;
o.payDeadline =block.timestamp+ timeToPay;
o.cancelAt =block.timestamp+ timeToPay + s1.orderGraceDur;
s1.orders.push(o);
s1.numOrders++;
liq.openOrders +=1;
s2.marketCore.setCounters(sp.addrs.marketId, true, false, false, mkt.numOrders +1);
s2.marketCore.setCounters(sp.addrs.marketId, false, false, true, mkt.numLiquidity - deduction);
emit SwapOrder(
sp.addrs.marketId,
sp.addrs.provider,
msg.sender,
sp.addrs.recipient,
[s1.numOrders -1, sp.lid, sp.amount, o.price, sp.offerId, sp.operator, timeToPay + s1.orderGraceDur],
o.providerIds,
o.takerIds
);
}
/// @notice Release an order by a market manager's signature/// @param s1 The State object/// @param s2 The State2 object/// @param owner The contract owner/// @param oid The order ID./// @param expireAt The expire time of the signature/// @param signature The signature created by the managerfunctioncancelOrderByManagerSig(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
address owner,
uint256 oid,
uint256 expireAt,
bytescalldata signature
) public{
Structs.Market memory mkt = s2.marketCore.getMarket(s1.orders[oid].marketId);
require(mkt.manager !=address(0), "OC: NO_MGR_SET");
require(expireAt >block.timestamp, "OC: EXPIRED_SIG");
require(verifyMarketManagerSig(mkt.manager, oid, 2, expireAt, signature), "OC: BAD_SIG");
cancelOrder(s1, s2, owner, oid, false, true);
emit FinalizedByManagerSig(mkt.manager, msg.sender, oid, 2);
}
/// @notice Cancel an inactive order. An inactive order is one where:/// The order has not been marked as paid, released or disputed/// and has reached its cancel time./// @param s1 State object/// @param s2 State2 object/// @param owner The contract owner/// @param id is the unique order ID/// @param byDispute Indicates that the method is called by the dispute manager contract./// @param byManager Indicates that the method is called by the market manager.functioncancelOrder(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
address owner,
uint256 id,
bool byDispute,
bool byManager
) public{
Structs.Order storage o = s1.orders[id];
Structs.Market memory mkt = s2.marketCore.getMarket(o.marketId);
require(
byDispute ||
byManager ||
o.provider ==msg.sender||
o.taker ==msg.sender||
mkt.manager ==msg.sender||
s2.minerReg.isMiner(msg.sender) ||
owner ==msg.sender,
"OC: NOT_AUTHORIZED"
);
require(byDispute || (!o.paid &&!o.released &&!o.disputed &&!o.cancelled), "OC: NOT_INACTIVE");
require(byDispute || o.cancelAt <=block.timestamp, "OC: NOT_CANCEL_TIME");
Structs.Liquidity storage liq = s1.liquidity[o.marketId][o.provider][o.lid];
// Handle ERC20/SYNTH, SYNTH/ERC20, SYNTH/NATIVE, NATIVE/SYNTHif (mkt.isFungible() && mkt.hasERC20OrNative()) {
liq.amount += o.amount;
s2.marketCore.setCounters(o.marketId, false, false, true, mkt.numLiquidity + o.amount);
if (mkt.quote.isERC20()) {
uint256 cost = (o.amount * o.price) /10**18;
IERC20(mkt.quote.addr).transfer(o.taker, cost);
}
if (mkt.quote.isNative()) {
uint256 cost = (o.amount * o.price) /10**18;
payable(o.taker).transfer(cost);
}
}
// Handle SYNTH/SYNTHif (mkt.base.isSynth() && mkt.quote.isSynth()) {
liq.amount += o.amount;
s2.marketCore.setCounters(o.marketId, false, false, true, mkt.numLiquidity + o.amount);
}
// Handle SYNTH/NFTif (mkt.base.isSynth() && mkt.quote.isNFT()) {
liq.amount += o.amount;
s2.marketCore.setCounters(o.marketId, false, false, true, mkt.numLiquidity + o.amount);
for (uint256 i =0; i < o.takerIds.length; i++) {
if (mkt.quote.isERC721()) IERC721Strip(mkt.quote.addr).transferFrom(address(this), o.taker, o.takerIds[i]);
else IERC1155Strip(mkt.quote.addr).safeTransferFrom(address(this), o.taker, o.takerIds[i], 1, "");
}
}
// Handle NFT/SYNTHif (mkt.base.isNFT() && mkt.quote.isSynth()) {
liq.ids = o.providerIds;
s2.marketCore.setCounters(o.marketId, false, false, true, mkt.numLiquidity + liq.ids.length);
}
liq.openOrders -=1;
o.cancelled =true;
o.finalizer =msg.sender;
if (s2.minerReg.isMiner(msg.sender)) {
s2.minerReg.addReward(MinerOperation.Cancel, msg.sender);
}
emit OrderCancelled(id, msg.sender, byDispute);
}
/// @notice Mark a swap order as paid using a market manager's signature/// @param s1 The State object/// @param s2 The State2 object/// @param oid The order ID./// @param expireAt The expire time of the signature/// @param signature The signature created by the managerfunctionmarkOrderAsPaidByManagerSig(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
uint256 oid,
uint256 expireAt,
bytescalldata signature
) public{
Structs.Market memory mkt = s2.marketCore.getMarket(s1.orders[oid].marketId);
require(mkt.manager !=address(0), "OC: NO_MGR_SET");
require(expireAt >block.timestamp, "OC: EXPIRED_SIG");
require(verifyMarketManagerSig(mkt.manager, oid, 0, expireAt, signature), "OC: BAD_SIG");
markOrderAsPaid(s1, s2.marketCore, oid, true);
emit FinalizedByManagerSig(mkt.manager, msg.sender, oid, 0);
}
/// @notice Mark a swap order as paid./// Order must be have been released or cancelled./// When the base asset is ERC20, the sender must be the taker./// When the base asset is synthetic,the sender must be the provider./// @param s1 State object/// @param marketCore The MarketCore contract address/// @param id The order ID./// @param byManager Indicates that the method is called by the market managerfunctionmarkOrderAsPaid(StructsLib.State storage s1, IMarketCore marketCore, uint256 id, bool byManager) public{
require(AuxLib.hasOrder(s1, id), "OC: UNKNOWN_ORDER");
Structs.Order storage o = s1.orders[id];
require(!o.released &&!o.cancelled &&!o.paid, "OC: NOT_ACTIVE");
Structs.Market memory market = marketCore.getMarket(o.marketId);
require(byManager ||msg.sender== MarketAuxLib.getPayer(market, o), "OC: NOT_PAYER");
o.paid =true;
o.disputeFrom =block.timestamp+ market.dur.disputeDelayDur;
emit SwapOrderPaid(id, byManager ? market.manager : msg.sender, market.dur.disputeDelayDur);
}
/// @notice Marks an order as `disputed`/// @param s1 State object/// @param marketCore The MarketCore contract address/// @param id The order ID or index.functionmarkAsDisputed(StructsLib.State storage s1, IMarketCore marketCore, uint256 id) public{
Structs.Order storage o = s1.orders[id];
o.disputed =true;
Structs.Market memory m = marketCore.getMarket(o.marketId);
m.numDisputes++;
marketCore.setCounters(o.marketId, false, true, false, m.numDisputes);
}
/// @notice Verify a market manager's signature/// @param signer The market manager's address/// @param id The order ID/// @param action Indicates the manager action (0: paid, 1: release, 2: cancel)/// @param expireAt Indicates the timestamp when the signature is to expire./// @param signature The signature created by the managerfunctionverifyMarketManagerSig(address signer,
uint256 id,
uint action,
uint256 expireAt,
bytesmemory signature
) internalviewreturns (bool) {
if (address(0) == signer) returnfalse;
bytes32 messageHash =keccak256(abi.encode(block.chainid, id, action, expireAt));
bytes32 ethSignedMessageHash = SigLib.getSignedMessageHash(messageHash);
return SigLib.recoverSigner(ethSignedMessageHash, signature) == signer;
}
}
Contract Source Code
File 33 of 44: OrderReleaseLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"../common/Structs.sol";
import"../market/interfaces/IMarketCore.sol";
import"../common/SafeERC20.sol";
import"../common/SigLib.sol";
import"./AuxLib.sol";
import"./StructsLib.sol";
import"@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import"../market/MarketAuxLib.sol";
import"./LiquidityLib.sol";
import"./FeeLib.sol";
import {IERC721Strip, IERC1155Strip} from"../common/StrippedInterfaces.sol";
libraryOrderReleaseLib{
usingMarketAuxLibforStructs.Asset;
usingMarketAuxLibforStructs.Market;
usingAuxLibforaddress;
eventSwap(addressindexed mid,
addressindexed provider,
addressindexed swapper,
address recipient,
address releaser,
uint256[6] numTypeInfo,
bool byDispute,
uint256[] idsFromProvider,
uint256[] idsFromTaker
);
eventFinalizedByManagerSig(address manager, address finalizer, uint256 orderId, uint action);
/// @notice Release an order by a market manager's signature/// @param s1 The State object/// @param s2 The State2 object/// @param owner The owner of the contract/// @param oid The order ID./// @param expireAt The expire time of the signature/// @param signature The signature created by the managerfunctionreleaseOrderByManagerSig(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
address owner,
uint256 oid,
uint256 expireAt,
bytescalldata signature
) public{
Structs.Market memory mkt = s2.marketCore.getMarket(s1.orders[oid].marketId);
require(mkt.manager !=address(0), "OC: NO_MGR_SET");
require(expireAt >block.timestamp, "OC: EXPIRED_SIG");
require(verifyMarketManagerSig(mkt.manager, oid, 1, expireAt, signature), "OC: BAD_SIG");
releaseOrder(s1, s2, owner, oid, false, true);
emit FinalizedByManagerSig(mkt.manager, msg.sender, oid, 1);
}
/// @notice Release asset./// @param s1 The State object/// @param s2 The State2 object/// @param owner The owner of the contract/// @param oid The order ID./// @param byDispute Indicates that the method is called by the dispute manager./// @param byManager Indicates that the method is called by the market manager via a manager signature.functionreleaseOrder(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
address owner,
uint256 oid,
bool byDispute,
bool byManager
) public{
Structs.Order storage o = s1.orders[oid];
require(!o.cancelled &&!o.released, "OC: ALREADY_FINALIZED");
Structs.Market memory mkt = s2.marketCore.getMarket(o.marketId);
address releaser = (byDispute)
? msg.sender
: (byManager)
? mkt.manager
: (o.taker == MarketAuxLib.getPayer(mkt, o))
? o.provider
: o.taker;
require(byDispute || byManager ||msg.sender== releaser ||msg.sender== owner, "OC: NOT_RELEASER");
require(o.paid, "OC: ORDER_UNPAID");
Structs.Asset memory asset;
// Handle ERC20/SYNTH, SYNTH/ERC20, SYNTH/NATIVE, NATIVE/SYNTHif (mkt.isFungible() && mkt.hasERC20OrNative()) {
asset = releaseForFungibles(s1, s2, o, mkt);
}
// Handle SYNTH/SYNTHif (mkt.base.isSynth() && mkt.quote.isSynth()) {
asset = releaseForSynthToSynthOrNFT(s1, s2, o, mkt);
}
// Handle SYNTH/NFTif (mkt.base.isSynth() && mkt.quote.isNFT()) {
asset = releaseForSynthToNFT(s1, s2, o, mkt);
}
// Handle NFT/SYNTHif (mkt.base.isNFT() && mkt.quote.isSynth()) {
asset = releaseForNFTToSynth(s1, s2, o, mkt);
}
// Mark as released.
o.released =true;
o.finalizer = releaser;
// Decrement open order count
s1.liquidity[o.marketId][o.provider][o.lid].openOrders -=1;
// Increment orderID since '0' indicates no order ID.emit Swap(
o.marketId,
o.provider,
o.taker,
o.recipient,
releaser,
[oid +1, o.lid, o.amount, o.price, o.offerId, o.operator],
byDispute,
o.providerIds,
o.takerIds
);
}
/// @dev Release asset for SYNTH/NFT/// @param s1 The State object/// @param s2 The State2 object/// @param o The order object/// @param mkt The market objectfunctionreleaseForSynthToNFT(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
Structs.Order storage o,
Structs.Market memory mkt
) internalreturns (Structs.Asset memory asset) {
asset = releaseForSynthToSynthOrNFT(s1, s2, o, mkt);
// Transfer locked NFT to providerfor (uint256 i =0; i < o.takerIds.length; i++) {
if (mkt.quote.isERC721()) IERC721Strip(mkt.quote.addr).transferFrom(address(this), o.provider, o.takerIds[i]);
else IERC1155Strip(mkt.quote.addr).safeTransferFrom(address(this), o.provider, o.takerIds[i], 1, "");
}
return asset;
}
structBoolSlot {
bool sharable;
bool isOperator;
}
/// @dev Release asset for NFT/SYNTH/// @param s1 The State object/// @param s2 The State2 object/// @param o The order object/// @param mkt The market objectfunctionreleaseForNFTToSynth(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
Structs.Order storage o,
Structs.Market memory mkt
) internalreturns (Structs.Asset memory asset) {
Structs.Liquidity storage liq = s1.liquidity[o.marketId][o.provider][o.lid];
// Compute fee
FeeInfo memory feeInfo = FeeLib.computeFee(
ComputeFeeParams(
mkt,
liq,
s2.assetReg,
s2.feePurse,
s1.ints.synthSwapFeeBp,
s1.ints.instantNFTSwapFeeBp,
s1.ints.instantSwapFeeBp,
s1.ints.stableSwapFeeBp,
s2.ints.nftDiscountBp,
o.lid,
o.amount,
o.price,
s2.nft,
o.provider,
o.taker
)
);
// Unlock fee from fee purse
s2.feePurse.unlock(o.provider, liq.protoFeePurseId);
s2.feePurse.unlock(o.provider, liq.mktCreatorFeePurseId);
liq.protoFeePurseId =0;
liq.mktCreatorFeePurseId =0;
// Transfer market creator fee to market creator
s2.feePurse.transferFrom(o.provider, mkt.creator, feeInfo.mktCreatorFee);
uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];
// Disburse fee to sharable pool if asset is shareable
BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, mkt.base.addr), s2.operatorMgr.isOperator(o.operator));
if (bools.sharable) {
s2.feePurse.transferFrom(o.provider, address(this), (feeInfo.protoFee * s1.feeBPs[1]) /10000);
totalFeeBps -= s1.feeBPs[1];
}
// Disburse fee to operator if operator is setif (bools.isOperator) {
address opAddr = s2.operatorMgr.getOperator(o.operator);
s2.feePurse.transferFrom(o.provider, opAddr, (feeInfo.protoFee * s1.feeBPs[2]) /10000);
totalFeeBps -= s1.feeBPs[2];
}
// Disburse fee to inventor
s2.feePurse.transferFrom(o.provider, s1.inventor, (feeInfo.protoFee * totalFeeBps) /10000);
// Transfer the base asset to recipientfor (uint256 i =0; i < o.providerIds.length; i++)
if (mkt.base.isERC721())
IERC721Strip(mkt.base.addr).transferFrom(address(this), o.recipient.setOrDef(o.taker), o.providerIds[i]);
else IERC1155Strip(mkt.base.addr).safeTransferFrom(address(this), o.recipient.setOrDef(o.taker), o.providerIds[i], 1, "");
return mkt.base;
}
structIntSlot {
uint256 remaining;
uint256 protoFeeRate;
uint256 mktCreatorFeeRate;
uint256 protoFee;
uint256 mktCreatorFee;
}
/// @dev Release asset for SYNTH/SYNTH & SYNTH/NFT/// @param s1 The State object/// @param s2 The State2 object/// @param o The order object/// @param mkt The market objectfunctionreleaseForSynthToSynthOrNFT(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
Structs.Order memory o,
Structs.Market memory mkt
) internalreturns (Structs.Asset memory asset) {
Structs.Liquidity storage liq = s1.liquidity[o.marketId][o.provider][o.lid];
// Compute fee
FeeInfo memory feeInfo = FeeLib.computeFee(
ComputeFeeParams(
mkt,
liq,
s2.assetReg,
s2.feePurse,
s1.ints.synthSwapFeeBp,
s1.ints.instantNFTSwapFeeBp,
s1.ints.instantSwapFeeBp,
s1.ints.stableSwapFeeBp,
s2.ints.nftDiscountBp,
o.lid,
o.amount,
o.price,
s2.nft,
o.provider,
o.taker
)
);
// Unlock fee in purse
s2.feePurse.unlock(o.provider, liq.protoFeePurseId);
s2.feePurse.unlock(o.provider, liq.mktCreatorFeePurseId);
// Transfer market creator order fee to market creator
s2.feePurse.transferFrom(o.provider, mkt.creator, feeInfo.mktCreatorFee);
uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];
// Disburse fee to sharable pool if asset is shareable
BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, asset.addr), s2.operatorMgr.isOperator(o.operator));
if (bools.sharable) {
s2.feePurse.transferFrom(o.provider, address(this), (feeInfo.protoFee * s1.feeBPs[1]) /10000);
totalFeeBps -= s1.feeBPs[1];
}
// Disburse fee to operator if operator is setif (bools.isOperator) {
address opAddr = s2.operatorMgr.getOperator(o.operator);
s2.feePurse.transferFrom(o.provider, opAddr, (feeInfo.protoFee * s1.feeBPs[2]) /10000);
totalFeeBps -= s1.feeBPs[2];
}
// Disburse fee to inventor
s2.feePurse.transferFrom(o.provider, s1.inventor, (feeInfo.protoFee * totalFeeBps) /10000);
// Lock fee for the remaining liquidity
liq.protoFeePurseId = s2.feePurse.lock(o.provider, (feeInfo.protoFeeRate * feeInfo.remaining) /10**18);
liq.mktCreatorFeePurseId = s2.feePurse.lock(o.provider, (feeInfo.mktCreatorFeeRate * feeInfo.remaining) /10**18);
return mkt.base;
}
functionreleaseForFungibles(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
Structs.Order storage o,
Structs.Market memory mkt
) internalreturns (Structs.Asset memory asset) {
asset = mkt.base.isOnchain() ? mkt.base : mkt.quote;
// Compute fee
Structs.Liquidity memory liq = s1.liquidity[o.marketId][o.provider][o.lid];
FeeInfo memory feeInfo = FeeLib.computeFee(
ComputeFeeParams(
mkt,
liq,
s2.assetReg,
s2.feePurse,
s1.ints.synthSwapFeeBp,
s1.ints.instantNFTSwapFeeBp,
s1.ints.instantSwapFeeBp,
s1.ints.stableSwapFeeBp,
s2.ints.nftDiscountBp,
o.lid,
o.amount,
o.price,
s2.nft,
o.provider,
o.taker
)
);
uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];
// Disburse fees to market creator
s1.feeBalance[mkt.creator][asset.addr] += feeInfo.mktCreatorFee;
// Disburse fee to sharable pool if asset is shareable
BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, asset.addr), s2.operatorMgr.isOperator(o.operator));
if (bools.sharable) {
s1.shareablesBalance[asset.addr] += (feeInfo.protoFee * s1.feeBPs[1]) /10000;
totalFeeBps -= s1.feeBPs[1];
}
// Disburse fee to operator if operator is setif (bools.isOperator) {
s1.operatorsBalance[s2.operatorMgr.getOperator(o.operator)][asset.addr] += (feeInfo.protoFee * s1.feeBPs[2]) /10000;
totalFeeBps -= s1.feeBPs[2];
}
// Disburse fee to inventor
s1.inventorBalance[asset.addr] += (feeInfo.protoFee * totalFeeBps) /10000;
// Transfer the order amountaddress receiver = (!mkt.base.isSynth()) ? o.taker : o.provider;
if (asset.isERC20()) {
IERC20(asset.addr).transfer(o.recipient.setOrDef(receiver), feeInfo.amtToCharge - feeInfo.totalFees);
} elseif (asset.isNative()) {
payable(o.recipient.setOrDef(receiver)).transfer(feeInfo.amtToCharge - feeInfo.totalFees);
}
return asset;
}
/// @notice Verify a market manager's signature/// @param signer The market manager's address/// @param id The order ID/// @param action Indicates the manager action (0: paid, 1: release, 2: cancel)/// @param expireAt Indicates the timestamp when the signature is to expire./// @param signature The signature created by the managerfunctionverifyMarketManagerSig(address signer,
uint256 id,
uint action,
uint256 expireAt,
bytesmemory signature
) internalviewreturns (bool) {
if (address(0) == signer) returnfalse;
bytes32 messageHash =keccak256(abi.encode(block.chainid, id, action, expireAt));
bytes32 ethSignedMessageHash = SigLib.getSignedMessageHash(messageHash);
return SigLib.recoverSigner(ethSignedMessageHash, signature) == signer;
}
}
Contract Source Code
File 34 of 44: Ownable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)pragmasolidity ^0.8.13;import"@openzeppelin/contracts/utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/contractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
require(owner() == _msgSender(), "OWNABLE: NOT_OWNER");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "INVALID_ADDRESS");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Contract Source Code
File 35 of 44: SafeERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)pragmasolidity ^0.8.4;import"../token/interfaces/IERC20.sol";
import"@openzeppelin/contracts/utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/librarySafeERC20{
usingAddressforaddress;
functionsafeTransfer(IERC20 token, address to, uint256 value) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
functionsafeTransferFrom(IERC20 token, addressfrom, address to, uint256 value) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
functionsafeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal{
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/function_callOptionalReturn(IERC20 token, bytesmemory data) private{
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that// the target address contains contract code and also asserts for success in the low-level call.bytesmemory returndata =address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length>0) {
// Return data is optionalrequire(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;libraryStructs{
enumAssetType {
SYNTH,
ERC20,
ERC721,
ERC1155,
NATIVE
}
structAsset {
address addr;
AssetType assetType;
}
structMarketDur {
// Time to wait before a dispute can be starteduint256 disputeDelayDur;
}
// Market represents a pair of assets where one// asset (base) is swapped for the other (synthetic).structMarket {
// The timestamp when it was created.uint256 createdAt;
// The number of orders created.uint256 numOrders;
// The number of disputes created.uint256 numDisputes;
// The number of liquidityuint256 numLiquidity;
// The percentage or fee rate to charge as commission for each trade.// Example:// - For non-synthetic pairs: 1.55% should be represented as 155 (1.55 * 100).// - For synthetic/nft pairs: 1 wei per 1 unit of liquidityuint256 commission;
// The id of the pool that that should mediate for this marketuint256 poolId;
// Duration params
MarketDur dur;
// The creator of the market.address creator;
// The market manageraddress manager;
// The base asset of the market.// This can be any ERC20 token.
Asset base;
// The quote asset of the market.// This can be any ERC20 token.
Asset quote;
// Market permissions
MarketPerms perms;
}
structMarketPerms {
// Indicate whether the market is permissioned for base liquidity provisioning.// When set to true, only specific addresses will be// allowed to add base liquidity.bool put;
// Indicate whether the market is permissioned for swaps.// When set to true, only specific addresses will be allowed to add base liquidity.bool swap;
// Indicates whether the market can receive only offers.bool offerOnly;
// Indicates whether to allow zero pricebool allowZeroOfferPrice;
}
/// @dev Liquidity represents an amount of an assetstructLiquidity {
// The ids of assets (for ERC721 and ERC1155 asset types)uint256[] ids;
// The amount of asset (for ERC20 and SYNTH asset types)uint256 amount;
// The price to sell 1 unit of liquidity for the quote asset.uint256 price;
// The minimum amount that can be swapped.uint256 minSwap;
/// The maximum amount that can be swapped.uint256 maxSwap;
// The number of seconds payment is expected to be made before grace perioduint256 timeToPay;
// The number of open ordersuint256 openOrders;
// The number of open disputesuint256 openDisputes;
// Purse ID of locked fee for protocoluint256 protoFeePurseId;
// Purse ID of locked fee for market creatoruint256 mktCreatorFeePurseId;
// Fee rate for protocol feeuint256 protoFeeRate;
// Fee rate for market creator feeuint256 mktCreatorFeeRate;
// At least one of the ID must matchuint256[] wantOrIds;
// All IDs must matchuint256[] wantAndIds;
// Indicates whether new trades can be createdbool paused;
// Indicate the address that is permitted to take the liquidity.// Used when the liquidity provider wants to limit who can take the liquidity.address target;
}
/// @dev Order represents an on-going swap order.structOrder {
// The ID of the market where the order belongs to.address marketId;
// The address of the liquidity provideraddress provider;
// The address of the liquidity takeraddress taker;
// The recipient of the ordered asset (optional)address recipient;
// The releaser or cancelleraddress finalizer;
// The liquidity IDuint256 lid;
// The swap amount.uint256 amount;
// The swap price.uint256 price;
// The start time.uint256 createdAt;
// The time before which payment is expected.uint256 payDeadline;
// The time an un-paid order can be cancelled by both parties.uint256 cancelAt;
// The time when a dispute can be started.uint256 disputeFrom;
// The ID of the offer that executed the swapuint256 offerId;
// The interface operatoruint256 operator;
// Taker token IDs locked in order. Filled when quote is NFTuint256[] takerIds;
// Provider token IDs locked in order. Filled when base is NFTuint256[] providerIds;
// Indicates whether the off-chain asset has been transferred or paid.bool paid;
// Indicates whether the locked asset has been released.bool released;
// Indicates whether the order is disputed.bool disputed;
// Indicates whether the order has been cancelledbool cancelled;
}
structOffer {
// creator of the offeraddress creator;
// marketId is the unique ID of the market.address marketId;
// provider is the address of the base asset liquidity provider.address provider;
// the actual recipientaddress recipient;
// liquidity IDuint256 lid;
// amount is the quantity of the base asset.// Applies when quote asset is fungible.uint256 amount;
// price is the price to pay for the offer amount.// Applies when base and quote asset are fungible.uint256 price;
// ids is a list of token ids to be offered.// Applies when quote is non-fungible.uint256[] ids;
// The liquidity associated to the offer.uint256 liq;
// deadline is the time the offer expiresuint256 deadline;
// The time when the offer can be executeduint256 execAt;
// The ID of the interface operatoruint256 operator;
// Indicate whether the offer has been acceptedbool accepted;
// Indicate whether the offer has been cancelledbool cancelled;
// Indicate whether the offer has been executedbool executed;
}
structMediator {
address addr;
uint256 bond;
uint256 bondReleaseAt;
uint256 matureAt;
}
structTicketStatus {
bool drafted;
bool joined;
bool blocked;
bool slashed;
bool paused;
}
structTicket {
uint256 idx;
uint256 poolId;
uint256 bond;
uint256 matureAt;
int256 poolIdx;
uint256 cancelAt;
uint256 joinBy;
uint256 numPaused;
address owner;
TicketStatus status;
}
structTicketPool {
bool exists;
bool approvedMediatorOnly;
bool approvedMarketOnly;
uint256 reqBond;
uint256 draftReward;
uint256 winReward;
uint256 noVoteSlashBP;
uint256 loseVoteSlashBP;
uint256 numDraftees;
uint256 evidenceDur;
uint256 predictDur;
}
structDelegateTicket {
address owner;
uint256 id;
}
structPoolTicket {
address owner;
uint256 idx;
}
structPurchaseWindow {
uint256 endTime;
uint256 numPurchases;
uint256 supply;
uint256 price;
}
}
Contract Source Code
File 40 of 44: StructsLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"../common/Structs.sol";
import"../assetregistry/interfaces/IAssetRegistry.sol";
import"../token/interfaces/IERC20.sol";
import"../market/interfaces/IMarketCore.sol";
import"../pools/interfaces/IFeePool.sol";
import"../feepurse/interfaces/IFeePurse.sol";
import"../badge/interfaces/IBadge.sol";
import"../operator/interfaces/IOperator.sol";
import"../minerregisty/interfaces/IMinerRegistry.sol";
libraryStructsLib{
structUserRewardLog {
uint256 phase;
uint256 period;
}
structIntSlotState {
// Protocol fee for erc20 -> erc20 swapuint256 instantSwapFeeBp;
// Protocol fee for erc20 -> synthetic swapuint256 synthSwapFeeBp;
// Protocol fee for stable coin assetsuint256 stableSwapFeeBp;
// Fee paid in NFT/ERC20 swapuint256 instantNFTSwapFeeBp;
// Fee paid when adding liquidity to a non-ERC20 market with a synthetic baseuint256 fixedSynthSwapFee;
// Fee paid when adding liquidity to a non-ERC20 market with an NFT baseuint256 fixedNFTSwapFee;
// Maximum open orders a liquidity provider can have openuint256 maxOpenOrders;
// Maximum open disputes a liquidity provider can have openuint256 maxOpenDisputes;
}
structState {
IntSlotState ints;
// protocol fee split base points// 0: inventor, 1: shared, 2: operatoruint256[3] feeBPs;
// The minimum number of seconds synthetic payments must be made within.uint256 minTimeToPay;
// The number of seconds after order pay deadline allowed to pass before// an order can be cancelled.uint256 orderGraceDur;
// The number of orders created.uint256 numOrders;
// Number of seconds betweenuint256 feePoolTxInterval;
// The protocol inventor.address inventor;
// The AMM swap router addressaddress ammSwapRouter;
// The list of orders.
Structs.Order[] orders;
// Temporary list of reward log to be skipped
UserRewardLog[] skipped;
// Mapping of address to fees received from assets// user => asset => balancemapping(address=>mapping(address=>uint256)) feeBalance;
// Index of sharable assets and their balancemapping(address=>uint256) shareablesBalance;
// Mapping of protocol fee balance// asset => balancemapping(address=>uint256) inventorBalance;
// Store protocol fee earned by operators// operator => asset => balancemapping(address=>mapping(address=>uint256)) operatorsBalance;
// Index of address and the amount of liquidity provided to markets.mapping(address=>mapping(address=> Structs.Liquidity[])) liquidity;
// Maps managers of liquidity// Maps liquidity owner => market => manager Addressmapping(address=>mapping(address=>address)) liqManagers;
// Maps address to the amount of reward earnedmapping(address=>uint256) rewards;
// Maps asset address to the timestamp of their last success transfer to FeePoolmapping(address=>uint256) lastFeePoolTransfer;
}
structUintSlotState2 {
// Base point representing NFT fee discountuint256 nftDiscountBp;
// The fee tier of ETH pool on the native AMM DEXuint256 ammEthPoolFee;
}
structState2 {
UintSlotState2 ints;
// Dispute manager contract addressaddress disputeManager;
// NFT contract addressaddress nft;
// ETH ERC20 token on native AMMaddress ammEth;
// Registry for asset information
IAssetRegistry assetReg;
// App token contract
IERC20 token;
// Market core contract
IMarketCore marketCore;
// Fee pool contract
IFeePool feePool;
// FeePurse contract
IFeePurse feePurse;
// Badge contract
IBadge badge;
// Operator manager contract
IOperator operatorMgr;
// Miner registry contract
IMinerRegistry minerReg;
// List of offers
Structs.Offer[] offers;
// Liquidity-level badges required of swappers.// market => provider => liquidity ID => [badgeID ... badgeIDmapping(address=>mapping(address=>mapping(uint256=>string[2][]))) reqSwapBadges;
// AMM pools where an asset can be swapped// input address => output (address, pool fee)mapping(address=> AmmSwapPoolInfo) ammSwapPool;
// Used to initialize an uint256 mapping.mapping(uint256=>bool) intIdx;
}
structAmmSwapPoolInfo {
address outAddr;
uint256 poolFee;
}
structSwapSlotAddresses {
// marketId is the unique ID of the market.address marketId;
// provider is the address of the base asset liquidity provider.address provider;
// the recipient of the output assetaddress recipient;
}
structSwapSlot {
// liquidity IDuint256 lid;
// amount is the quantity of base asset to be swapped.// Applies when quote asset is fungibleuint256 amount;
// maxPrice is the maximum price to pay for amount.// Applies when quote asset is fungibleuint256 maxPrice;
// ids is a list of non-fungible asset ids to be swapped for the base liquidityuint256[] ids;
// Indicate that a swap was initiated by an offer with the given IDuint256 offerId;
// The ID of the interface operatoruint256 operator;
// Slot for variables with address type
SwapSlotAddresses addrs;
}
structUpdateLiquiditySlot {
address marketId;
uint256 lid;
bool deduct;
bool pause;
uint256 amount;
uint256[] ids;
uint256[] wantOrIds;
uint256[] wantAndIds;
uint256 price;
uint256 minSwap;
uint256 maxSwap;
uint256 timeToPay;
address target;
}
}
Contract Source Code
File 41 of 44: SwapFungibleLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"../common/Structs.sol";
import"../market/interfaces/IMarketCore.sol";
import"../common/SafeERC20.sol";
import"./AuxLib.sol";
import"./StructsLib.sol";
import"../market/MarketAuxLib.sol";
import"./OrderLib.sol";
import {IERC1155Strip} from"../common/StrippedInterfaces.sol";
import"./LiquidityLib.sol";
import"./FeeLib.sol";
librarySwapFungibleLib{
usingSafeERC20forIERC20;
usingAuxLibforuint256[];
usingAuxLibforaddress;
usingMarketAuxLibforStructs.Asset;
eventSwap(addressindexed mid,
addressindexed provider,
addressindexed swapper,
address recipient,
address releaser,
uint256[6] numTypeInfo,
bool byDispute,
uint256[] idsFromProvider,
uint256[] idsFromTaker
);
/// @dev Swap quote asset for a fungible base asset./// Covers:/// ERC20/ERC20, ERC20/SYNTH, ERC20/NATIVE, ERC20/NFT, SYNTH/ERC20,/// SYNTH/NATIVE, SYNTH/SYNTH, SYNTH/NFT,/// NATIVE/ERC20, NATIVE/SYNTH, NATIVE/NATIVE, NATIVE/NFT/// @param state Storage state./// @param state2 Storage state 2./// @param sp The parameters needed to swap./// @param mkt The market object for the market being swapped on.functionswap(
StructsLib.State storage state,
StructsLib.State2 storage state2,
StructsLib.SwapSlot memory sp,
Structs.Market memory mkt
) public{
require(AuxLib.hasNonZeroLiquidity(state, sp.addrs.marketId, sp.addrs.provider, sp.lid), "OC: ZERO_LIQ");
Structs.Liquidity storage liq = state.liquidity[sp.addrs.marketId][sp.addrs.provider][sp.lid];
require(liq.target.isZero() || liq.target ==msg.sender, "OC: NOT_TARGET");
AuxLib.checkFungibleSwapParams(sp, mkt, liq);
require(sp.operator ==0|| state2.operatorMgr.isOperator(sp.operator), "OC: UNKNOWN_OPERATOR");
// Handle ERC20/ERC20, ERC20/NATIVE, NATIVE/ERC20, NATIVE/NATIVEif (
(mkt.base.isERC20() && (mkt.quote.isERC20() || mkt.quote.isNative())) ||
(mkt.base.isNative() && (mkt.quote.isERC20() || mkt.quote.isNative()))
) {
return swapERC20OrNativeForERC20OrNative(state, state2, sp, mkt, liq);
}
// Handle ERC20/NFT, NATIVE/NFTif ((mkt.base.isERC20() || mkt.base.isNative()) && mkt.quote.isNFT()) {
return swapNFTForERC20OrNative(state, state2, sp, mkt, liq);
}
// Create order for ERC20/SYNTH, SYNTH/ERC20, SYNTH/SYNTH, SYNTH/NFT, NATIVE/SYNTH, SYNTH/NATIVE
OrderLib.createOrder(state, state2, sp, mkt, liq);
}
structBoolSlot {
bool sharable;
bool isOperator;
}
/// @dev Handle ERC20/ERC20, ERC20/NATIVE, NATIVE/NATIVE, NATIVE/ERC20 swap (Instant Swap)/// @param s1 Storage state./// @param s2 Storage state 2./// @param sp The parameters needed to swap./// @param mkt The market object for the market being swapped on./// @param liq The liquidity object for the liquidity being swapped on.functionswapERC20OrNativeForERC20OrNative(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
StructsLib.SwapSlot memory sp,
Structs.Market memory mkt,
Structs.Liquidity storage liq
) public{
// If swap was initiated via an offer, use price from offer instead of liquidityuint256 price = (sp.offerId >0) ? sp.maxPrice : liq.price;
// Compute cost of swapping for base. Ensure swapper's quote balance is sufficient.// uint baseDecimals = (mkt.base.isERC20()) ? IERC20(mkt.base.addr).decimals() : 18;uint256 cost = (sp.amount * price) /10** ((mkt.base.isERC20()) ? IERC20(mkt.base.addr).decimals() : 18);
require(!mkt.quote.isERC20() || IERC20(mkt.quote.addr).balanceOf(msg.sender) >= cost, "OC: LOW_QUOTE_BAL");
require(!mkt.quote.isNative() ||msg.value>= cost, "OC: LOW_QUOTE_BAL");
// Compute fee
FeeInfo memory feeInfo = FeeLib.computeFee(
ComputeFeeParams(
mkt,
liq,
s2.assetReg,
s2.feePurse,
s1.ints.synthSwapFeeBp,
s1.ints.instantNFTSwapFeeBp,
s1.ints.instantSwapFeeBp,
s1.ints.stableSwapFeeBp,
s2.ints.nftDiscountBp,
sp.lid,
sp.amount,
price,
s2.nft,
sp.addrs.provider,
msg.sender
)
);
uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];
// Disburse fee to market creator
s1.feeBalance[mkt.creator][mkt.base.addr] += feeInfo.mktCreatorFee;
BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, mkt.base.addr), s2.operatorMgr.isOperator(sp.operator));
// Disburse fee to sharable pool if asset is sharableif (bools.sharable) {
s1.shareablesBalance[mkt.base.addr] += (feeInfo.protoFee * s1.feeBPs[1]) /10000;
totalFeeBps -= s1.feeBPs[1];
}
// Disburse fee to operator if operator is setif (bools.isOperator) {
s1.operatorsBalance[s2.operatorMgr.getOperator(sp.operator)][mkt.base.addr] += (feeInfo.protoFee * s1.feeBPs[2]) /10000;
totalFeeBps -= s1.feeBPs[2];
}
// Disburse fee to inventor
s1.inventorBalance[mkt.base.addr] += (feeInfo.protoFee * totalFeeBps) /10000;
// Update liquidity
liq.amount -= sp.amount;
s2.marketCore.setCounters(sp.addrs.marketId, false, false, true, mkt.numLiquidity - sp.amount);
// Transfer quote asset (swap cost) from swapper to liquidity provider.if (mkt.quote.isERC20()) {
IERC20(mkt.quote.addr).safeTransferFrom(msg.sender, sp.addrs.provider, cost);
} elseif (mkt.quote.isNative()) {
payable(sp.addrs.provider).transfer(cost);
}
// Transfer base asset (swap amount) to recipientif (mkt.base.isERC20()) {
IERC20(mkt.base.addr).safeTransfer(sp.addrs.recipient.setOrDef(msg.sender), sp.amount - feeInfo.totalFees);
} elseif (mkt.base.isNative()) {
payable(sp.addrs.recipient.setOrDef(msg.sender)).transfer(sp.amount - feeInfo.totalFees);
}
emit Swap(
sp.addrs.marketId,
sp.addrs.provider,
msg.sender,
sp.addrs.recipient,
address(0),
[0, sp.lid, sp.amount, price, sp.offerId, sp.operator],
false,
newuint256[](0),
newuint256[](0)
);
}
/// @dev Handle ERC20/NFT, NATIVE/NFT swap (Instant Swap)/// @param s1 Storage state./// @param s2 Storage state 2./// @param sp The parameters needed to swap./// @param mkt The market object for the market being swapped on./// @param liq The liquidity object for the liquidity being swapped on.functionswapNFTForERC20OrNative(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
StructsLib.SwapSlot memory sp,
Structs.Market memory mkt,
Structs.Liquidity storage liq
) public{
// Find the IDs that match the wants request.// If swap was initiated via offer, use offer supplied ids.uint256[] memory matchIds = (sp.offerId >0)
? sp.ids
: LiquidityLib.findMatchInWantIds(sp.ids, s2.intIdx, liq.wantOrIds, liq.wantAndIds);
require(matchIds.length>0, "OC: NO_ID_MATCH");
// Compute fee
FeeInfo memory feeInfo = FeeLib.computeFee(
ComputeFeeParams(
mkt,
liq,
s2.assetReg,
s2.feePurse,
s1.ints.synthSwapFeeBp,
s1.ints.instantNFTSwapFeeBp,
s1.ints.instantSwapFeeBp,
s1.ints.stableSwapFeeBp,
s2.ints.nftDiscountBp,
sp.lid,
sp.amount,
0,
s2.nft,
sp.addrs.provider,
msg.sender
)
);
uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];
// Disburse fee to market creator
s1.feeBalance[mkt.creator][mkt.base.addr] += feeInfo.mktCreatorFee;
BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, mkt.base.addr), s2.operatorMgr.isOperator(sp.operator));
// Disburse fee to sharable pool if asset is sharableif (bools.sharable) {
s1.shareablesBalance[mkt.base.addr] += (feeInfo.protoFee * s1.feeBPs[1]) /10000;
totalFeeBps -= s1.feeBPs[1];
}
// Disburse fee to operator if operator is setif (bools.isOperator) {
s1.operatorsBalance[s2.operatorMgr.getOperator(sp.operator)][mkt.base.addr] += (feeInfo.protoFee * s1.feeBPs[2]) /10000;
totalFeeBps -= s1.feeBPs[2];
}
// Disburse fee to inventor
s1.inventorBalance[mkt.base.addr] += (feeInfo.protoFee * totalFeeBps) /10000;
// Transfer base asset to recipientif (mkt.base.isERC20()) {
IERC20(mkt.base.addr).safeTransfer(sp.addrs.recipient.setOrDef(msg.sender), liq.amount - feeInfo.totalFees);
} elseif (mkt.base.isNative()) {
payable(sp.addrs.recipient.setOrDef(msg.sender)).transfer(liq.amount - feeInfo.totalFees);
}
// Transfer quote asset to liquidity providerfor (uint256 i =0; i < matchIds.length; i++) {
if (matchIds[i] ==0) continue;
if (mkt.quote.isERC721()) IERC721Strip(mkt.quote.addr).transferFrom(msg.sender, sp.addrs.provider, matchIds[i]);
else IERC1155Strip(mkt.quote.addr).safeTransferFrom(msg.sender, sp.addrs.provider, matchIds[i], 1, "");
}
// Update liquidity
s2.marketCore.setCounters(sp.addrs.marketId, false, false, true, mkt.numLiquidity - liq.amount);
liq.amount =0;
emit Swap(
sp.addrs.marketId,
sp.addrs.provider,
msg.sender,
sp.addrs.recipient,
address(0),
[0, sp.lid, sp.amount, liq.price, sp.offerId, sp.operator],
false,
newuint256[](0),
matchIds
);
}
}
Contract Source Code
File 42 of 44: SwapLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"../common/Structs.sol";
import"../market/interfaces/IMarketCore.sol";
import"../common/SafeERC20.sol";
import"./AuxLib.sol";
import"./StructsLib.sol";
import"../market/MarketAuxLib.sol";
import"../common/StrippedInterfaces.sol";
import"./OrderLib.sol";
import"./OrderReleaseLib.sol";
import"./SwapFungibleLib.sol";
import"./SwapNonFungibleLib.sol";
librarySwapLib{
usingMarketAuxLibforStructs.Asset;
/// @dev Swap quote asset for the base asset./// @param state The State object/// @param state2 The State2 object/// @param sp The swap parametersfunctionswapQuoteForBase(StructsLib.State storage state, StructsLib.State2 storage state2, StructsLib.SwapSlot memory sp) public{
require(state2.marketCore.isPutPermitted(sp.addrs.marketId, sp.addrs.provider));
require(state2.marketCore.isSwapPermitted(sp.addrs.marketId, msg.sender));
Structs.Market memory market = state2.marketCore.getMarket(sp.addrs.marketId);
require(!market.perms.offerOnly, "OC: OFFER_ONLY");
checkMarketSwapBadges(state2, sp.addrs.marketId);
checkProviderSwapBadges(state2, sp.addrs.marketId, sp.lid, sp.addrs.provider);
if (market.base.isFungible()) SwapFungibleLib.swap(state, state2, sp, market);
else SwapNonFungibleLib.swap(state, state2, sp, market);
}
/// @dev Create order for ERC20/SYNTH, SYNTH/ERC20, SYNTH/SYNTH & SYNTH/NFT/// @param state The State object/// @param state2 The State2 object/// @param sp The swap parameters/// @param market The market object/// @param liq The liquidity objectfunctioncreateOrder(
StructsLib.State storage state,
StructsLib.State2 storage state2,
StructsLib.SwapSlot memory sp,
Structs.Market memory market,
Structs.Liquidity storage liq
) public{
OrderLib.createOrder(state, state2, sp, market, liq);
}
/// @dev Release ERC20 asset./// @param state The State object/// @param state2 The State2 object/// @param owner The contract owner/// @param oid The order ID./// @param byDispute Indicates that the method is called by the dispute manager.functionreleaseOrder(
StructsLib.State storage state,
StructsLib.State2 storage state2,
address owner,
uint256 oid,
bool byDispute
) public{
OrderReleaseLib.releaseOrder(state, state2, owner, oid, byDispute, false);
}
/// @notice Release an order using a market manager's signature/// @param state The State object/// @param state2 The State2 object/// @param owner The contract owner/// @param oid The order ID/// @param expireAt The expire time of the signature/// @param signature The signature created by the managerfunctionreleaseOrderByManagerSig(
StructsLib.State storage state,
StructsLib.State2 storage state2,
address owner,
uint256 oid,
uint256 expireAt,
bytescalldata signature
) public{
OrderReleaseLib.releaseOrderByManagerSig(state, state2, owner, oid, expireAt, signature);
}
/// @dev Cancel an inactive order. An inactive order is one where:/// The order has not been marked as paid, released or disputed/// and has reached its cancel time./// @param state State object/// @param state2 State2 object/// @param owner The contract owner/// @param id is the unique order ID/// @param byDispute Indicates that the method is called by the dispute manager.functioncancelOrder(
StructsLib.State storage state,
StructsLib.State2 storage state2,
address owner,
uint256 id,
bool byDispute
) public{
OrderLib.cancelOrder(state, state2, owner, id, byDispute, false);
}
/// @notice Cancel an order using a market manager's signature/// @param state The State object/// @param state2 The State2 object/// @param owner The contract owner/// @param oid The order ID/// @param expireAt The expire time of the signature/// @param signature The signature created by the managerfunctioncancelOrderByManagerSig(
StructsLib.State storage state,
StructsLib.State2 storage state2,
address owner,
uint256 oid,
uint256 expireAt,
bytescalldata signature
) external{
OrderLib.cancelOrderByManagerSig(state, state2, owner, oid, expireAt, signature);
}
/// @dev Mark a swap order as paid./// Order must not have been released or cancelled./// When the base asset is on-chain (ERC20 or NFT), the sender must be the taker./// When the base asset is synthetic, the sender must be the provider./// @param state State object/// @param marketCore The MarketCore contract address/// @param id The order ID.functionmarkOrderAsPaid(StructsLib.State storage state, IMarketCore marketCore, uint256 id, bool byManager) public{
OrderLib.markOrderAsPaid(state, marketCore, id, byManager);
}
/// @notice Mark an order as paid using a market manager's signature/// @param state The State object/// @param state2 The State2 object/// @param oid The order ID/// @param expireAt The expire time of the signature/// @param signature The signature created by the managerfunctionmarkOrderAsPaidByManagerSig(
StructsLib.State storage state,
StructsLib.State2 storage state2,
uint256 oid,
uint256 expireAt,
bytescalldata signature
) external{
OrderLib.markOrderAsPaidByManagerSig(state, state2, oid, expireAt, signature);
}
/// @dev Marks an order as `disputed`/// @param state State object/// @param marketCore The MarketCore contract address/// @param id The order ID or index.functionmarkAsDisputed(StructsLib.State storage state, IMarketCore marketCore, uint256 id) public{
OrderLib.markAsDisputed(state, marketCore, id);
}
/// @dev Check if sender has the required swap badges set by the market creatorfunctioncheckMarketSwapBadges(StructsLib.State2 storage s2, address marketId) internalview{
(, string[2][] memory reqSwapBadges) = s2.marketCore.getReqBadges(marketId);
if (reqSwapBadges.length>0) {
uint256 len = reqSwapBadges.length;
for (uint256 i =0; i < len; i++) {
require(s2.badge.hasBadge(msg.sender, reqSwapBadges[i][0], reqSwapBadges[i][1]), "OC: MKT_BADGE_REQ");
}
}
}
/// @dev Check if sender has the required swap badges set by the liquidity provider/// @param s2 A state object/// @param marketId The market ID/// @param lid The liquidity ID/// @param provider The provider addressfunctioncheckProviderSwapBadges(StructsLib.State2 storage s2, address marketId, uint256 lid, address provider) internalview{
string[2][] memory reqSwapBadges = s2.reqSwapBadges[marketId][provider][lid];
if (reqSwapBadges.length>0) {
uint256 len = reqSwapBadges.length;
for (uint256 i =0; i < len; i++) {
require(s2.badge.hasBadge(msg.sender, reqSwapBadges[i][0], reqSwapBadges[i][1]), "OC: LP_BADGE_REQ");
}
}
}
}
Contract Source Code
File 43 of 44: SwapNonFungibleLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"../common/Structs.sol";
import"../market/interfaces/IMarketCore.sol";
import"../common/SafeERC20.sol";
import"./AuxLib.sol";
import"./StructsLib.sol";
import"../market/MarketAuxLib.sol";
import"./OrderLib.sol";
import {IERC1155Strip} from"../common/StrippedInterfaces.sol";
import"./FeeLib.sol";
librarySwapNonFungibleLib{
usingSafeERC20forIERC20;
usingMarketAuxLibforStructs.Asset;
usingAuxLibforaddress;
eventSwap(addressindexed mid,
addressindexed provider,
addressindexed swapper,
address recipient,
address releaser,
uint256[6] numTypeInfo,
bool byDispute,
uint256[] idsFromProvider,
uint256[] idsFromTaker
);
/// @dev Swap quote asset for a non-fungible base asset./// Covers NFT/ERC20, NFT/NATIVE, NFT/SYNTH, NFT/NFT/// @param s1 Storage state./// @param s2 Storage state 2./// @param sp The parameters needed to swap./// @param market The market object for the market being swapped on.functionswap(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
StructsLib.SwapSlot memory sp,
Structs.Market memory market
) public{
require(AuxLib.hasNonZeroLiquidity(s1, sp.addrs.marketId, sp.addrs.provider, sp.lid), "OC: ZERO_LIQ");
Structs.Liquidity storage liq = s1.liquidity[sp.addrs.marketId][sp.addrs.provider][sp.lid];
require(liq.target.isZero() || liq.target ==msg.sender, "OC: NOT_TARGET");
AuxLib.checkNonFungibleSwapParams(sp, market, liq);
require(sp.operator ==0|| s2.operatorMgr.isOperator(sp.operator), "OC: UNKNOWN_OPERATOR");
// If quote asset is ERC20 or NATIVEif (market.quote.isERC20() || market.quote.isNative()) {
return swapERC20OrNativeForNFT(s1, s2, sp, market, liq);
}
// If quote asset is NFTif (market.quote.isNFT()) {
return swapNFTForNFT(s1, s2, sp, market, liq);
}
// Create a swap order (NFT/SYNTH)
OrderLib.createOrder(s1, s2, sp, market, liq);
}
structBoolSlot {
bool sharable;
bool isOperator;
}
/// @dev Handle NFT/NFT swap (Instant)/// @param s1 Storage state./// @param s2 Storage state 2./// @param sp The parameters needed to swap./// @param mkt The market object for the market being swapped on./// @param liq The liquidity object.functionswapNFTForNFT(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
StructsLib.SwapSlot memory sp,
Structs.Market memory mkt,
Structs.Liquidity storage liq
) public{
address sdr =msg.sender;
// Find the IDs that match the wants request// If swap was initiated via offer, use offer supplied ids.uint256[] memory matchIds = (sp.offerId >0)
? sp.ids
: LiquidityLib.findMatchInWantIds(sp.ids, s2.intIdx, liq.wantOrIds, liq.wantAndIds);
require(matchIds.length>0, "OC: NO_ID_MATCH");
// Compute fee
FeeInfo memory feeInfo = FeeLib.computeFee(
ComputeFeeParams(
mkt,
liq,
s2.assetReg,
s2.feePurse,
s1.ints.synthSwapFeeBp,
s1.ints.instantNFTSwapFeeBp,
s1.ints.instantSwapFeeBp,
s1.ints.stableSwapFeeBp,
s2.ints.nftDiscountBp,
sp.lid,
sp.amount,
0,
s2.nft,
sp.addrs.provider,
msg.sender
)
);
// Unlock fee from fee purse
s2.feePurse.unlock(sp.addrs.provider, liq.protoFeePurseId);
s2.feePurse.unlock(sp.addrs.provider, liq.mktCreatorFeePurseId);
liq.protoFeePurseId =0;
liq.mktCreatorFeePurseId =0;
// Transfer fee to market creator
s2.feePurse.transferFrom(sp.addrs.provider, mkt.creator, feeInfo.mktCreatorFee);
uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];
BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, mkt.base.addr), s2.operatorMgr.isOperator(sp.operator));
// Disburse fee to sharable pool if asset is sharableif (bools.sharable) {
s2.feePurse.transferFrom(sp.addrs.provider, address(this), (feeInfo.protoFee * s1.feeBPs[1]) /10000);
totalFeeBps -= s1.feeBPs[1];
}
// Disburse fee to operator if operator is setif (bools.isOperator) {
address opAddr = s2.operatorMgr.getOperator(sp.operator);
s2.feePurse.transferFrom(sp.addrs.provider, opAddr, (feeInfo.protoFee * s1.feeBPs[2]) /10000);
totalFeeBps -= s1.feeBPs[2];
}
// Disburse fee to inventor
s2.feePurse.transferFrom(sp.addrs.provider, s1.inventor, (feeInfo.protoFee * totalFeeBps) /10000);
// Transfer quote asset to liquidity providerfor (uint256 i =0; i < matchIds.length; i++) {
if (matchIds[i] ==0) continue;
if (mkt.quote.isERC721()) IERC721Strip(mkt.quote.addr).transferFrom(sdr, sp.addrs.provider, matchIds[i]);
else IERC1155Strip(mkt.quote.addr).safeTransferFrom(sdr, sp.addrs.provider, matchIds[i], 1, "");
}
// Transfer the base asset to recipientfor (uint256 i =0; i < liq.ids.length; i++)
if (mkt.base.isERC721()) IERC721Strip(mkt.base.addr).transferFrom(address(this), sp.addrs.recipient.setOrDef(sdr), liq.ids[i]);
else IERC1155Strip(mkt.base.addr).safeTransferFrom(address(this), sp.addrs.recipient.setOrDef(sdr), liq.ids[i], 1, "");
s2.marketCore.setCounters(sp.addrs.marketId, false, false, true, mkt.numLiquidity - liq.ids.length);
emit Swap(
sp.addrs.marketId,
sp.addrs.provider,
msg.sender,
sp.addrs.recipient,
address(0),
[0, sp.lid, sp.amount, 0, sp.offerId, sp.operator],
false,
liq.ids,
matchIds
);
delete liq.ids;
}
/// @dev Handle NFT/ERC20, NFT/NATIVE swap (Instant)/// @param s1 Storage state./// @param s2 Storage state 2./// @param sp The parameters needed to swap./// @param mkt The market object for the market being swapped on./// @param liq The liquidity object.functionswapERC20OrNativeForNFT(
StructsLib.State storage s1,
StructsLib.State2 storage s2,
StructsLib.SwapSlot memory sp,
Structs.Market memory mkt,
Structs.Liquidity storage liq
) public{
uint256 price = (sp.offerId >0) ? sp.maxPrice : liq.price;
// Compute fee
FeeInfo memory feeInfo = FeeLib.computeFee(
ComputeFeeParams(
mkt,
liq,
s2.assetReg,
s2.feePurse,
s1.ints.synthSwapFeeBp,
s1.ints.instantNFTSwapFeeBp,
s1.ints.instantSwapFeeBp,
s1.ints.stableSwapFeeBp,
s2.ints.nftDiscountBp,
sp.lid,
sp.amount,
price,
s2.nft,
sp.addrs.provider,
msg.sender
)
);
// Dispurse fee to market creator
s1.feeBalance[mkt.creator][mkt.quote.addr] += feeInfo.mktCreatorFee;
BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, mkt.quote.addr), s2.operatorMgr.isOperator(sp.operator));
uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];
// Disburse fee to sharable pool if asset is sharableif (bools.sharable) {
s1.shareablesBalance[mkt.quote.addr] += (feeInfo.protoFee * s1.feeBPs[1]) /10000;
totalFeeBps -= s1.feeBPs[1];
}
// Disburse fee to operator if operator is setif (bools.isOperator) {
s1.operatorsBalance[s2.operatorMgr.getOperator(sp.operator)][mkt.quote.addr] += (feeInfo.protoFee * s1.feeBPs[2]) /10000;
totalFeeBps -= s1.feeBPs[2];
}
// Disburse fee to inventor
s1.inventorBalance[mkt.quote.addr] += (feeInfo.protoFee * totalFeeBps) /10000;
// Transfer quote asset to contract and to the liquidity providerif (mkt.quote.isERC20()) {
IERC20(mkt.quote.addr).safeTransferFrom(msg.sender, address(this), price);
IERC20(mkt.quote.addr).safeTransfer(sp.addrs.provider, price - feeInfo.totalFees);
} elseif (mkt.quote.isNative()) {
require(msg.value>= price, "OC: LOW_DEPOSIT");
payable(sp.addrs.provider).transfer(price - feeInfo.totalFees);
}
// Transfer base assets to recipientfor (uint256 i =0; i < liq.ids.length; i++)
if (mkt.base.isERC721())
IERC721Strip(mkt.base.addr).transferFrom(address(this), sp.addrs.recipient.setOrDef(msg.sender), liq.ids[i]);
else IERC1155Strip(mkt.base.addr).safeTransferFrom(address(this), sp.addrs.recipient.setOrDef(msg.sender), liq.ids[i], 1, "");
s2.marketCore.setCounters(sp.addrs.marketId, false, false, true, mkt.numLiquidity - liq.ids.length);
emit Swap(
sp.addrs.marketId,
sp.addrs.provider,
msg.sender,
sp.addrs.recipient,
address(0),
[0, sp.lid, sp.amount, price, sp.offerId, sp.operator],
false,
liq.ids,
newuint256[](0)
);
delete liq.ids;
}
}
Contract Source Code
File 44 of 44: WithdrawLib.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.13;import"./StructsLib.sol";
import"../token/interfaces/IERC20.sol";
import"../common/SafeERC20.sol";
import"../pools/interfaces/IFeePool.sol";
import"../feepurse/interfaces/IFeePurse.sol";
import"../market/interfaces/IMarketCore.sol";
import"@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
libraryWithdrawLib{
usingSafeERC20forIERC20;
eventTransferredToFeePool(addressindexed token, uint256 amount, address convAsset, uint256 convAmount);
eventTransferInventorBalance(address owner, address token, uint256 amount);
eventTransferOperatorBalance(address owner, address token, uint256 amount);
/// @dev Withdraw fees allocated to the protocol inventor/// @param state State object/// @param asset The address of the asset the fee is denominated in.functionwithdrawInventorBalance(StructsLib.State storage state, StructsLib.State2 storage state2, address asset) public{
uint256 bal = state.inventorBalance[asset];
state.inventorBalance[asset] =0;
if (IMarketCore(state2.marketCore).getNativeAssetAddr() == asset) {
payable(state.inventor).transfer(bal);
} else {
IERC20(asset).transfer(state.inventor, bal);
}
emit TransferInventorBalance(state.inventor, asset, bal);
bal = state2.feePurse.balanceOf(state.inventor);
if (bal >0) {
state2.feePurse.withdrawFrom(bal, state.inventor, state.inventor);
emit TransferInventorBalance(state.inventor, asset, bal);
}
}
/// @dev Withdraw fees allocated to operator (include fees in FeePurse)/// @param state State object/// @param state2 State object/// @param token The address of the token the fee is denominated in.functionwithdrawOperatorBalance(StructsLib.State storage state, StructsLib.State2 storage state2, address token) public{
uint256 bal = state.operatorsBalance[msg.sender][token];
state.operatorsBalance[msg.sender][token] =0;
if (IMarketCore(state2.marketCore).getNativeAssetAddr() == token) {
payable(msg.sender).transfer(bal);
} else {
IERC20(token).transfer(msg.sender, bal);
}
emit TransferOperatorBalance(msg.sender, token, bal);
bal = state2.feePurse.balanceOf(msg.sender);
if (bal >0) {
state2.feePurse.withdrawFrom(bal, msg.sender, msg.sender);
emit TransferOperatorBalance(msg.sender, token, bal);
}
}
/// @dev Transfer fees earned on shareable assets to the fee reward contract (include fees in FeePurse)/// @param state State object/// @param state2 State object/// @param token The address of a shareable tokenfunctiontransferToFeePool(StructsLib.State storage state, StructsLib.State2 storage state2, address token) public{
uint256 lastTx = state.lastFeePoolTransfer[token];
require(lastTx ==0||block.timestamp- lastTx > state.feePoolTxInterval, "OC: NOT_READY");
uint256 bal = state.shareablesBalance[token];
if (bal >0) {
// Clear the balance and set last transfer time
state.shareablesBalance[token] =0;
state.lastFeePoolTransfer[token] =block.timestamp;
// Convert balance to another asset (e.g. ETH, USDC)
ISwapRouter swapRouter = ISwapRouter(state.ammSwapRouter);
// Determine the output asset and pooladdress tokenOut = state2.ammEth;
uint256 poolFee = state2.ints.ammEthPoolFee;
if (state2.ammSwapPool[token].outAddr !=address(0)) {
tokenOut = state2.ammSwapPool[token].outAddr;
poolFee = state2.ammSwapPool[token].poolFee;
}
require(token != tokenOut, "OC: SAME_ASSET");
// Execute swap
IERC20(token).safeIncreaseAllowance(address(swapRouter), bal);
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
tokenIn: token,
tokenOut: tokenOut,
fee: uint24(poolFee),
recipient: address(state2.feePool),
deadline: block.timestamp,
amountIn: bal,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
uint256 amountOut;
if (token == IMarketCore(state2.marketCore).getNativeAssetAddr()) {
amountOut = swapRouter.exactInputSingle{value: bal}(params);
} else {
amountOut = swapRouter.exactInputSingle(params);
}
// Notify fee pool of new deposit
state2.feePool.notifyFeeDeposit(tokenOut, amountOut);
emit TransferredToFeePool(token, bal, tokenOut, amountOut);
}
// Transfer shareable fees held in feepurse to feepool
bal = state2.feePurse.balanceOf(address(this));
if (bal >0) {
state2.feePurse.withdraw(bal, address(state2.feePool));
state2.feePool.notifyFeeDeposit(address(state2.feePurse.getFeeToken()), bal);
emit TransferredToFeePool(address(state2.feePurse.getFeeToken()), bal, address(0), 0);
}
}
}