Overview
ETH Balance
0 ETH
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 25 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Execute Orders | 11580627 | 375 days ago | IN | 2 wei | 0.00170346 | ||||
Post Orders | 11580624 | 375 days ago | IN | 0 ETH | 0.00087819 | ||||
Execute Orders | 11580534 | 375 days ago | IN | 2 wei | 0.00219824 | ||||
Post Orders | 11580530 | 375 days ago | IN | 0 ETH | 0.00095931 | ||||
Execute Orders | 11567452 | 375 days ago | IN | 2 wei | 0.00227377 | ||||
Post Orders | 11567450 | 375 days ago | IN | 0 ETH | 0.00117304 | ||||
Execute Orders | 11567432 | 375 days ago | IN | 2 wei | 0.00274602 | ||||
Post Orders | 11567428 | 375 days ago | IN | 0 ETH | 0.00111511 | ||||
Execute Orders | 11490576 | 378 days ago | IN | 2 wei | 0.00201483 | ||||
Post Orders | 11490573 | 378 days ago | IN | 0 ETH | 0.00100311 | ||||
Cancel Order | 10989792 | 395 days ago | IN | 2 wei | 0.00023403 | ||||
Cancel Order | 10989792 | 395 days ago | IN | 2 wei | 0.00021528 | ||||
Post Orders | 10989791 | 395 days ago | IN | 0 ETH | 0.00135728 | ||||
Post Orders | 10989790 | 395 days ago | IN | 0 ETH | 0.00154538 | ||||
Execute Orders | 10985507 | 396 days ago | IN | 2 wei | 0.00133659 | ||||
Execute Orders | 10985474 | 396 days ago | IN | 2 wei | 0.0014335 | ||||
Execute Orders | 10908625 | 399 days ago | IN | 2 wei | 0.00444789 | ||||
Post Orders | 10908623 | 399 days ago | IN | 0 ETH | 0.00233275 | ||||
Execute Orders | 10874043 | 400 days ago | IN | 2 wei | 0.00488285 | ||||
Post Orders | 10874039 | 400 days ago | IN | 0 ETH | 0.00218349 | ||||
Execute Orders | 10874000 | 400 days ago | IN | 2 wei | 0.00539976 | ||||
Post Orders | 10873997 | 400 days ago | IN | 0 ETH | 0.00214189 | ||||
Cancel Order | 10873191 | 400 days ago | IN | 2 wei | 0.00159039 | ||||
Cancel Order | 10873163 | 400 days ago | IN | 2 wei | 0.00231945 | ||||
Post Orders | 10873000 | 400 days ago | IN | 0 ETH | 0.00249605 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
11580627 | 375 days ago | 2 wei | ||||
11580534 | 375 days ago | 2 wei | ||||
11567452 | 375 days ago | 2 wei | ||||
11567432 | 375 days ago | 2 wei | ||||
11490576 | 378 days ago | 2 wei | ||||
10985507 | 396 days ago | 2 wei | ||||
10985474 | 396 days ago | 2 wei | ||||
10908625 | 399 days ago | 2 wei | ||||
10874043 | 400 days ago | 2 wei | ||||
10874000 | 400 days ago | 2 wei | ||||
10873191 | 400 days ago | 2 wei | ||||
10873163 | 400 days ago | 2 wei | ||||
10862507 | 400 days ago | 2 wei | ||||
10862496 | 400 days ago | 2 wei | ||||
10849657 | 401 days ago | 2 wei | ||||
10849647 | 401 days ago | 2 wei | ||||
10844917 | 401 days ago | 2 wei | ||||
10833503 | 401 days ago | 2 wei | ||||
10831674 | 402 days ago | 2 wei | ||||
10831622 | 402 days ago | 2 wei | ||||
10829661 | 402 days ago | 2 wei | ||||
10829063 | 402 days ago | 2 wei | ||||
10829026 | 402 days ago | 2 wei | ||||
10829012 | 402 days ago | 2 wei | ||||
10829011 | 402 days ago | 2 wei |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x25111C14...94a1dD60F The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
LimitOrderBook
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 400 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "../interfaces/IPerpetualManager.sol"; import "../interfaces/IPerpetualOrder.sol"; import "../interfaces/IClientOrder.sol"; import "../../libraries/Bytes32Pagination.sol"; import "../../libraries/OrderFlags.sol"; import "../functions/PerpetualHashFunctions.sol"; /** * @title Limit/Stop Order Book Proxy Contract. * * @notice A new perpetual limit order book contract. * Each order is sent to the perpetual-specific limit order book contract instance. Executions can be started by * any participant but only go via instances of this contract. * Orders can be posted by anyone but they require valid signatures if not submitted by the trader. This allows brokers * to offer a gas-free execution of orders to their traders (the broker pays for gas). * Orders cannot be replayed. * The order submitted by the client is of type ClientOrder that contains a * possible parent/child link. The ClientOrder data is transformed into a Order struct which * strips off the parent/child link and adds the submitted-block number. * The submitted block number is relevant to avoid early execution (front-running prevention). * The parent/child link is important for orders that are conditional on other orders. * Parent child structure is as follows: * - relationship is reflected with the two order-digests parentChildDigest1 and parentChildDigest2 * - a child can only be executed if the linked parent: is executed/ is cancelled/ never existed * - a parent can be executed if other conditions (e.g. limit price) hold * - if a parent is cancelled due to some trade failure (e.g. Market Order hits slippage limit), children are cancelled too * - parents can still be cancelled by trader which does not imply the children are cancelled * * */ contract LimitOrderBook is IPerpetualOrder, IClientOrder { using Bytes32Pagination for bytes32[]; using Address for address; using PerpetualHashFunctions for Order; uint256 private constant MAX_ORDERS_PER_TRADER = 50; // Events event PerpetualLimitOrderCreated( uint24 indexed perpetualId, address indexed trader, address brokerAddr, Order order, bytes32 digest ); event ExecutionFailed( uint24 indexed perpetualId, address indexed trader, bytes32 digest, string reason ); // traders can add a callback address to their main order which will be called // on execution. event Callback(address callbackTarget, bool success, uint32 gasLimit); uint32 public callbackGasLimit; enum OrderStatus { CANCELED, EXECUTED, OPEN, UNKNOWN } struct OrderDependency { // Parents have either 2 non-zero entries, // or just the first !=0 // A child has parentChildEntry1=0 and // stores the single parent in entry2 bytes32 parentChildDigest1; bytes32 parentChildDigest2; } uint8 private immutable iCancelDelaySec; uint16 private immutable postingFeeTbps; // Stores perpetual id - specific to a perpetual uint24 public immutable perpetualId; // timestamp when the market was observed to re-open. // used to prevent cancel orders being executed right after re-open. // marketCloseSwitchTimestamp>0: timestamp when the market was opened // marketCloseSwitchTimestamp<0: abs(.) timestamp when the market was closed int64 public marketCloseSwitchTimestamp; // Array of digests of all orders - irrespecitve of deletion bytes32[] public allDigests; // Address of trader => digests (orders) mapping(address => bytes32[]) public digestsOfTrader; // Digest of an order => the order and its data mapping(bytes32 => IPerpetualOrder.Order) public orderOfDigest; // OrderDigest => Signature mapping(bytes32 => bytes) public orderSignature; // OrderDigest => Dependencies mapping(bytes32 => OrderDependency) public orderDependency; bytes32[] private actvDigests; // active order digests mapping(bytes32 => uint256) public actvDigestPos; // position of order digest in array // Addresses that are pre-authorized to execute orders mapping(address => bool) public approvedExecutor; // OrderDigest => callback function on execution/fail mapping(bytes32 => address) public callbackFunctions; // Stores last order digest bytes32 public lastOrderHash; // Perpetual Manager IPerpetualManager public immutable perpManager; /** * @notice Creates the Perpetual Limit Order Book. * @dev Replacement of constructor by initialize function for Upgradable Contracts * This function will be called only once while deploying order book using Factory. * @param _perpetualManagerAddr the address of perpetual proxy manager. * @param _perpetualId The id of perpetual. * @param _iCancelDelaySec How many seconds do we need to wait for canceling to be allowed * @param _postingFeeTbps Posting fee in tenth of a basis point * @param _callbackGasLimit gas limit for callback function * */ constructor( address _perpetualManagerAddr, uint24 _perpetualId, uint8 _iCancelDelaySec, uint16 _postingFeeTbps, uint32 _callbackGasLimit ) payable { // payable constructor reduces contract size require(_perpetualManagerAddr != address(0), "perpetual manager invalid"); require(_perpetualId != uint24(0), "perpetualId invalid"); perpetualId = _perpetualId; perpManager = IPerpetualManager(_perpetualManagerAddr); iCancelDelaySec = _iCancelDelaySec; postingFeeTbps = _postingFeeTbps; marketCloseSwitchTimestamp = int64(uint64(block.timestamp)); callbackGasLimit = _callbackGasLimit; } /** * @notice Creates Limit/Stop Orders using an array of order objects with the following fields: * iPerpetualId global id for perpetual * traderAddr address of trader * fAmount amount in base currency to be traded * fLimitPrice limit price * fTriggerPrice trigger price, non-zero for stop orders * iDeadline deadline for price (seconds timestamp) * executorAddr address of abstract executor * flags trade flags * @param _orders the orders' details. * @param _signatures The traders signatures. Required if broker submits order, otherwise it can be bytes32(0) * */ function postOrders(ClientOrder[] calldata _orders, bytes[] calldata _signatures) external { require(_orders.length > 0 && _orders.length == _signatures.length, "arrays mismatch"); _handleMarketOpening(); for (uint256 i; i < _orders.length; ) { _postOrder(_orders[i], _signatures[i]); unchecked { ++i; } } } /** * Internal version of postOrder * @param _order the order details. * @param _signature The traders signature. Required if broker submits order, otherwise it can be bytes32(0) * @return digest of the order */ function _postOrder(ClientOrder calldata _order, bytes memory _signature) internal returns (bytes32 digest) { // Validations require(perpetualId == _order.iPerpetualId, "wrong order book"); require(_order.traderAddr != address(0), "invalid-trader"); require(_order.fAmount != 0, "invalid amount"); require(_order.iDeadline > block.timestamp, "invalid-deadline"); // executionTimestamp prior to now+7 days require( _order.executionTimestamp < _order.iDeadline && _order.executionTimestamp < block.timestamp + 604800, "invalid exec ts" ); if (OrderFlags.isStopOrder(_order.flags)) { require(_order.fTriggerPrice > 0, "invalid trigger price"); } // copy client-order into a more lean perp-order Order memory perpOrder = _clientOrderToPerpOrder(_order); // set order submission time perpOrder.submittedTimestamp = uint32(block.timestamp); _checkBrokerSignature(perpOrder); digest = perpOrder._getDigest(address(perpManager), true); // no link to itself require( _order.parentChildDigest1 != digest && _order.parentChildDigest2 != digest, "order self-linked" ); // register the dependency between orders _addOrderDependency(_order, digest); if (_order.callbackTarget != address(0) && _order.callbackTarget.isContract()) { callbackFunctions[digest] = _order.callbackTarget; } // if broker submits the trader on behalf of the trader, // the trader needs to have a signature in the order if ( msg.sender != _order.traderAddr && !perpManager.isDelegate(_order.traderAddr, msg.sender) ) { //if no signature reverts with ECDSA: invalid signature length address signatory = ECDSA.recover(digest, _signature); //Verify address is not null and PK is not null either. require(signatory != address(0), "invalid signature"); require(signatory == perpOrder.traderAddr, "invalid signature"); } require(orderOfDigest[digest].traderAddr == address(0), "order-exists"); require( digestsOfTrader[perpOrder.traderAddr].length < MAX_ORDERS_PER_TRADER, "too many orders" ); // prevent clogging order books through replay of adversary, immunefy 9652 require(!perpManager.isOrderExecuted(digest), "order executed"); require(!perpManager.isOrderCanceled(digest), "order canceled"); // register _addOrder(digest, perpOrder, _signature); // posting fee = cancellation fee (rebated if executed) perpManager.chargePostingFee(perpOrder, postingFeeTbps); emit PerpetualLimitOrderCreated( _order.iPerpetualId, _order.traderAddr, _order.brokerAddr, perpOrder, digest ); return digest; } function _clientOrderToPerpOrder(ClientOrder calldata _order) internal pure returns (Order memory perpOrder) { perpOrder.flags = _order.flags; perpOrder.iPerpetualId = _order.iPerpetualId; perpOrder.brokerFeeTbps = _order.brokerFeeTbps; perpOrder.traderAddr = _order.traderAddr; perpOrder.brokerAddr = _order.brokerAddr; perpOrder.brokerSignature = _order.brokerSignature; perpOrder.fAmount = _order.fAmount; perpOrder.fLimitPrice = _order.fLimitPrice; perpOrder.fTriggerPrice = _order.fTriggerPrice; perpOrder.leverageTDR = _order.leverageTDR; perpOrder.iDeadline = _order.iDeadline; perpOrder.executionTimestamp = _order.executionTimestamp; return perpOrder; } function _perpOrderToClientOrder(Order storage _order, bytes32 _orderDigest) internal view returns (ClientOrder memory clientOrder) { clientOrder.flags = _order.flags; clientOrder.iPerpetualId = _order.iPerpetualId; clientOrder.brokerFeeTbps = _order.brokerFeeTbps; clientOrder.traderAddr = _order.traderAddr; clientOrder.brokerAddr = _order.brokerAddr; clientOrder.brokerSignature = _order.brokerSignature; clientOrder.fAmount = _order.fAmount; clientOrder.fLimitPrice = _order.fLimitPrice; clientOrder.fTriggerPrice = _order.fTriggerPrice; clientOrder.leverageTDR = _order.leverageTDR; clientOrder.iDeadline = _order.iDeadline; clientOrder.executionTimestamp = _order.executionTimestamp; clientOrder.parentChildDigest1 = orderDependency[_orderDigest].parentChildDigest1; clientOrder.parentChildDigest2 = orderDependency[_orderDigest].parentChildDigest2; return clientOrder; } /** * marketCloseSwitchBlock stores the last block that the market was * observed to be closed or opened. If the market was closed, * the sign is negative, if the market is open, the sign is positive. * Example 123, this means the market was first observed to be opened * at block 123 * Example -345, this means the market was first observed to be closed * at block 345 */ function _handleMarketOpening() internal { bool isClosed = perpManager.isPerpMarketClosed(perpetualId); if (marketCloseSwitchTimestamp > 0 && isClosed) { // the market was open marketCloseSwitchBlock>0, but // is closed now marketCloseSwitchTimestamp = -int64(uint64(block.timestamp)); } else if (marketCloseSwitchTimestamp < 0 && !isClosed) { // the market was closed marketCloseSwitchBlock<0, but // is open now marketCloseSwitchTimestamp = int64(uint64(block.timestamp)); } } /** * @notice Execute Orders or cancel & remove them (if expired). * @dev Interacts with the PerpetualTradeManager. * @param _digests hash of the order. * @param _executorAddr address that will receive referral rebate * */ function executeOrders( bytes32[] calldata _digests, address _executorAddr, bytes[] calldata _updateData, uint64[] calldata _publishTimes ) external payable { uint256 numOrders = _digests.length; require(numOrders > 0, "no orders"); // oracle: reverts if data is invalid (too old, wrong feeds) uint256 tSub = orderOfDigest[_digests[0]].submittedTimestamp; uint256 tExc = orderOfDigest[_digests[0]].executionTimestamp; uint256 timeLimit = tSub > tExc ? tSub : tExc; uint256 shortestTimeElapsed = timeLimit < block.timestamp ? block.timestamp - timeLimit : 0; for (uint256 i = 1; i < numOrders; ) { tSub = orderOfDigest[_digests[i]].submittedTimestamp; tExc = orderOfDigest[_digests[i]].executionTimestamp; timeLimit = tSub > tExc ? tSub : tExc; uint256 age = timeLimit < block.timestamp ? block.timestamp - timeLimit : 0; shortestTimeElapsed = shortestTimeElapsed < age ? shortestTimeElapsed : age; unchecked { ++i; } } // set 'maxAcceptableFeedAge' to the shortest time elapsed between order posting and now // to ensure the price is newer than the order. if (_updateData.length > 0) { perpManager.updatePriceFeeds{ value: msg.value }( perpetualId, _updateData, _publishTimes, shortestTimeElapsed ); } require(timeLimit <= perpManager.getOracleUpdateTime(perpetualId), "outdated oracles"); // update state if needed (closed -> open) _handleMarketOpening(); // execute orders one by one for (uint256 i; i < numOrders; ) { _executeOrder(_digests[i], _executorAddr); unchecked { ++i; } } } /** * @notice Cancels limit/stop order * @dev Order can be cancelled by the trader himself or it can be * removed by the relayer if it has expired. * @param _digest hash of the order. * @param _signature signed cancel-order; 0 if order expired * */ function cancelOrder( bytes32 _digest, bytes calldata _signature, bytes[] calldata _updateData, uint64[] calldata _publishTimes ) external payable { Order memory order = orderOfDigest[_digest]; require(perpetualId == order.iPerpetualId, "order not found"); perpManager.updatePriceFeeds{ value: msg.value }( perpetualId, _updateData, _publishTimes, 0 ); _handleMarketOpening(); // market is open: uint64 tsStart = marketCloseSwitchTimestamp >= 0 ? uint64(marketCloseSwitchTimestamp) : uint64(-marketCloseSwitchTimestamp); if (tsStart < order.submittedTimestamp) { tsStart = order.submittedTimestamp; } // cannot cancel when market is closed, or not sufficient delay since close if (marketCloseSwitchTimestamp < 0 || block.timestamp < tsStart + iCancelDelaySec) { emit ExecutionFailed(perpetualId, order.traderAddr, _digest, "cancel delay required"); } else { // allow only signed cancel if cancel was not executed by trader if ( msg.sender != order.traderAddr && !perpManager.isDelegate(order.traderAddr, msg.sender) ) { address signatory = ECDSA.recover( order._getDigest(address(perpManager), false), _signature ); // order.traderAddr cannot be zero (_postOrder would have reverted) // --> it suffices to check that signatory = trader require(signatory == order.traderAddr, "trader must sign"); } perpManager.executeCancelOrder(perpetualId, _digest); _removeOrder(_digest); _invokeCallback(_digest, false); } } /** * check the broker signature (if there is one). * @param _order order with stored broker signature */ function _checkBrokerSignature(Order memory _order) internal view { if (_order.brokerSignature.length == 0) { return; } address signatory = ECDSA.recover( _order._getBrokerDigest(address(perpManager)), _order.brokerSignature ); require(signatory != address(0), "invalid broker sig"); require(signatory == _order.brokerAddr, "invalid broker sig"); } /** * @notice Execute Order or cancel & remove it * @param _digest order Id (hash of the order) * @param _executorAddr Address to credit for the order execution */ function _executeOrder(bytes32 _digest, address _executorAddr) internal { Order memory order = orderOfDigest[_digest]; address trader = order.traderAddr; require(trader != address(0x0), "order not found"); order.executorAddr = _executorAddr; // check whether this is a child order that has an outstanding dependency, // if so revert (orders stay in order book) require(!_hasOutstandingDependency(_digest), "dpcy not fulfilled"); // Remove the order (locally) if it has expired bool removeDependency; // = false; bool isExecuted; // = false; if (block.timestamp <= order.iDeadline) { require(block.timestamp >= order.executionTimestamp, "exec too early"); try perpManager.tradeViaOrderBook(order, approvedExecutor[msg.sender]) returns ( bool orderSuccess ) { perpManager.rebatePostingFee(order, postingFeeTbps); if (!orderSuccess) { // market orders do not revert on price exceeds limit, instead the trade-function // returns false. This is to have a "fill-or-kill" behavior for market orders // with slippage protection. Hence the event here: emit ExecutionFailed(perpetualId, trader, _digest, "price exceeds limit"); // remove dependent orders if parent order execution failed to to slippage removeDependency = true; } else { isExecuted = true; } } catch Error(string memory reason) { /* order should be removed in the following cases "already closed" <- REMOVE THIS IN CONTRACT AND TESTS "trade amount too small" <- no rebate "position too small" <- no rebate "no amount to close" <- REMOVE THIS IN CONTRACT AND TESTS; no rebate "trader has no position to close" <- no rebate "allowance not enough" <- no rebate "balance not enough" <- no rebate "margin not enough" <- no rebate "state should be NORMAL" <- no rebate (rare) "cannot be closing if no exposure" <- indifferent "Trade amt>max amt for trader/AMM" <- rebate Do not delete dependencies: "order cancelled" <- no rebate <- don't delete children "order executed" <- no rebate <- don't delete children market order: "price exceeds limit" <- we end up in !orderSuccess above order should remain in order book (=revert) if "Trade amt>max amt for trader/AMM" "delay required" "market is closed" "trade is close only" limit/stop order: "price exceeds limit" "trigger cond not met" isFillOrKill -> always remove order, except when delay was not met */ if (_isStringEqual(reason, "Trade amt>max amt for trader/AMM")) { perpManager.rebatePostingFee(order, postingFeeTbps); removeDependency = true; } else if ( _isStringEqual(reason, "delay required") || (!OrderFlags.isFillOrKill(order.flags) && (_isStringEqual(reason, "market is closed") || _isStringEqual(reason, "trigger cond not met") || (!OrderFlags.isMarketOrder(order.flags) && (_isStringEqual(reason, "trade is close only") || _isStringEqual(reason, "price exceeds limit") || _isStringEqual(reason, "outdated oracles"))))) ) { // order should remain in order book, re-throw error revert(reason); } else if ( _isStringEqual(reason, "order cancelled") || _isStringEqual(reason, "order executed") ) { removeDependency = false; } else { removeDependency = true; } // emit event emit ExecutionFailed(perpetualId, trader, _digest, reason); } } else { // deadline exceeded emit ExecutionFailed(perpetualId, trader, _digest, "deadline"); // also remove dependent orders removeDependency = true; } // if expired or executed or caught in "orderly fail", we remove the order // if the order does not match with prices, there is a revert so // we do not end up here _wipeOrder(_digest, removeDependency); _invokeCallback(_digest, isExecuted); } /** * Check whether there is a dependent parent order that has not been executed * Returns false if the order is a parent order, * returns false if the order has no parent order, * returns false if parent order was executed or cancelled * returns false if the parent order does not exist * @param _digest order digest for the order we check * @return boolean whether has dependent parent order (that is not executed or cancelled) */ function _hasOutstandingDependency(bytes32 _digest) internal view returns (bool) { OrderDependency storage dpcy = orderDependency[_digest]; if (dpcy.parentChildDigest1 != bytes32(0)) { // is a parent order return false; } bytes32 parentDigest = dpcy.parentChildDigest2; if ( parentDigest == bytes32(0) || // no parent, Jesus perpManager.isOrderCanceled(parentDigest) || // parent order was cancelled perpManager.isOrderExecuted(parentDigest) || // parent order was executed orderOfDigest[parentDigest].traderAddr == address(0) // parent order does not exist ) { return false; } return true; } /** * Remove order from this order book and add to cancel list in perpetual manager proxy * @param _digest order digest */ function _wipeOrder(bytes32 _digest, bool _removeDependency) internal { // remove from this order book _removeOrder(_digest); // ensure order cannot be replayed: add to cancel list in perpetual manager if (!perpManager.isOrderExecuted(_digest)) { perpManager.executeCancelOrder(perpetualId, _digest); } // remove dependent orders if this was a parent order if (_removeDependency) { _wipeDependentOrders(_digest); } delete orderDependency[_digest]; } /** * If _parentDigest is indeed a parent order, children will be * removed. * Delete orderDependency for a given parent order. * Delete child orders and their dependency entries. * @param _parentDigest orderId of parent order */ function _wipeDependentOrders(bytes32 _parentDigest) internal { (bytes32 child1, bytes32 child2) = _getValidChildren(_parentDigest); if (child1 != bytes32(0)) { _wipeOrder(child1, false); } if (child2 != bytes32(0)) { _wipeOrder(child2, false); } } /** * @param _order client order with potential order dependency * @param _digest digest of the order we are registring the dependency for */ function _addOrderDependency(ClientOrder calldata _order, bytes32 _digest) internal { if (_order.parentChildDigest1 == bytes32(0) && _order.parentChildDigest2 == bytes32(0)) { return; } OrderDependency memory dpcy; dpcy.parentChildDigest1 = _order.parentChildDigest1; dpcy.parentChildDigest2 = _order.parentChildDigest2; orderDependency[_digest] = dpcy; } /** * Parent can have one or two children, child only has 1 parent, hence: * Parent: _dpcy.parentChildEntry1!=0 * Child: _dpcy.parentChildEntry2!=0 && _dpcy.parentChildEntry1==0 * @param _dpcy dependency struct */ function _isParentOrder(OrderDependency storage _dpcy) internal view returns (bool) { return _dpcy.parentChildDigest1 != bytes32(0); } /** * Return digests of 2 children (or 0) * @param _parent digest of parent order * @return bytes32(0) or digest of child order 1 * @return bytes32(0) or digest of child order 2 */ function _getValidChildren(bytes32 _parent) internal view returns (bytes32, bytes32) { OrderDependency storage dpcy = orderDependency[_parent]; if (!_isParentOrder(dpcy)) { return (bytes32(0), bytes32(0)); } bytes32 child1 = dpcy.parentChildDigest1; if (orderDependency[child1].parentChildDigest2 != _parent) { // child order does not have parent entry child1 = bytes32(0); } if (dpcy.parentChildDigest2 == bytes32(0)) { // parent only has 1 child entry return (child1, bytes32(0)); } bytes32 child2 = dpcy.parentChildDigest2; if (orderDependency[child2].parentChildDigest2 != _parent) { // child order does not have parent entry return (child1, bytes32(0)); } return (child1, child2); } /** * Get number of active orders */ function orderCount() external view returns (uint256) { return actvDigests.length; } /** * @notice Internal function to add order to order book. * */ function _addOrder( bytes32 _digest, Order memory _order, bytes memory _signature ) internal { orderOfDigest[_digest] = _order; orderSignature[_digest] = _signature; allDigests.push(_digest); digestsOfTrader[_order.traderAddr].push(_digest); // add order to orderbook linked list actvDigests.push(_digest); // the position entry is position + 1 // (first element 1 so we can distinguish from not existing and pos 0) actvDigestPos[_digest] = actvDigests.length; } /** * @notice Internal function to remove order from order book. * @dev We do not remove entry from orderOfDigest & orderSignature & orderDependency. * */ function _removeOrder(bytes32 _digest) internal { if (_digest == bytes32(0)) { // zero digest is always empty return; } // remove from trader's order-array 'orderOfDigest' // Order storage order = orderOfDigest[_digest]; bytes32[] storage orderArr = digestsOfTrader[orderOfDigest[_digest].traderAddr]; // done if nothing to remove if (orderArr.length == 0) { return; } uint256 k; while (k < orderArr.length) { if (orderArr[k] == _digest) { orderArr[k] = orderArr[orderArr.length - 1]; orderArr.pop(); k = MAX_ORDERS_PER_TRADER; } unchecked { ++k; } } // remove order delete orderOfDigest[_digest]; // remove from active orders // set position of currently last element in activeDigests array to the // position of the element we want to delete uint256 pos = actvDigestPos[_digest]; bytes32 last = actvDigests[actvDigests.length - 1]; actvDigestPos[last] = pos; actvDigestPos[_digest] = 0; actvDigests[pos - 1] = last; actvDigests.pop(); } /** * Return the order status: OPEN, EXECUTED, CANCELED, UNKNOWN order * @param _digest order identifier */ function getOrderStatus(bytes32 _digest) external view returns (OrderStatus) { if (perpManager.isOrderCanceled(_digest)) { return OrderStatus.CANCELED; } if (perpManager.isOrderExecuted(_digest)) { return OrderStatus.EXECUTED; } if (orderOfDigest[_digest].traderAddr == address(0)) { return OrderStatus.UNKNOWN; } return OrderStatus.OPEN; } /** * @notice Returns the number of (active) limit orders of a trader * @param trader address of trader. * */ function numberOfDigestsOfTrader(address trader) external view returns (uint256) { return digestsOfTrader[trader].length; } /** * @notice Returns the number of all limit orders - including those * that are cancelled/removed. * */ function numberOfAllDigests() external view returns (uint256) { return allDigests.length; } /** * @notice Returns an array of digests of orders of a trader * @param trader address of trader. * @param page start/offset. * @param limit count. * */ function limitDigestsOfTrader( address trader, uint256 page, uint256 limit ) external view returns (bytes32[] memory) { return digestsOfTrader[trader].paginate(page, limit); } /** * @notice Returns an array of all digests - including those * that are cancelled/removed. * */ function allLimitDigests(uint256 page, uint256 limit) external view returns (bytes32[] memory) { return allDigests.paginate(page, limit); } /** * @notice Returns the address of trader for an order digest * @param digest order digest. * @return trader address * */ function getTrader(bytes32 digest) external view returns (address) { return orderOfDigest[digest].traderAddr; } /** * @notice Returns all orders(specified by offset/start and limit/count) of a trader * @param trader address of trader. * @param offset start. * @param limit count. * @return orders : array of orders * */ function getOrders( address trader, uint256 offset, uint256 limit ) external view returns (ClientOrder[] memory orders) { orders = new ClientOrder[](limit); bytes32[] storage digests = digestsOfTrader[trader]; for (uint256 i = 0; i < limit; ) { if (i + offset < digests.length) { bytes32 digest = digests[i + offset]; orders[i] = _perpOrderToClientOrder(orderOfDigest[digest], digest); } unchecked { ++i; } } } /** * Polls all orders in the given range. Return arrays * always have length numElements with zero entries if not * available * @param _from start index * @param _numElements end index * @return orders client orders found * @return orderHashes corresponding order hashes * @return submittedTs corresponding submitted timestamps */ function pollRange(uint256 _from, uint256 _numElements) external view returns ( ClientOrder[] memory orders, bytes32[] memory orderHashes, uint32[] memory submittedTs ) { uint256 to = _from + _numElements; if (to > actvDigests.length) { to = actvDigests.length; } // for backward compatibility we return zero elements // if no more orders available orders = new ClientOrder[](_numElements); orderHashes = new bytes32[](_numElements); submittedTs = new uint32[](_numElements); for (uint256 k = _from; k < to; ) { uint256 j = k - _from; orderHashes[j] = actvDigests[k]; orders[j] = _perpOrderToClientOrder(orderOfDigest[orderHashes[j]], orderHashes[j]); submittedTs[j] = orderOfDigest[orderHashes[j]].submittedTimestamp; unchecked { k++; } } } function _isStringEqual(string memory _a, string memory _b) internal pure returns (bool) { return (bytes(_a).length == bytes(_b).length) && (keccak256(bytes(_a)) == keccak256(bytes(_b))); } /** * Grant permission to execute trades without delay * @param _executor Address of the executor to add */ function addExecutor(address _executor) external payable { // only ob factory can call this - so payable modifier saves gas and has no effect require(msg.sender == perpManager.getOrderBookFactoryAddress(), "only ob-factory"); require(!approvedExecutor[_executor], "already authorized"); approvedExecutor[_executor] = true; } /** * Revoke permission to execute trades wtihout delay * @param _executor Address of the executor to remove */ function removeExecutor(address _executor) external payable { // only ob factory can call this - so payable modifier saves gas and has no effect require(msg.sender == perpManager.getOrderBookFactoryAddress(), "only ob-factory"); require(approvedExecutor[_executor], "not authorized"); approvedExecutor[_executor] = false; } /** * Set the gas-limit of the external callback function * @param _gasLimit gas limit for callback function */ function setCallbackGasLimit(uint32 _gasLimit) external payable { // only ob factory can call this - so payable modifier saves gas and has no effect require(msg.sender == perpManager.getOrderBookFactoryAddress(), "only ob-factory"); callbackGasLimit = _gasLimit; } /** * Invokes the callback function * @param _orderDigest order identifier * @param _wasExecuted true if order was executed successfully, false if order removed */ function _invokeCallback(bytes32 _orderDigest, bool _wasExecuted) internal { address callbackTarget = callbackFunctions[_orderDigest]; delete callbackFunctions[_orderDigest]; if (callbackTarget == address(0)) { return; } if (callbackGasLimit == 0) { return; } bool success; try ID8XExecutionCallbackReceiver(callbackTarget).d8xExecutionCallback{ gas: callbackGasLimit }(_orderDigest, _wasExecuted) { success = true; } catch {} emit Callback(callbackTarget, success, callbackGasLimit); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) pragma solidity ^0.8.0; import "../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. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the 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. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^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. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol) pragma solidity ^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. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } else if (error == RecoverError.InvalidSignatureV) { revert("ECDSA: invalid signature 'v' 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._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { // Check the signature length // - case 65: r,s,v signature (standard) // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ 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. assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else if (signature.length == 64) { bytes32 r; bytes32 vs; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) vs := mload(add(signature, 0x40)) } return tryRecover(hash, r, vs); } 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. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (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._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (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._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (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._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (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 (v != 27 && v != 28) { return (address(0), RecoverError.InvalidSignatureV); } // If the signature is valid (and not malleable), return the signer address address 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. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (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}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(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}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(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}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) // D8X, 2022 pragma solidity 0.8.21; /** * This is a modified version of the OpenZeppelin ownable contract * Modifications * - instead of an owner, we have two actors: maintainer and governance * - maintainer can have certain priviledges but cannot transfer maintainer mandate * - governance can exchange maintainer and exchange itself * - renounceOwnership is removed * * * @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. */ abstract contract Maintainable { address private _maintainer; address private _governance; event MaintainerTransferred(address indexed previousMaintainer, address indexed newMaintainer); event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance); /** * @dev Initializes the contract setting the deployer as the initial maintainer. */ constructor() { _transferMaintainer(msg.sender); _transferGovernance(msg.sender); } /** * @dev Returns the address of the current owner. */ function maintainer() public view virtual returns (address) { return _maintainer; } /** * @dev Returns the address of the governance. */ function governance() public view virtual returns (address) { return _governance; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyMaintainer() { require(maintainer() == msg.sender, "only maintainer"); _; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyGovernance() { require(governance() == msg.sender, "only governance"); _; } /** * @dev Transfers maintainer mandate of the contract to a new account (`newMaintainer`). * Can only be called by the governance. */ function transferMaintainer(address newMaintainer) public virtual { require(msg.sender == _governance, "only governance"); require(newMaintainer != address(0), "zero address"); _transferMaintainer(newMaintainer); } /** * @dev Transfers governance mandate of the contract to a new account (`newGovernance`). * Can only be called by the governance. */ function transferGovernance(address newGovernance) public virtual { require(msg.sender == _governance, "only governance"); require(newGovernance != address(0), "zero address"); _transferGovernance(newGovernance); } /** * @dev Transfers maintainer of the contract to a new account (`newMaintainer`). * Internal function without access restriction. */ function _transferMaintainer(address newMaintainer) internal virtual { address oldM = _maintainer; _maintainer = newMaintainer; emit MaintainerTransferred(oldM, newMaintainer); } /** * @dev Transfers governance of the contract to a new account (`newGovernance`). * Internal function without access restriction. */ function _transferGovernance(address newGovernance) internal virtual { address oldG = _governance; _governance = newGovernance; emit GovernanceTransferred(oldG, newGovernance); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IShareTokenFactory { function createShareToken(uint8 _poolId, address _marginTokenAddr) external returns (address); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity 0.8.21; interface ISpotOracle { /** * @dev The market is closed if the market is not in its regular trading period. */ function isMarketClosed() external view returns (bool); function setMarketClosed(bool _marketClosed) external; /** * @dev The oracle service was shutdown and never online again. */ function isTerminated() external view returns (bool); function setTerminated(bool _terminated) external; /** * Spot price. */ function getSpotPrice() external view returns (int128, uint256); /** * Get base currency symbol. */ function getBaseCurrency() external view returns (bytes4); /** * Get quote currency symbol. */ function getQuoteCurrency() external view returns (bytes4); /** * Price Id */ function priceId() external view returns (bytes32); /** * Address of the underlying feed. */ function priceFeed() external view returns (address); /** * Conservative update period of this feed in seconds. */ function feedPeriod() external view returns (uint256); }
// SPDX-License-Identifier: BSD-4-Clause /* * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting. * Author: Mikhail Vladimirov <[email protected]> */ pragma solidity 0.8.21; /** * Smart contract library of mathematical functions operating with signed * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is * basically a simple fraction whose numerator is signed 128-bit integer and * denominator is 2^64. As long as denominator is always the same, there is no * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are * represented by int128 type holding only the numerator. */ library ABDKMath64x64 { /* * Minimum value signed 64.64-bit fixed point number may have. */ int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; /* * Maximum value signed 64.64-bit fixed point number may have. */ int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; /** * Convert signed 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromInt(int256 x) internal pure returns (int128) { require(x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF, "ABDK.fromInt"); return int128(x << 64); } /** * Convert signed 64.64 fixed point number into signed 64-bit integer number * rounding down. * * @param x signed 64.64-bit fixed point number * @return signed 64-bit integer number */ function toInt(int128 x) internal pure returns (int64) { return int64(x >> 64); } /** * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromUInt(uint256 x) internal pure returns (int128) { require(x <= 0x7FFFFFFFFFFFFFFF, "ABDK.fromUInt"); return int128(int256(x << 64)); } /** * Convert signed 64.64 fixed point number into unsigned 64-bit integer * number rounding down. Revert on underflow. * * @param x signed 64.64-bit fixed point number * @return unsigned 64-bit integer number */ function toUInt(int128 x) internal pure returns (uint64) { require(x >= 0, "ABDK.toUInt"); return uint64(uint128(x >> 64)); } /** * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point * number rounding down. Revert on overflow. * * @param x signed 128.128-bin fixed point number * @return signed 64.64-bit fixed point number */ function from128x128(int256 x) internal pure returns (int128) { int256 result = x >> 64; require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.from128x128"); return int128(result); } /** * Convert signed 64.64 fixed point number into signed 128.128 fixed point * number. * * @param x signed 64.64-bit fixed point number * @return signed 128.128 fixed point number */ function to128x128(int128 x) internal pure returns (int256) { return int256(x) << 64; } /** * Calculate x + y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function add(int128 x, int128 y) internal pure returns (int128) { int256 result = int256(x) + y; require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.add"); return int128(result); } /** * Calculate x - y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sub(int128 x, int128 y) internal pure returns (int128) { int256 result = int256(x) - y; require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.sub"); return int128(result); } /** * Calculate x * y rounding down. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function mul(int128 x, int128 y) internal pure returns (int128) { int256 result = (int256(x) * y) >> 64; require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.mul"); return int128(result); } /** * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point * number and y is signed 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y signed 256-bit integer number * @return signed 256-bit integer number */ function muli(int128 x, int256 y) internal pure returns (int256) { if (x == MIN_64x64) { require( y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF && y <= 0x1000000000000000000000000000000000000000000000000, "ABDK.muli-1" ); return -y << 63; } else { bool negativeResult = false; if (x < 0) { x = -x; negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint256 absoluteResult = mulu(x, uint256(y)); if (negativeResult) { require( absoluteResult <= 0x8000000000000000000000000000000000000000000000000000000000000000, "ABDK.muli-2" ); return -int256(absoluteResult); // We rely on overflow behavior here } else { require( absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.muli-3" ); return int256(absoluteResult); } } } /** * Calculate x * y rounding down, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y unsigned 256-bit integer number * @return unsigned 256-bit integer number */ function mulu(int128 x, uint256 y) internal pure returns (uint256) { if (y == 0) return 0; require(x >= 0, "ABDK.mulu-1"); uint256 lo = (uint256(int256(x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64; uint256 hi = uint256(int256(x)) * (y >> 128); require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.mulu-2"); hi <<= 64; require( hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo, "ABDK.mulu-3" ); return hi + lo; } /** * Calculate x / y rounding towards zero. Revert on overflow or when y is * zero. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function div(int128 x, int128 y) internal pure returns (int128) { require(y != 0, "ABDK.div-1"); int256 result = (int256(x) << 64) / y; require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.div-2"); return int128(result); } /** * Calculate x / y rounding towards zero, where x and y are signed 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x signed 256-bit integer number * @param y signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function divi(int256 x, int256 y) internal pure returns (int128) { require(y != 0, "ABDK.divi-1"); bool negativeResult = false; if (x < 0) { x = -x; // We rely on overflow behavior here negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint128 absoluteResult = divuu(uint256(x), uint256(y)); if (negativeResult) { require(absoluteResult <= 0x80000000000000000000000000000000, "ABDK.divi-2"); return -int128(absoluteResult); // We rely on overflow behavior here } else { require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.divi-3"); return int128(absoluteResult); // We rely on overflow behavior here } } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function divu(uint256 x, uint256 y) internal pure returns (int128) { require(y != 0, "ABDK.divu-1"); uint128 result = divuu(x, y); require(result <= uint128(MAX_64x64), "ABDK.divu-2"); return int128(result); } /** * Calculate -x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function neg(int128 x) internal pure returns (int128) { require(x != MIN_64x64, "ABDK.neg"); return -x; } /** * Calculate |x|. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function abs(int128 x) internal pure returns (int128) { require(x != MIN_64x64, "ABDK.abs"); return x < 0 ? -x : x; } /** * Calculate 1 / x rounding towards zero. Revert on overflow or when x is * zero. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function inv(int128 x) internal pure returns (int128) { require(x != 0, "ABDK.inv-1"); int256 result = int256(0x100000000000000000000000000000000) / x; require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.inv-2"); return int128(result); } /** * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function avg(int128 x, int128 y) internal pure returns (int128) { return int128((int256(x) + int256(y)) >> 1); } /** * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down. * Revert on overflow or in case x * y is negative. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function gavg(int128 x, int128 y) internal pure returns (int128) { int256 m = int256(x) * int256(y); require(m >= 0, "ABDK.gavg-1"); require( m < 0x4000000000000000000000000000000000000000000000000000000000000000, "ABDK.gavg-2" ); return int128(sqrtu(uint256(m))); } /** * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y uint256 value * @return signed 64.64-bit fixed point number */ function pow(int128 x, uint256 y) internal pure returns (int128) { bool negative = x < 0 && y & 1 == 1; uint256 absX = uint128(x < 0 ? -x : x); uint256 absResult; absResult = 0x100000000000000000000000000000000; if (absX <= 0x10000000000000000) { absX <<= 63; while (y != 0) { if (y & 0x1 != 0) { absResult = (absResult * absX) >> 127; } absX = (absX * absX) >> 127; if (y & 0x2 != 0) { absResult = (absResult * absX) >> 127; } absX = (absX * absX) >> 127; if (y & 0x4 != 0) { absResult = (absResult * absX) >> 127; } absX = (absX * absX) >> 127; if (y & 0x8 != 0) { absResult = (absResult * absX) >> 127; } absX = (absX * absX) >> 127; y >>= 4; } absResult >>= 64; } else { uint256 absXShift = 63; if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; } if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; } if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; } if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; } if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; } if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; } uint256 resultShift; while (y != 0) { require(absXShift < 64, "ABDK.pow-1"); if (y & 0x1 != 0) { absResult = (absResult * absX) >> 127; resultShift += absXShift; if (absResult > 0x100000000000000000000000000000000) { absResult >>= 1; resultShift += 1; } } absX = (absX * absX) >> 127; absXShift <<= 1; if (absX >= 0x100000000000000000000000000000000) { absX >>= 1; absXShift += 1; } y >>= 1; } require(resultShift < 64, "ABDK.pow-2"); absResult >>= 64 - resultShift; } int256 result = negative ? -int256(absResult) : int256(absResult); require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.pow-3"); return int128(result); } /** * Calculate sqrt (x) rounding down. Revert if x < 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sqrt(int128 x) internal pure returns (int128) { require(x >= 0, "ABDK.sqrt"); return int128(sqrtu(uint256(int256(x)) << 64)); } /** * Calculate binary logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function log_2(int128 x) internal pure returns (int128) { require(x > 0, "ABDK.log_2"); int256 msb; int256 xc = x; if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; } if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore int256 result = (msb - 64) << 64; uint256 ux = uint256(int256(x)) << uint256(127 - msb); for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) { ux *= ux; uint256 b = ux >> 255; ux >>= 127 + b; result += bit * int256(b); } return int128(result); } /** * Calculate natural logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function ln(int128 x) internal pure returns (int128) { unchecked { require(x > 0, "ABDK.ln"); return int128( int256((uint256(int256(log_2(x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF) >> 128) ); } } /** * Calculate binary exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp_2(int128 x) internal pure returns (int128) { require(x < 0x400000000000000000, "ABDK.exp_2-1"); // Overflow if (x < -0x400000000000000000) return 0; // Underflow uint256 result = 0x80000000000000000000000000000000; if (x & 0x8000000000000000 > 0) result = (result * 0x16A09E667F3BCC908B2FB1366EA957D3E) >> 128; if (x & 0x4000000000000000 > 0) result = (result * 0x1306FE0A31B7152DE8D5A46305C85EDEC) >> 128; if (x & 0x2000000000000000 > 0) result = (result * 0x1172B83C7D517ADCDF7C8C50EB14A791F) >> 128; if (x & 0x1000000000000000 > 0) result = (result * 0x10B5586CF9890F6298B92B71842A98363) >> 128; if (x & 0x800000000000000 > 0) result = (result * 0x1059B0D31585743AE7C548EB68CA417FD) >> 128; if (x & 0x400000000000000 > 0) result = (result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8) >> 128; if (x & 0x200000000000000 > 0) result = (result * 0x10163DA9FB33356D84A66AE336DCDFA3F) >> 128; if (x & 0x100000000000000 > 0) result = (result * 0x100B1AFA5ABCBED6129AB13EC11DC9543) >> 128; if (x & 0x80000000000000 > 0) result = (result * 0x10058C86DA1C09EA1FF19D294CF2F679B) >> 128; if (x & 0x40000000000000 > 0) result = (result * 0x1002C605E2E8CEC506D21BFC89A23A00F) >> 128; if (x & 0x20000000000000 > 0) result = (result * 0x100162F3904051FA128BCA9C55C31E5DF) >> 128; if (x & 0x10000000000000 > 0) result = (result * 0x1000B175EFFDC76BA38E31671CA939725) >> 128; if (x & 0x8000000000000 > 0) result = (result * 0x100058BA01FB9F96D6CACD4B180917C3D) >> 128; if (x & 0x4000000000000 > 0) result = (result * 0x10002C5CC37DA9491D0985C348C68E7B3) >> 128; if (x & 0x2000000000000 > 0) result = (result * 0x1000162E525EE054754457D5995292026) >> 128; if (x & 0x1000000000000 > 0) result = (result * 0x10000B17255775C040618BF4A4ADE83FC) >> 128; if (x & 0x800000000000 > 0) result = (result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB) >> 128; if (x & 0x400000000000 > 0) result = (result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9) >> 128; if (x & 0x200000000000 > 0) result = (result * 0x10000162E43F4F831060E02D839A9D16D) >> 128; if (x & 0x100000000000 > 0) result = (result * 0x100000B1721BCFC99D9F890EA06911763) >> 128; if (x & 0x80000000000 > 0) result = (result * 0x10000058B90CF1E6D97F9CA14DBCC1628) >> 128; if (x & 0x40000000000 > 0) result = (result * 0x1000002C5C863B73F016468F6BAC5CA2B) >> 128; if (x & 0x20000000000 > 0) result = (result * 0x100000162E430E5A18F6119E3C02282A5) >> 128; if (x & 0x10000000000 > 0) result = (result * 0x1000000B1721835514B86E6D96EFD1BFE) >> 128; if (x & 0x8000000000 > 0) result = (result * 0x100000058B90C0B48C6BE5DF846C5B2EF) >> 128; if (x & 0x4000000000 > 0) result = (result * 0x10000002C5C8601CC6B9E94213C72737A) >> 128; if (x & 0x2000000000 > 0) result = (result * 0x1000000162E42FFF037DF38AA2B219F06) >> 128; if (x & 0x1000000000 > 0) result = (result * 0x10000000B17217FBA9C739AA5819F44F9) >> 128; if (x & 0x800000000 > 0) result = (result * 0x1000000058B90BFCDEE5ACD3C1CEDC823) >> 128; if (x & 0x400000000 > 0) result = (result * 0x100000002C5C85FE31F35A6A30DA1BE50) >> 128; if (x & 0x200000000 > 0) result = (result * 0x10000000162E42FF0999CE3541B9FFFCF) >> 128; if (x & 0x100000000 > 0) result = (result * 0x100000000B17217F80F4EF5AADDA45554) >> 128; if (x & 0x80000000 > 0) result = (result * 0x10000000058B90BFBF8479BD5A81B51AD) >> 128; if (x & 0x40000000 > 0) result = (result * 0x1000000002C5C85FDF84BD62AE30A74CC) >> 128; if (x & 0x20000000 > 0) result = (result * 0x100000000162E42FEFB2FED257559BDAA) >> 128; if (x & 0x10000000 > 0) result = (result * 0x1000000000B17217F7D5A7716BBA4A9AE) >> 128; if (x & 0x8000000 > 0) result = (result * 0x100000000058B90BFBE9DDBAC5E109CCE) >> 128; if (x & 0x4000000 > 0) result = (result * 0x10000000002C5C85FDF4B15DE6F17EB0D) >> 128; if (x & 0x2000000 > 0) result = (result * 0x1000000000162E42FEFA494F1478FDE05) >> 128; if (x & 0x1000000 > 0) result = (result * 0x10000000000B17217F7D20CF927C8E94C) >> 128; if (x & 0x800000 > 0) result = (result * 0x1000000000058B90BFBE8F71CB4E4B33D) >> 128; if (x & 0x400000 > 0) result = (result * 0x100000000002C5C85FDF477B662B26945) >> 128; if (x & 0x200000 > 0) result = (result * 0x10000000000162E42FEFA3AE53369388C) >> 128; if (x & 0x100000 > 0) result = (result * 0x100000000000B17217F7D1D351A389D40) >> 128; if (x & 0x80000 > 0) result = (result * 0x10000000000058B90BFBE8E8B2D3D4EDE) >> 128; if (x & 0x40000 > 0) result = (result * 0x1000000000002C5C85FDF4741BEA6E77E) >> 128; if (x & 0x20000 > 0) result = (result * 0x100000000000162E42FEFA39FE95583C2) >> 128; if (x & 0x10000 > 0) result = (result * 0x1000000000000B17217F7D1CFB72B45E1) >> 128; if (x & 0x8000 > 0) result = (result * 0x100000000000058B90BFBE8E7CC35C3F0) >> 128; if (x & 0x4000 > 0) result = (result * 0x10000000000002C5C85FDF473E242EA38) >> 128; if (x & 0x2000 > 0) result = (result * 0x1000000000000162E42FEFA39F02B772C) >> 128; if (x & 0x1000 > 0) result = (result * 0x10000000000000B17217F7D1CF7D83C1A) >> 128; if (x & 0x800 > 0) result = (result * 0x1000000000000058B90BFBE8E7BDCBE2E) >> 128; if (x & 0x400 > 0) result = (result * 0x100000000000002C5C85FDF473DEA871F) >> 128; if (x & 0x200 > 0) result = (result * 0x10000000000000162E42FEFA39EF44D91) >> 128; if (x & 0x100 > 0) result = (result * 0x100000000000000B17217F7D1CF79E949) >> 128; if (x & 0x80 > 0) result = (result * 0x10000000000000058B90BFBE8E7BCE544) >> 128; if (x & 0x40 > 0) result = (result * 0x1000000000000002C5C85FDF473DE6ECA) >> 128; if (x & 0x20 > 0) result = (result * 0x100000000000000162E42FEFA39EF366F) >> 128; if (x & 0x10 > 0) result = (result * 0x1000000000000000B17217F7D1CF79AFA) >> 128; if (x & 0x8 > 0) result = (result * 0x100000000000000058B90BFBE8E7BCD6D) >> 128; if (x & 0x4 > 0) result = (result * 0x10000000000000002C5C85FDF473DE6B2) >> 128; if (x & 0x2 > 0) result = (result * 0x1000000000000000162E42FEFA39EF358) >> 128; if (x & 0x1 > 0) result = (result * 0x10000000000000000B17217F7D1CF79AB) >> 128; result >>= uint256(int256(63 - (x >> 64))); require(result <= uint256(int256(MAX_64x64)), "ABDK.exp_2-2"); return int128(int256(result)); } /** * Calculate natural exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp(int128 x) internal pure returns (int128) { require(x < 0x400000000000000000, "ABDK.exp"); // Overflow if (x < -0x400000000000000000) return 0; // Underflow return exp_2(int128((int256(x) * 0x171547652B82FE1777D0FFDA0D23A7D12) >> 128)); } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return unsigned 64.64-bit fixed point number */ function divuu(uint256 x, uint256 y) private pure returns (uint128) { require(y != 0, "ABDK.divuu-1"); uint256 result; if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) result = (x << 64) / y; else { uint256 msb = 192; uint256 xc = x >> 192; if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore result = (x << (255 - msb)) / (((y - 1) >> (msb - 191)) + 1); require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.divuu-2"); uint256 hi = result * (y >> 128); uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); uint256 xh = x >> 192; uint256 xl = x << 64; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here lo = hi << 128; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here assert(xh == hi >> 128); result += xl / y; } require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.divuu-3"); return uint128(result); } /** * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer * number. * * @param x unsigned 256-bit integer number * @return unsigned 128-bit integer number */ function sqrtu(uint256 x) private pure returns (uint128) { if (x == 0) return 0; else { uint256 xx = x; uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } if (xx >= 0x10000) { xx >>= 16; r <<= 8; } if (xx >= 0x100) { xx >>= 8; r <<= 4; } if (xx >= 0x10) { xx >>= 4; r <<= 2; } if (xx >= 0x8) { r <<= 1; } r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; // Seven iterations should be enough uint256 r1 = x / r; return uint128(r < r1 ? r : r1); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; library Bytes32Pagination { function paginate( bytes32[] memory hashes, uint256 page, uint256 limit ) internal pure returns (bytes32[] memory result) { result = new bytes32[](limit); for (uint256 i = 0; i < limit; i++) { if (page * limit + i < hashes.length) { result[i] = hashes[page * limit + i]; } else { break; } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "./ABDKMath64x64.sol"; library ConverterDec18 { using ABDKMath64x64 for int128; /* * Minimum value signed 64.64-bit fixed point number may have. */ int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; /* * Maximum value signed 64.64-bit fixed point number may have. */ int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; int256 private constant DECIMALS = 10**18; int128 private constant ONE_64x64 = 0x010000000000000000; int128 public constant HALF_TBPS = 92233720368548; //1e-5 * 0.5 * 2**64 // convert tenth of basis point to dec 18: uint256 public constant TBPSTODEC18 = 0x9184e72a000; // hex(10^18 * 10^-5)=(10^13) // convert tenth of basis point to ABDK 64x64: int128 public constant TBPSTOABDK = 0xa7c5ac471b48; // hex(2^64 * 10^-5) // convert two-digit integer reprentation to ABDK int128 public constant TDRTOABDK = 0x28f5c28f5c28f5c; // hex(2^64 * 10^-2) function tbpsToDec18(uint16 Vtbps) internal pure returns (uint256) { return TBPSTODEC18 * uint256(Vtbps); } function tbpsToABDK(uint16 Vtbps) internal pure returns (int128) { return int128(uint128(TBPSTOABDK) * uint128(Vtbps)); } function TDRToABDK(uint16 V2Tdr) internal pure returns (int128) { return int128(uint128(TDRTOABDK) * uint128(V2Tdr)); } function ABDKToTbps(int128 Vabdk) internal pure returns (uint16) { // add 0.5 * 1e-5 to ensure correct rounding to tenth of bps return uint16(uint128(Vabdk.add(HALF_TBPS) / TBPSTOABDK)); } function fromDec18(int256 x) internal pure returns (int128) { int256 result = (x * ONE_64x64) / DECIMALS; require(x >= MIN_64x64 && x <= MAX_64x64, "result out of range"); return int128(result); } function toDec18(int128 x) internal pure returns (int256) { return (int256(x) * DECIMALS) / ONE_64x64; } function toUDec18(int128 x) internal pure returns (uint256) { require(x >= 0, "negative value"); return uint256(toDec18(x)); } function toUDecN(int128 x, uint8 decimals) internal pure returns (uint256) { require(x >= 0, "negative value"); return uint256((int256(x) * int256(10**decimals)) / ONE_64x64); } function fromDecN(int256 x, uint8 decimals) internal pure returns (int128) { int256 result = (x * ONE_64x64) / int256(10**decimals); require(x >= MIN_64x64 && x <= MAX_64x64, "result out of range"); return int128(result); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; /** * @title Library for managing loan sets. * * @notice Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * Include with `using EnumerableBytes4Set for EnumerableBytes4Set.Bytes4Set;`. * */ library EnumerableBytes4Set { struct Bytes4Set { // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes4 => uint256) index; bytes4[] values; } /** * @notice Add a value to a set. O(1). * * @param set The set of values. * @param value The new value to add. * * @return False if the value was already in the set. */ function addBytes4(Bytes4Set storage set, bytes4 value) internal returns (bool) { if (!contains(set, value)) { set.values.push(value); set.index[value] = set.values.length; return true; } else { return false; } } /** * @notice Remove a value from a set. O(1). * * @param set The set of values. * @param value The value to remove. * * @return False if the value was not present in the set. */ function removeBytes4(Bytes4Set storage set, bytes4 value) internal returns (bool) { if (contains(set, value)) { uint256 toDeleteIndex = set.index[value] - 1; uint256 lastIndex = set.values.length - 1; /// If the element we're deleting is the last one, /// we can just remove it without doing a swap. if (lastIndex != toDeleteIndex) { bytes4 lastValue = set.values[lastIndex]; /// Move the last value to the index where the deleted value is. set.values[toDeleteIndex] = lastValue; /// Update the index for the moved value. set.index[lastValue] = toDeleteIndex + 1; // All indexes are 1-based } /// Delete the index entry for the deleted value. delete set.index[value]; /// Delete the old entry for the moved value. set.values.pop(); return true; } else { return false; } } /** * @notice Find out whether a value exists in the set. * * @param set The set of values. * @param value The value to find. * * @return True if the value is in the set. O(1). */ function contains(Bytes4Set storage set, bytes4 value) internal view returns (bool) { return set.index[value] != 0; } /** * @notice Get all set values. * * @param set The set of values. * @param start The offset of the returning set. * @param count The limit of number of values to return. * * @return output An array with all values in the set. O(N). * * @dev Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * WARNING: This function may run out of gas on large sets: use {length} and * {get} instead in these cases. */ function enumerate( Bytes4Set storage set, uint256 start, uint256 count ) internal view returns (bytes4[] memory output) { uint256 end = start + count; require(end >= start, "addition overflow"); end = set.values.length < end ? set.values.length : end; if (end == 0 || start >= end) { return output; } output = new bytes4[](end - start); for (uint256 i; i < end - start; i++) { output[i] = set.values[i + start]; } return output; } /** * @notice Get the legth of the set. * * @param set The set of values. * * @return the number of elements on the set. O(1). */ function length(Bytes4Set storage set) internal view returns (uint256) { return set.values.length; } /** * @notice Get an item from the set by its index. * * @dev Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. * * @param set The set of values. * @param index The index of the value to return. * * @return the element stored at position `index` in the set. O(1). */ function get(Bytes4Set storage set, uint256 index) internal view returns (bytes4) { return set.values[index]; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. */ library EnumerableSetUpgradeable { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. bytes32 lastvalue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastvalue; // Update the index for the moved value set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { require(set._values.length > index, "EnumerableSet: idx out of bounds"); return set._values[index]; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } function enumerate( AddressSet storage set, uint256 start, uint256 count ) internal view returns (address[] memory output) { uint256 end = start + count; require(end >= start, "addition overflow"); uint256 len = length(set); end = len < end ? len : end; if (end == 0 || start >= end) { return output; } output = new address[](end - start); for (uint256 i; i < end - start; i++) { output[i] = at(set, i + start); } return output; } function enumerateAll(AddressSet storage set) internal view returns (address[] memory output) { return enumerate(set, 0, length(set)); } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; library OrderFlags { uint32 internal constant MASK_CLOSE_ONLY = 0x80000000; uint32 internal constant MASK_MARKET_ORDER = 0x40000000; uint32 internal constant MASK_STOP_ORDER = 0x20000000; uint32 internal constant MASK_FILL_OR_KILL = 0x10000000; uint32 internal constant MASK_KEEP_POS_LEVERAGE = 0x08000000; uint32 internal constant MASK_LIMIT_ORDER = 0x04000000; /** * @dev Check if the flags contain close-only flag * @param flags The flags * @return bool True if the flags contain close-only flag */ function isCloseOnly(uint32 flags) internal pure returns (bool) { return (flags & MASK_CLOSE_ONLY) > 0; } /** * @dev Check if the flags contain market flag * @param flags The flags * @return bool True if the flags contain market flag */ function isMarketOrder(uint32 flags) internal pure returns (bool) { return (flags & MASK_MARKET_ORDER) > 0; } /** * @dev Check if the flags contain fill-or-kill flag * @param flags The flags * @return bool True if the flags contain fill-or-kill flag */ function isFillOrKill(uint32 flags) internal pure returns (bool) { return (flags & MASK_FILL_OR_KILL) > 0; } /** * @dev We keep the position leverage for a closing position, if we have * an order with the flag MASK_KEEP_POS_LEVERAGE, or if we have * a limit or stop order. * @param flags The flags * @return bool True if we should keep the position leverage on close */ function keepPositionLeverageOnClose(uint32 flags) internal pure returns (bool) { return (flags & (MASK_KEEP_POS_LEVERAGE | MASK_STOP_ORDER | MASK_LIMIT_ORDER)) > 0; } /** * @dev Check if the flags contain stop-loss flag * @param flags The flags * @return bool True if the flags contain stop-loss flag */ function isStopOrder(uint32 flags) internal pure returns (bool) { return (flags & MASK_STOP_ORDER) > 0; } /** * @dev Check if the flags contain limit-order flag * @param flags The flags * @return bool True if the flags contain limit-order flag */ function isLimitOrder(uint32 flags) internal pure returns (bool) { return (flags & MASK_LIMIT_ORDER) > 0; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; import "../../interface/IShareTokenFactory.sol"; import "../../libraries/ABDKMath64x64.sol"; import "./../functions/AMMPerpLogic.sol"; import "../../libraries/EnumerableSetUpgradeable.sol"; import "../../libraries/EnumerableBytes4Set.sol"; import "../../governance/Maintainable.sol"; /* solhint-disable max-states-count */ contract PerpStorage is Maintainable, Pausable, ReentrancyGuard { using ABDKMath64x64 for int128; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; using EnumerableBytes4Set for EnumerableBytes4Set.Bytes4Set; // enumerable map of bytes4 or addresses /** * @notice Perpetual state: * - INVALID: Uninitialized or not non-existent perpetual. * - INITIALIZING: Only when LiquidityPoolData.isRunning == false. Traders cannot perform operations. * - NORMAL: Full functional state. Traders are able to perform all operations. * - EMERGENCY: Perpetual is unsafe and the perpetual needs to be settled. * - SETTLE: Perpetual ready to be settled * - CLEARED: All margin accounts are cleared. Traders can withdraw remaining margin balance. */ enum PerpetualState { INVALID, INITIALIZING, NORMAL, EMERGENCY, SETTLE, CLEARED } // margin and liquidity pool are held in 'collateral currency' which can be either of // quote currency, base currency, or quanto currency // solhint-disable-next-line const-name-snakecase int128 internal constant ONE_64x64 = 0x10000000000000000; // 2^64 int128 internal constant FUNDING_INTERVAL_SEC = 0x70800000000000000000; //3600 * 8 * 0x10000000000000000 = 8h in seconds scaled by 2^64 for ABDKMath64x64 int128 internal constant MIN_NUM_LOTS_PER_POSITION = 0x0a0000000000000000; // 10, minimal position size in number of lots uint8 internal constant MASK_ORDER_CANCELLED = 0x1; uint8 internal constant MASK_ORDER_EXECUTED = 0x2; // at target, 1% of missing amount is transferred // at every rebalance uint8 internal iPoolCount; // delay required for trades to mitigate oracle front-running in seconds uint8 internal iTradeDelaySec; address internal ammPerpLogic; IShareTokenFactory internal shareTokenFactory; //pool id (incremental index, starts from 1) => pool data mapping(uint8 => LiquidityPoolData) internal liquidityPools; //perpetual id => pool id mapping(uint24 => uint8) internal perpetualPoolIds; address internal orderBookFactory; /** * @notice Data structure to store oracle price data. */ struct PriceTimeData { int128 fPrice; uint64 time; } /** * @notice Data structure to store user margin information. */ struct MarginAccount { int128 fLockedInValueQC; // unrealized value locked-in when trade occurs int128 fCashCC; // cash in collateral currency (base, quote, or quanto) int128 fPositionBC; // position in base currency (e.g., 1 BTC for BTCUSD) int128 fUnitAccumulatedFundingStart; // accumulated funding rate uint64 iLastOpenTimestamp; // timestamp in seconds when the position was last opened/increased uint16 feeTbps; // exchange fee in tenth of a basis point uint16 brokerFeeTbps; // broker fee in tenth of a basis point bytes16 positionId; // unique id for the position (for given trader, and perpetual). Current position, zero otherwise. } /** * @notice Store information for a given perpetual market. */ struct PerpetualData { // ------ 0 uint8 poolId; uint24 id; int32 fInitialMarginRate; //parameter: initial margin int32 fSigma2; // parameter: volatility of base-quote pair uint32 iLastFundingTime; //timestamp since last funding rate payment uint32 iLastSettlementPriceUpdateTimestamp; // timestamp when last price was observed happened uint32 iLastPriceJumpTimestamp; // timestamp when last price jump happened int32 fMaintenanceMarginRate; // parameter: maintenance margin PerpetualState state; // Perpetual AMM state AMMPerpLogic.CollateralCurrency eCollateralCurrency; //parameter: in what currency is the collateral held? uint16 minimalSpreadTbps; //parameter: minimal spread between long and short perpetual price // ------ 1 bytes4 S2BaseCCY; //base currency of S2 bytes4 S2QuoteCCY; //quote currency of S2 uint16 incentiveSpreadTbps; //parameter: maximum spread added to the PD uint16 jumpSpreadTbps; // spread that is adjusted based on price jumps bytes4 S3BaseCCY; //base currency of S3 bytes4 S3QuoteCCY; //quote currency of S3 int32 fSigma3; // parameter: volatility of quanto-quote pair int32 fRho23; // parameter: correlation of quanto/base returns uint16 liquidationPenaltyRateTbps; //parameter: penalty if AMM closes the position and not the trader //------- 2 PriceTimeData currentMarkPremiumRate; //relative diff to index price EMA, used for markprice. //------- 3 int128 premiumRatesEMA; // EMA of premium rate int128 fUnitAccumulatedFunding; //accumulated funding in collateral currency //------- 4 int128 fOpenInterest; //open interest is the larger of the amount of long and short positions in base currency int128 fTargetAMMFundSize; //target liquidity pool funds to allocate to the AMM //------- 5 int128 fCurrentTraderExposureEMA; // trade amounts (storing absolute value) int128 fCurrentFundingRate; // current instantaneous funding rate //------- 6 int128 fLotSizeBC; //parameter: minimal trade unit (in base currency) to avoid dust positions int128 fReferralRebateCC; //parameter: referall rebate in collateral currency //------- 7 int128 fTargetDFSize; // target default fund size int128 fkStar; // signed trade size that minimizes the AMM risk //------- 8 int128 fAMMTargetDD; // parameter: target distance to default (=inverse of default probability) int128 fAMMMinSizeCC; // parameter: minimal size of AMM pool, regardless of current exposure //------- 9 int128 fMinimalTraderExposureEMA; // parameter: minimal value for fCurrentTraderExposureEMA that we don't want to undershoot int128 fMinimalAMMExposureEMA; // parameter: minimal abs value for fCurrentAMMExposureEMA that we don't want to undershoot //------- 10 int128 fSettlementS3PriceData; //quanto index int128 fSettlementS2PriceData; //base-quote pair. Used as last price in normal state. //------- 11 int128 fTotalMarginBalance; //calculated for settlement, in collateral currency int32 fMarkPriceEMALambda; // parameter: Lambda parameter for EMA used in mark-price for funding rates int32 fFundingRateClamp; // parameter: funding rate clamp between which we charge 1bps int32 fMaximalTradeSizeBumpUp; // parameter: >1, users can create a maximal position of size fMaximalTradeSizeBumpUp*fCurrentAMMExposureEMA uint32 iLastTargetPoolSizeTime; //timestamp (seconds) since last update of fTargetDFSize and fTargetAMMFundSize //------- 12 int32 fDFCoverNRate; // parameter: cover-n rule for default fund. E.g., fDFCoverNRate=0.05 -> we try to cover 5% of active accounts with default fund //------- int128[2] fStressReturnS3; // parameter: negative and positive stress returns for quanto-quote asset int128[2] fDFLambda; // parameter: EMA lambda for AMM and trader exposure K,k: EMA*lambda + (1-lambda)*K. 0 regular lambda, 1 if current value exceeds past int128[2] fCurrentAMMExposureEMA; // 0: negative aggregated exposure (storing negative value), 1: positive int128[2] fStressReturnS2; // parameter: negative and positive stress returns for base-quote asset // ----- } address internal oracleFactoryAddress; // users mapping(uint24 => EnumerableSetUpgradeable.AddressSet) internal activeAccounts; //perpetualId => traderAddressSet // accounts mapping(uint24 => mapping(address => MarginAccount)) internal marginAccounts; // delegates mapping(address => address) internal delegates; // broker maps: poolId -> brokeraddress-> lots contributed // contains non-zero entries for brokers. Brokers pay default fund contributions. mapping(uint8 => mapping(address => uint32)) internal brokerMap; struct LiquidityPoolData { bool isRunning; // state uint8 iPerpetualCount; // state uint8 id; // parameter: index, starts from 1 int32 fCeilPnLShare; // parameter: cap on the share of PnL allocated to liquidity providers uint8 marginTokenDecimals; // parameter: decimals of margin token, inferred from token contract uint16 iTargetPoolSizeUpdateTime; //parameter: timestamp in seconds. How often we update the pool's target size address marginTokenAddress; //parameter: address of the margin token // ----- uint64 prevAnchor; // state: keep track of timestamp since last withdrawal was initiated int128 fRedemptionRate; // state: used for settlement in case of AMM default address shareTokenAddress; // parameter // ----- int128 fPnLparticipantsCashCC; // state: addLiquidity/withdrawLiquidity + profit/loss - rebalance int128 fTargetAMMFundSize; // state: target liquidity for all perpetuals in pool (sum) // ----- int128 fDefaultFundCashCC; // state: profit/loss int128 fTargetDFSize; // state: target default fund size for all perpetuals in pool // ----- int128 fBrokerCollateralLotSize; // param:how much collateral do brokers deposit when providing "1 lot" (not trading lot) uint128 prevTokenAmount; // state // ----- uint128 nextTokenAmount; // state uint128 totalSupplyShareToken; // state // ----- int128 fBrokerFundCashCC; // state: amount of cash in broker fund } address internal treasuryAddress; // address for the protocol treasury //pool id => perpetual id list mapping(uint8 => uint24[]) internal perpetualIds; //pool id => perpetual id => data mapping(uint8 => mapping(uint24 => PerpetualData)) internal perpetuals; /// @dev flag whether MarginTradeOrder was already executed or cancelled mapping(bytes32 => uint8) internal executedOrCancelledOrders; //proxy mapping(bytes32 => EnumerableBytes4Set.Bytes4Set) internal moduleActiveFuncSignatureList; mapping(bytes32 => address) internal moduleNameToAddress; mapping(address => bytes32) internal moduleAddressToModuleName; } /* solhint-enable max-states-count */
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "../../libraries/ABDKMath64x64.sol"; import "../../libraries/ConverterDec18.sol"; import "../../perpetual/interfaces/IAMMPerpLogic.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract AMMPerpLogic is Ownable, IAMMPerpLogic { using ABDKMath64x64 for int128; /* solhint-disable const-name-snakecase */ int128 internal constant ONE_64x64 = 0x10000000000000000; // 2^64 int128 internal constant TWO_64x64 = 0x20000000000000000; // 2*2^64 int128 internal constant FOUR_64x64 = 0x40000000000000000; //4*2^64 int128 internal constant HALF_64x64 = 0x8000000000000000; //0.5*2^64 int128 internal constant TWENTY_64x64 = 0x140000000000000000; //20*2^64 int128 private constant CDF_CONST_0 = 0x023a6ce358298c; int128 private constant CDF_CONST_1 = -0x216c61522a6f3f; int128 private constant CDF_CONST_2 = 0xc9320d9945b6c3; int128 private constant CDF_CONST_3 = -0x01bcfd4bf0995aaf; int128 private constant CDF_CONST_4 = -0x086de76427c7c501; int128 private constant CDF_CONST_5 = 0x749741d084e83004; int128 private constant CDF_CONST_6 = 0xcc42299ea1b28805; int128 private constant CDF_CONST_7 = 0x0281b263fec4e0a007; int128 private constant EXPM1_Q0 = 0x0a26c00000000000000000; int128 private constant EXPM1_Q1 = 0x0127500000000000000000; int128 private constant EXPM1_P0 = 0x0513600000000000000000; int128 private constant EXPM1_P1 = 0x27600000000000000000; int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; /* solhint-enable const-name-snakecase */ enum CollateralCurrency { QUOTE, BASE, QUANTO } struct AMMVariables { // all variables are // signed 64.64-bit fixed point number int128 fLockedValue1; // L1 in quote currency int128 fPoolM1; // M1 in quote currency int128 fPoolM2; // M2 in base currency int128 fPoolM3; // M3 in quanto currency int128 fAMM_K2; // AMM exposure (positive if trader long) int128 fCurrentTraderExposureEMA; // current average unsigned trader exposure } struct MarketVariables { int128 fIndexPriceS2; // base index int128 fIndexPriceS3; // quanto index int128 fSigma2; // standard dev of base currency int128 fSigma3; // standard dev of quanto currency int128 fRho23; // correlation base/quanto currency } /** * Calculate a half-spread that depends on past jumps * @dev if more than 30 seconds the function returns minimal spread. * Accuracy of this is obviously parametrization dependent * @param _jumpTbps jump spread based on return of oracle price * @param _minimalSpreadTbps minimal spread the EMA converges to * @param _numSecSinceJump time passed since jump * @param _fLambda convergence * @return fHalfSpread (int128) */ function volatilitySpread( uint16 _jumpTbps, uint16 _minimalSpreadTbps, uint256 _numSecSinceJump, int128 _fLambda ) external pure returns (int128) { // v0 = (1-L**numBlocksSinceJump) * minimalSpread // v1 = jump*L**numBlocksSinceJump // return v0+v1 if (_numSecSinceJump > 30) { return ConverterDec18.tbpsToABDK(_minimalSpreadTbps); } return _emaWithTimeJumps(_minimalSpreadTbps, _jumpTbps, _fLambda, _numSecSinceJump); } /** * Calculate the penalty for closing a position before the holding period is satisfied * @dev The penalty is 8 bps for same second and decreases exponentially to 0 at 32 seconds * @param _secondsSinceLastOpen how much time passed since the position was opened * @param _fLambda lambda for the exponential decay: penalty_nSeconds ~ lambda ^ nSeconds * @return penalty Penalty in tbps */ function holdingPeriodPenalty(uint256 _secondsSinceLastOpen, int128 _fLambda) external pure returns (uint16 penalty) { if (_secondsSinceLastOpen > 32) { return 0; } penalty = ConverterDec18.ABDKToTbps( _emaWithTimeJumps(0, 80, _fLambda, _secondsSinceLastOpen) ); } /** * Calculate a EWMA when the last observation happened n periods ago * @dev Given is x_t = (1 - lambda) * mean + lambda * x_t-1, and x_0 = _newObs * it returns the value of x_deltaTime * @param _mean long term mean * @param _newObs observation deltaTime periods ago * @param _fLambda lambda of the EWMA * @param _deltaTime number of periods elapsed * @return result EWMA at deltaPeriods */ function _emaWithTimeJumps( uint16 _mean, uint16 _newObs, int128 _fLambda, uint256 _deltaTime ) internal pure returns (int128 result) { _fLambda = _fLambda.pow(_deltaTime); result = ConverterDec18.tbpsToABDK(_mean).mul(ONE_64x64.sub(_fLambda)); result = result.add(_fLambda.mul(ConverterDec18.tbpsToABDK(_newObs))); } /** * Calculate the normal CDF value of _fX, i.e., * k=P(X<=_fX), for X~normal(0,1) * The approximation is of the form * Phi(x) = 1 - phi(x) / (x + exp(p(x))), * where p(x) is a polynomial of degree 6 * @param _fX signed 64.64-bit fixed point number * @return fY approximated normal-cdf evaluated at X */ function _normalCDF(int128 _fX) internal pure returns (int128 fY) { bool isNegative = _fX < 0; if (isNegative) { _fX = _fX.neg(); } if (_fX > FOUR_64x64) { fY = int128(0); } else { fY = _fX.mul(CDF_CONST_0).add(CDF_CONST_1); fY = _fX.mul(fY).add(CDF_CONST_2); fY = _fX.mul(fY).add(CDF_CONST_3); fY = _fX.mul(fY).add(CDF_CONST_4); fY = _fX.mul(fY).add(CDF_CONST_5).mul(_fX).neg().exp(); fY = fY.mul(CDF_CONST_6).add(_fX); fY = _fX.mul(_fX).mul(HALF_64x64).neg().exp().div(CDF_CONST_7).div(fY); } if (!isNegative) { fY = ONE_64x64.sub(fY); } return fY; } /** * Calculate the target size for the default fund * * @param _fK2AMM signed 64.64-bit fixed point number, Conservative negative[0]/positive[1] AMM exposure * @param _fk2Trader signed 64.64-bit fixed point number, Conservative (absolute) trader exposure * @param _fCoverN signed 64.64-bit fixed point number, cover-n rule for default fund parameter * @param fStressRet2 signed 64.64-bit fixed point number, negative[0]/positive[1] stress returns for base/quote pair * @param fStressRet3 signed 64.64-bit fixed point number, negative[0]/positive[1] stress returns for quanto/quote currency * @param fIndexPrices signed 64.64-bit fixed point number, spot price for base/quote[0] and quanto/quote[1] pairs * @param _eCCY enum that specifies in which currency the collateral is held: QUOTE, BASE, QUANTO * @return approximated normal-cdf evaluated at X */ function calculateDefaultFundSize( int128[2] memory _fK2AMM, int128 _fk2Trader, int128 _fCoverN, int128[2] memory fStressRet2, int128[2] memory fStressRet3, int128[2] memory fIndexPrices, AMMPerpLogic.CollateralCurrency _eCCY ) external pure override returns (int128) { require(_fK2AMM[0] < 0, "_fK2AMM[0] must be negative"); require(_fK2AMM[1] > 0, "_fK2AMM[1] must be positive"); require(_fk2Trader > 0, "_fk2Trader must be positive"); int128[2] memory fEll; // downward stress scenario fEll[0] = (_fK2AMM[0].abs().add(_fk2Trader.mul(_fCoverN))).mul( ONE_64x64.sub((fStressRet2[0].exp())) ); // upward stress scenario fEll[1] = (_fK2AMM[1].abs().add(_fk2Trader.mul(_fCoverN))).mul( (fStressRet2[1].exp().sub(ONE_64x64)) ); int128 fIstar; if (_eCCY == AMMPerpLogic.CollateralCurrency.BASE) { fIstar = fEll[0].div(fStressRet2[0].exp()); int128 fI2 = fEll[1].div(fStressRet2[1].exp()); if (fI2 > fIstar) { fIstar = fI2; } } else if (_eCCY == AMMPerpLogic.CollateralCurrency.QUANTO) { fIstar = fEll[0].div(fStressRet3[0].exp()); int128 fI2 = fEll[1].div(fStressRet3[1].exp()); if (fI2 > fIstar) { fIstar = fI2; } fIstar = fIstar.mul(fIndexPrices[0].div(fIndexPrices[1])); } else { assert(_eCCY == AMMPerpLogic.CollateralCurrency.QUOTE); if (fEll[0] > fEll[1]) { fIstar = fEll[0].mul(fIndexPrices[0]); } else { fIstar = fEll[1].mul(fIndexPrices[0]); } } return fIstar; } /** * Calculate the risk neutral Distance to Default (Phi(DD)=default probability) when * there is no quanto currency collateral. * We assume r=0 everywhere. * The underlying distribution is log-normal, hence the log below. * All variables are 64.64-bit fixed point number (or struct thereof) * @param fSigma2 current Market variables (price¶ms) * @param _fSign signed 64.64-bit fixed point number, sign of denominator of distance to default * @return _fThresh signed 64.64-bit fixed point number, number for which the log is the unnormalized distance to default */ function _calculateRiskNeutralDDNoQuanto( int128 fSigma2, int128 _fSign, int128 _fThresh ) internal pure returns (int128) { require(_fThresh > 0, "argument to log must be >0"); int128 _fLogTresh = _fThresh.ln(); int128 fSigma2_2 = fSigma2.mul(fSigma2); int128 fMean = fSigma2_2.div(TWO_64x64).neg(); int128 fDistanceToDefault = ABDKMath64x64.sub(_fLogTresh, fMean).div(fSigma2); // because 1-Phi(x) = Phi(-x) we change the sign if _fSign<0 // now we would like to get the normal cdf of that beast if (_fSign < 0) { fDistanceToDefault = fDistanceToDefault.neg(); } return fDistanceToDefault; } /** * Calculate the standard deviation for the random variable * evolving when quanto currencies are involved. * We assume r=0 everywhere. * All variables are 64.64-bit fixed point number (or struct thereof) * @param _mktVars current Market variables (price¶ms) * @param _fC3 signed 64.64-bit fixed point number current AMM/Market variables * @param _fC3_2 signed 64.64-bit fixed point number, squared fC3 * @return fSigmaZ standard deviation, 64.64-bit fixed point number */ function _calculateStandardDeviationQuanto( MarketVariables memory _mktVars, int128 _fC3, int128 _fC3_2 ) internal pure returns (int128 fSigmaZ) { // fVarA = (exp(sigma2^2) - 1) int128 fVarA = _mktVars.fSigma2.mul(_mktVars.fSigma2); // fVarB = 2*(exp(sigma2*sigma3*rho) - 1) int128 fVarB = _mktVars.fSigma2.mul(_mktVars.fSigma3).mul(_mktVars.fRho23).mul(TWO_64x64); // fVarC = exp(sigma3^2) - 1 int128 fVarC = _mktVars.fSigma3.mul(_mktVars.fSigma3); // sigmaZ = fVarA*C^2 + fVarB*C + fVarC fSigmaZ = fVarA.mul(_fC3_2).add(fVarB.mul(_fC3)).add(fVarC).sqrt(); } /** * Calculate the risk neutral Distance to Default (Phi(DD)=default probability) when * presence of quanto currency collateral. * * We approximate the distribution with a normal distribution * We assume r=0 everywhere. * All variables are 64.64-bit fixed point number * @param _ammVars current AMM/Market variables * @param _mktVars current Market variables (price¶ms) * @param _fSign 64.64-bit fixed point number, current AMM/Market variables * @return fDistanceToDefault signed 64.64-bit fixed point number */ function _calculateRiskNeutralDDWithQuanto( AMMVariables memory _ammVars, MarketVariables memory _mktVars, int128 _fSign, int128 _fThresh ) internal pure returns (int128 fDistanceToDefault) { require(_fSign > 0, "no sign in quanto case"); // 1) Calculate C3 int128 fC3 = _mktVars.fIndexPriceS2.mul(_ammVars.fPoolM2.sub(_ammVars.fAMM_K2)).div( _ammVars.fPoolM3.mul(_mktVars.fIndexPriceS3) ); int128 fC3_2 = fC3.mul(fC3); // 2) Calculate Variance int128 fSigmaZ = _calculateStandardDeviationQuanto(_mktVars, fC3, fC3_2); // 3) Calculate mean int128 fMean = fC3.add(ONE_64x64); // 4) Distance to default fDistanceToDefault = _fThresh.sub(fMean).div(fSigmaZ); } function calculateRiskNeutralPD( AMMVariables memory _ammVars, MarketVariables memory _mktVars, int128 _fTradeAmount, bool _withCDF ) external view virtual override returns (int128, int128) { return _calculateRiskNeutralPD(_ammVars, _mktVars, _fTradeAmount, _withCDF); } /** * Calculate the risk neutral default probability (>=0). * Function decides whether pricing with or without quanto CCY is chosen. * We assume r=0 everywhere. * All variables are 64.64-bit fixed point number (or struct thereof) * @param _ammVars current AMM variables. * @param _mktVars current Market variables (price¶ms) * @param _fTradeAmount Trade amount (can be 0), hence amounts k2 are not already factored in * that is, function will set K2:=K2+k2, L1:=L1+k2*s2 (k2=_fTradeAmount) * @param _withCDF bool. If false, the normal-cdf is not evaluated (in case the caller is only * interested in the distance-to-default, this saves calculations) * @return (default probabilit, distance to default) ; 64.64-bit fixed point numbers */ function _calculateRiskNeutralPD( AMMVariables memory _ammVars, MarketVariables memory _mktVars, int128 _fTradeAmount, bool _withCDF ) internal pure returns (int128, int128) { int128 dL = _fTradeAmount.mul(_mktVars.fIndexPriceS2); int128 dK = _fTradeAmount; _ammVars.fLockedValue1 = _ammVars.fLockedValue1.add(dL); _ammVars.fAMM_K2 = _ammVars.fAMM_K2.add(dK); // -L1 - k*s2 - M1 int128 fNumerator = (_ammVars.fLockedValue1.neg()).sub(_ammVars.fPoolM1); // s2*(M2-k2-K2) if no quanto, else M3 * s3 int128 fDenominator = _ammVars.fPoolM3 == 0 ? (_ammVars.fPoolM2.sub(_ammVars.fAMM_K2)).mul(_mktVars.fIndexPriceS2) : _ammVars.fPoolM3.mul(_mktVars.fIndexPriceS3); // handle edge sign cases first int128 fThresh; if (_ammVars.fPoolM3 == 0) { if (fNumerator < 0) { if (fDenominator >= 0) { // P( den * exp(x) < 0) = 0 return (int128(0), TWENTY_64x64.neg()); } else { // num < 0 and den < 0, and P(exp(x) > infty) = 0 int256 result = (int256(fNumerator) << 64) / fDenominator; if (result > MAX_64x64) { return (int128(0), TWENTY_64x64.neg()); } fThresh = int128(result); } } else if (fNumerator > 0) { if (fDenominator <= 0) { // P( exp(x) >= 0) = 1 return (int128(ONE_64x64), TWENTY_64x64); } else { // num > 0 and den > 0, and P(exp(x) < infty) = 1 int256 result = (int256(fNumerator) << 64) / fDenominator; if (result > MAX_64x64) { return (int128(ONE_64x64), TWENTY_64x64); } fThresh = int128(result); } } else { return fDenominator >= 0 ? (int128(0), TWENTY_64x64.neg()) : (int128(ONE_64x64), TWENTY_64x64); } } else { // denom is O(M3 * S3), div should not overflow fThresh = fNumerator.div(fDenominator); } // if we're here fDenominator !=0 and fThresh did not overflow // sign tells us whether we consider norm.cdf(f(threshold)) or 1-norm.cdf(f(threshold)) // we recycle fDenominator to store the sign since it's no longer used fDenominator = fDenominator < 0 ? ONE_64x64.neg() : ONE_64x64; int128 dd = _ammVars.fPoolM3 == 0 ? _calculateRiskNeutralDDNoQuanto(_mktVars.fSigma2, fDenominator, fThresh) : _calculateRiskNeutralDDWithQuanto(_ammVars, _mktVars, fDenominator, fThresh); int128 q; if (_withCDF) { q = _normalCDF(dd); } return (q, dd); } /** * Calculate additional/non-risk based slippage. * Ensures slippage is bounded away from zero for small trades, * and plateaus for larger-than-average trades, so that price becomes risk based. * * All variables are 64.64-bit fixed point number (or struct thereof) * @param _ammVars current AMM variables - we need the current average exposure per trader * @param _fTradeAmount 64.64-bit fixed point number, signed size of trade * @return 64.64-bit fixed point number, a number between minus one and one */ function _calculateBoundedSlippage(AMMVariables memory _ammVars, int128 _fTradeAmount) internal pure returns (int128) { int128 fTradeSizeEMA = _ammVars.fCurrentTraderExposureEMA; int128 fSlippageSize = ONE_64x64; if (_fTradeAmount.abs() < fTradeSizeEMA) { fSlippageSize = fSlippageSize.sub(_fTradeAmount.abs().div(fTradeSizeEMA)); fSlippageSize = ONE_64x64.sub(fSlippageSize.mul(fSlippageSize)); } return _fTradeAmount > 0 ? fSlippageSize : fSlippageSize.neg(); } /** * Calculate AMM price. * * All variables are 64.64-bit fixed point number (or struct thereof) * @param _ammVars current AMM variables. * @param _mktVars current Market variables (price¶ms) * Trader amounts k2 must already be factored in * that is, K2:=K2+k2, L1:=L1+k2*s2 * @param _fTradeAmount 64.64-bit fixed point number, signed size of trade * @param _fHBidAskSpread half bid-ask spread, 64.64-bit fixed point number * @return 64.64-bit fixed point number, AMM price */ function calculatePerpetualPrice( AMMVariables memory _ammVars, MarketVariables memory _mktVars, int128 _fTradeAmount, int128 _fHBidAskSpread, int128 _fIncentiveSpread ) external view virtual override returns (int128) { // add minimal spread in quote currency _fHBidAskSpread = _fTradeAmount > 0 ? _fHBidAskSpread : _fHBidAskSpread.neg(); if (_fTradeAmount == 0) { _fHBidAskSpread = 0; } // get risk-neutral default probability (always >0) { int128 fQ; int128 dd; int128 fkStar = _ammVars.fPoolM2.sub(_ammVars.fAMM_K2); (fQ, dd) = _calculateRiskNeutralPD(_ammVars, _mktVars, _fTradeAmount, true); if (_ammVars.fPoolM3 != 0) { // amend K* (see whitepaper) int128 nominator = _mktVars.fRho23.mul(_mktVars.fSigma2.mul(_mktVars.fSigma3)); int128 denom = _mktVars.fSigma2.mul(_mktVars.fSigma2); int128 h = nominator.div(denom).mul(_ammVars.fPoolM3); h = h.mul(_mktVars.fIndexPriceS3).div(_mktVars.fIndexPriceS2); fkStar = fkStar.add(h); } // decide on sign of premium if (_fTradeAmount < fkStar) { fQ = fQ.neg(); } // no rebate if exposure increases if (_fTradeAmount > 0 && _ammVars.fAMM_K2 > 0) { fQ = fQ > 0 ? fQ : int128(0); } else if (_fTradeAmount < 0 && _ammVars.fAMM_K2 < 0) { fQ = fQ < 0 ? fQ : int128(0); } // handle discontinuity at zero if ( _fTradeAmount == 0 && ((fQ < 0 && _ammVars.fAMM_K2 > 0) || (fQ > 0 && _ammVars.fAMM_K2 < 0)) ) { fQ = fQ.div(TWO_64x64); } _fHBidAskSpread = _fHBidAskSpread.add(fQ); } // get additional slippage if (_fTradeAmount != 0) { _fIncentiveSpread = _fIncentiveSpread.mul( _calculateBoundedSlippage(_ammVars, _fTradeAmount) ); _fHBidAskSpread = _fHBidAskSpread.add(_fIncentiveSpread); } // s2*(1 + sign(qp-q)*q + sign(k)*minSpread) return _mktVars.fIndexPriceS2.mul(ONE_64x64.add(_fHBidAskSpread)); } /** * Calculate target collateral M1 (Quote Currency), when no M2, M3 is present * The targeted default probability is expressed using the inverse * _fTargetDD = Phi^(-1)(targetPD) * _fK2 in absolute terms must be 'reasonably large' * sigma3, rho23, IndexpriceS3 not relevant. * @param _fK2 signed 64.64-bit fixed point number, !=0, EWMA of actual K. * @param _fL1 signed 64.64-bit fixed point number, >0, EWMA of actual L. * @param _mktVars contains 64.64 values for fIndexPriceS2*, fIndexPriceS3, fSigma2*, fSigma3, fRho23 * @param _fTargetDD signed 64.64-bit fixed point number * @return M1Star signed 64.64-bit fixed point number, >0 */ function getTargetCollateralM1( int128 _fK2, int128 _fL1, MarketVariables memory _mktVars, int128 _fTargetDD ) external pure virtual override returns (int128) { assert(_fK2 != 0); assert(_mktVars.fSigma3 == 0); assert(_mktVars.fIndexPriceS3 == 0); assert(_mktVars.fRho23 == 0); int128 fMu2 = HALF_64x64.neg().mul(_mktVars.fSigma2).mul(_mktVars.fSigma2); int128 ddScaled = _fK2 < 0 ? _mktVars.fSigma2.mul(_fTargetDD) : _mktVars.fSigma2.mul(_fTargetDD).neg(); int128 A1 = ABDKMath64x64.exp(fMu2.add(ddScaled)); return _fK2.mul(_mktVars.fIndexPriceS2).mul(A1).sub(_fL1); } /** * Calculate target collateral *M2* (Base Currency), when no M1, M3 is present * The targeted default probability is expressed using the inverse * _fTargetDD = Phi^(-1)(targetPD) * _fK2 in absolute terms must be 'reasonably large' * sigma3, rho23, IndexpriceS3 not relevant. * @param _fK2 signed 64.64-bit fixed point number, EWMA of actual K. * @param _fL1 signed 64.64-bit fixed point number, EWMA of actual L. * @param _mktVars contains 64.64 values for fIndexPriceS2, fIndexPriceS3, fSigma2, fSigma3, fRho23 * @param _fTargetDD signed 64.64-bit fixed point number * @return M2Star signed 64.64-bit fixed point number */ function getTargetCollateralM2( int128 _fK2, int128 _fL1, MarketVariables memory _mktVars, int128 _fTargetDD ) external pure virtual override returns (int128) { assert(_fK2 != 0); assert(_mktVars.fSigma3 == 0); assert(_mktVars.fIndexPriceS3 == 0); assert(_mktVars.fRho23 == 0); int128 fMu2 = HALF_64x64.mul(_mktVars.fSigma2).mul(_mktVars.fSigma2).neg(); int128 ddScaled = _fL1 < 0 ? _mktVars.fSigma2.mul(_fTargetDD) : _mktVars.fSigma2.mul(_fTargetDD).neg(); int128 A1 = ABDKMath64x64.exp(fMu2.add(ddScaled)).mul(_mktVars.fIndexPriceS2); return _fK2.sub(_fL1.div(A1)); } /** * Calculate target collateral M3 (Quanto Currency), when no M1, M2 not present * @param _fK2 signed 64.64-bit fixed point number. EWMA of actual K. * @param _fL1 signed 64.64-bit fixed point number. EWMA of actual L. * @param _mktVars contains 64.64 values for * fIndexPriceS2, fIndexPriceS3, fSigma2, fSigma3, fRho23 - all required * @param _fTargetDD signed 64.64-bit fixed point number * @return M2Star signed 64.64-bit fixed point number */ function getTargetCollateralM3( int128 _fK2, int128 _fL1, MarketVariables memory _mktVars, int128 _fTargetDD ) external pure override returns (int128) { assert(_fK2 != 0); assert(_mktVars.fSigma3 != 0); assert(_mktVars.fIndexPriceS3 != 0); assert(_mktVars.fRho23 != 0); // we solve the quadratic equation A x^2 + Bx + C = 0 // B = 2 * [X + Y * target_dd^2 * (exp(rho*sigma2*sigma3) - 1) ] // C = X^2 - Y^2 * target_dd^2 * (exp(sigma2^2) - 1) // where: // X = L1 / S3 - Y and Y = K2 * S2 / S3 // we re-use L1 for X and K2 for Y to save memory since they don't enter the equations otherwise _fK2 = _fK2.mul(_mktVars.fIndexPriceS2).div(_mktVars.fIndexPriceS3); // Y _fL1 = _fL1.div(_mktVars.fIndexPriceS3).sub(_fK2); // X // we only need the square of the target DD _fTargetDD = _fTargetDD.mul(_fTargetDD); // and we only need B/2 int128 fHalfB = _fL1.add( _fK2.mul(_fTargetDD.mul(_mktVars.fRho23.mul(_mktVars.fSigma2.mul(_mktVars.fSigma3)))) ); int128 fC = _fL1.mul(_fL1).sub( _fK2.mul(_fK2).mul(_fTargetDD).mul(_mktVars.fSigma2.mul(_mktVars.fSigma2)) ); // A = 1 - (exp(sigma3^2) - 1) * target_dd^2 int128 fA = ONE_64x64.sub(_mktVars.fSigma3.mul(_mktVars.fSigma3).mul(_fTargetDD)); // we re-use C to store the discriminant: D = (B/2)^2 - A * C fC = fHalfB.mul(fHalfB).sub(fA.mul(fC)); if (fC < 0) { // no solutions -> AMM is in profit, probability is smaller than target regardless of capital return int128(0); } // we want the larger of (-B/2 + sqrt((B/2)^2-A*C)) / A and (-B/2 - sqrt((B/2)^2-A*C)) / A // so it depends on the sign of A, or, equivalently, the sign of sqrt(...)/A fC = ABDKMath64x64.sqrt(fC).div(fA); fHalfB = fHalfB.div(fA); return fC > 0 ? fC.sub(fHalfB) : fC.neg().sub(fHalfB); } /** * Calculate the required deposit for a new position * of size _fPosition+_fTradeAmount and leverage _fTargetLeverage, * having an existing position with balance fBalance0 and size _fPosition. * This is the amount to be added to the margin collateral and can be negative (hence remove). * Fees not factored-in. * @param _fPosition0 signed 64.64-bit fixed point number. Position in base currency * @param _fBalance0 signed 64.64-bit fixed point number. Current balance. * @param _fTradeAmount signed 64.64-bit fixed point number. Trade amt in base currency * @param _fTargetLeverage signed 64.64-bit fixed point number. Desired leverage * @param _fPrice signed 64.64-bit fixed point number. Price for the trade of size _fTradeAmount * @param _fS2Mark signed 64.64-bit fixed point number. Mark-price * @param _fS3 signed 64.64-bit fixed point number. Collateral 2 quote conversion * @return signed 64.64-bit fixed point number. Required cash_cc */ function getDepositAmountForLvgPosition( int128 _fPosition0, int128 _fBalance0, int128 _fTradeAmount, int128 _fTargetLeverage, int128 _fPrice, int128 _fS2Mark, int128 _fS3, int128 _fS2 ) external pure override returns (int128) { // calculation has to be aligned with _getAvailableMargin and _executeTrade // calculation // otherwise the calculated deposit might not be enough to declare // the margin to be enough // aligned with get available margin balance int128 fPremiumCash = _fTradeAmount.mul(_fPrice.sub(_fS2)); int128 fDeltaLockedValue = _fTradeAmount.mul(_fS2); int128 fPnL = _fTradeAmount.mul(_fS2Mark); // we replace _fTradeAmount * price/S3 by // fDeltaLockedValue + fPremiumCash to be in line with // _executeTrade fPnL = fPnL.sub(fDeltaLockedValue).sub(fPremiumCash); int128 fLvgFrac = _fPosition0.add(_fTradeAmount).abs(); fLvgFrac = fLvgFrac.mul(_fS2Mark).div(_fTargetLeverage); fPnL = fPnL.sub(fLvgFrac).div(_fS3); _fBalance0 = _fBalance0.add(fPnL); return _fBalance0.neg(); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "../interfaces/IPerpetualOrder.sol"; library PerpetualHashFunctions { string private constant NAME = "Perpetual Trade Manager"; //The EIP-712 typehash for the contract's domain. bytes32 private constant DOMAIN_TYPEHASH = 0x8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866; // keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)") //The EIP-712 typehash for the Order struct used by the contract. bytes32 private constant TRADE_ORDER_TYPEHASH = 0xe5599e89712387846e6878c8cac8abdf5d6051ccbc4a6cfa5efe389720300ec8; // keccak256( // "Order(uint24 iPerpetualId,uint16 brokerFeeTbps,address traderAddr,address brokerAddr,int128 fAmount,int128 fLimitPrice,int128 fTriggerPrice,uint32 iDeadline,uint32 flags,uint16 leverageTDR,uint32 executionTimestamp)" // ) bytes32 private constant TRADE_BROKER_TYPEHASH = 0x1aae56290d242a9c761ca2ef80072ffe2a6171793ad9f88e04426b2acc5e730d; // keccak256( // "Order(uint24 iPerpetualId,uint16 brokerFeeTbps,address traderAddr,uint32 iDeadline)" // ) /** * @notice Creates the hash for an order * @param _order the address of perpetual proxy manager. * @param _contract The id of perpetual. * @param _createOrder true if order is to be executed, false for cancel-order digest * @return hash of order and _createOrder-flag * */ function _getDigest( IPerpetualOrder.Order memory _order, address _contract, bool _createOrder ) internal view returns (bytes32) { /* * The DOMAIN_SEPARATOR is a hash that uniquely identifies a * smart contract. It is built from a string denoting it as an * EIP712 Domain, the name of the token contract, the version, * the chainId in case it changes, and the address that the * contract is deployed at. */ bytes32 domainSeparator = keccak256( abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(NAME)), _getChainId(), _contract) ); // ORDER_TYPEHASH bytes32 structHash = _getStructHash(_order); bytes32 digest = keccak256(abi.encode(domainSeparator, structHash, _createOrder)); digest = ECDSA.toEthSignedMessageHash(digest); return digest; } /** * @dev Get digest a broker would sign, given an order and perpetual * @param _order Order struct * @param _contract Address of the perpetual manager */ function _getBrokerDigest(IPerpetualOrder.Order memory _order, address _contract) internal view returns (bytes32) { bytes32 domainSeparator = keccak256( abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(NAME)), _getChainId(), _contract) ); // ORDER_TYPEHASH bytes32 structHash = _getStructBrokerHash(_order); bytes32 digest = keccak256(abi.encode(domainSeparator, structHash)); digest = ECDSA.toEthSignedMessageHash(digest); return digest; } /** * @dev Chain Id */ function _getChainId() internal view returns (uint256) { uint256 chainId; // solhint-disable-next-line no-inline-assembly assembly { chainId := chainid() } return chainId; } /** * @notice Creates the hash of the order-struct * @dev order.executorAddr is not hashed, * because it is to be set by the executor * @param _order : order struct * @return bytes32 hash of order * */ function _getStructHash(IPerpetualOrder.Order memory _order) internal pure returns (bytes32) { bytes32 structHash = keccak256( abi.encode( TRADE_ORDER_TYPEHASH, _order.iPerpetualId, _order.brokerFeeTbps, // trader needs to sign for the broker fee _order.traderAddr, _order.brokerAddr, // trader needs to sign for broker _order.fAmount, _order.fLimitPrice, _order.fTriggerPrice, _order.iDeadline, _order.flags, _order.leverageTDR, _order.executionTimestamp ) ); return structHash; } /** * @dev Hash an order struct, used when creating the digest for a broker to sign. * @param _order Order struct */ function _getStructBrokerHash(IPerpetualOrder.Order memory _order) internal pure returns (bytes32) { bytes32 structHash = keccak256( abi.encode( TRADE_BROKER_TYPEHASH, _order.iPerpetualId, _order.brokerFeeTbps, _order.traderAddr, _order.iDeadline ) ); return structHash; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "../functions/AMMPerpLogic.sol"; interface IAMMPerpLogic { function volatilitySpread( uint16 _jumpTbps, uint16 _MinimalSpreadTbps, uint256 _numBlockSinceJump, int128 _fLambda ) external pure returns (int128); function holdingPeriodPenalty(uint256 _numBlockSinceLastOpen, int128 _fLambda) external pure returns (uint16); function calculateDefaultFundSize( int128[2] memory _fK2AMM, int128 _fk2Trader, int128 _fCoverN, int128[2] memory fStressRet2, int128[2] memory fStressRet3, int128[2] memory fIndexPrices, AMMPerpLogic.CollateralCurrency _eCCY ) external pure returns (int128); function calculateRiskNeutralPD( AMMPerpLogic.AMMVariables memory _ammVars, AMMPerpLogic.MarketVariables memory _mktVars, int128 _fTradeAmount, bool _withCDF ) external view returns (int128, int128); function calculatePerpetualPrice( AMMPerpLogic.AMMVariables memory _ammVars, AMMPerpLogic.MarketVariables memory _mktVars, int128 _fTradeAmount, int128 _fBidAskSpread, int128 _fIncentiveSpread ) external view returns (int128); function getTargetCollateralM1( int128 _fK2, int128 _fL1, AMMPerpLogic.MarketVariables memory _mktVars, int128 _fTargetDD ) external pure returns (int128); function getTargetCollateralM2( int128 _fK2, int128 _fL1, AMMPerpLogic.MarketVariables memory _mktVars, int128 _fTargetDD ) external pure returns (int128); function getTargetCollateralM3( int128 _fK2, int128 _fL1, AMMPerpLogic.MarketVariables memory _mktVars, int128 _fTargetDD ) external pure returns (int128); function getDepositAmountForLvgPosition( int128 _fPosition0, int128 _fBalance0, int128 _fTradeAmount, int128 _fTargetLeverage, int128 _fPrice, int128 _fS2Mark, int128 _fS3, int128 _fS2 ) external pure returns (int128); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; /** * @title Trader/Broker facing order struct * @notice this order struct is sent to the limit order book and converted into an IPerpetualOrder */ interface IClientOrder { struct ClientOrder { uint24 iPerpetualId; // unique id of the perpetual int128 fLimitPrice; // order will not execute if realized price is above (buy) or below (sell) this price uint16 leverageTDR; // leverage, set to 0 if deposit margin and trade separate; format: two-digit integer (e.g., 12.34 -> 1234) uint32 executionTimestamp; // the order will not be executed before this timestamp, allows TWAP orders uint32 flags; // Order-flags are specified in OrderFlags.sol uint32 iDeadline; // order will not be executed after this deadline address brokerAddr; // can be empty, address of the broker int128 fTriggerPrice; // trigger price for stop-orders|0. Order can be executed if the mark price is below this price (sell order) or above (buy) int128 fAmount; // signed amount of base-currency. Will be rounded to lot size bytes32 parentChildDigest1; // see notice in LimitOrderBook.sol address traderAddr; // address of the trader bytes32 parentChildDigest2; // see notice in LimitOrderBook.sol uint16 brokerFeeTbps; // broker fee in tenth of a basis point bytes brokerSignature; // signature, can be empty if no brokerAddr provided address callbackTarget; // address of contract implementing callback function //address executorAddr; <- will be set by LimitOrderBook //uint64 submittedBlock <- will be set by LimitOrderBook } } interface ID8XExecutionCallbackReceiver { function d8xExecutionCallback(bytes32 orderDigest, bool isExecuted) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "./IPerpetualOrder.sol"; /** * @notice The libraryEvents defines events that will be raised from modules (contract/modules). * @dev DO REMEMBER to add new events in modules here. */ interface ILibraryEvents { // PerpetualModule event Clear(uint24 indexed perpetualId, address indexed trader); event Settle(uint24 indexed perpetualId, address indexed trader, int256 amount); event SettlementComplete(uint24 indexed perpetualId); event SetNormalState(uint24 indexed perpetualId); event SetEmergencyState( uint24 indexed perpetualId, int128 fSettlementMarkPremiumRate, int128 fSettlementS2Price, int128 fSettlementS3Price ); event SettleState(uint24 indexed perpetualId); event SetClearedState(uint24 indexed perpetualId); event UpdateUnitAccumulatedFunding(uint24 perpetualId, int128 unitAccumulativeFunding); // Participation pool event LiquidityAdded( uint8 indexed poolId, address indexed user, uint256 tokenAmount, uint256 shareAmount ); event LiquidityProvisionPaused(bool pauseOn, uint8 poolId); event LiquidityRemoved( uint8 indexed poolId, address indexed user, uint256 tokenAmount, uint256 shareAmount ); event LiquidityWithdrawalInitiated( uint8 indexed poolId, address indexed user, uint256 shareAmount ); // setters // oracles event SetOracles(uint24 indexed perpetualId, bytes4[2] baseQuoteS2, bytes4[2] baseQuoteS3); // perp parameters event SetPerpetualBaseParameters(uint24 indexed perpetualId, int128[7] baseParams); event SetPerpetualRiskParameters( uint24 indexed perpetualId, int128[5] underlyingRiskParams, int128[12] defaultFundRiskParams ); event SetParameter(uint24 indexed perpetualId, string name, int128 value); event SetParameterPair(uint24 indexed perpetualId, string name, int128 value1, int128 value2); // pool parameters event SetPoolParameter(uint8 indexed poolId, string name, int128 value); event TransferAddressTo(string name, address oldOBFactory, address newOBFactory); // only governance event SetBlockDelay(uint8 delay); // fee structure parameters event SetBrokerDesignations(uint32[] designations, uint16[] fees); event SetBrokerTiers(uint256[] tiers, uint16[] feesTbps); event SetTraderTiers(uint256[] tiers, uint16[] feesTbps); event SetTraderVolumeTiers(uint256[] tiers, uint16[] feesTbps); event SetBrokerVolumeTiers(uint256[] tiers, uint16[] feesTbps); event SetUtilityToken(address tokenAddr); event BrokerLotsTransferred( uint8 indexed poolId, address oldOwner, address newOwner, uint32 numLots ); event BrokerVolumeTransferred( uint8 indexed poolId, address oldOwner, address newOwner, int128 fVolume ); // funds event UpdateParticipationFundCash( uint8 indexed poolId, int128 fDeltaAmountCC, int128 fNewFundCash ); event UpdateDefaultFundCash(uint8 indexed poolId, int128 fDeltaAmountCC, int128 fNewFundCash); // brokers event UpdateBrokerAddedCash(uint8 indexed poolId, uint32 iLots, uint32 iNewBrokerLots); event UpdateBrokerFundCash(uint8 indexed poolId, int128 fDeltaAmountCC, int128 fNewFundCash); // TradeModule event Trade( uint24 indexed perpetualId, address indexed trader, bytes16 indexed positionId, IPerpetualOrder.Order order, bytes32 orderDigest, int128 newPositionSizeBC, int128 price, int128 fFeeCC, int128 fPnlCC, int128 fB2C ); event UpdateMarginAccount( uint24 indexed perpetualId, address indexed trader, bytes16 indexed positionId, int128 fPositionBC, int128 fCashCC, int128 fLockedInValueQC, int128 fFundingPaymentCC, int128 fOpenInterestBC ); event Liquidate( uint24 perpetualId, address indexed liquidator, address indexed trader, bytes16 indexed positionId, int128 amountLiquidatedBC, int128 liquidationPrice, int128 newPositionSizeBC, int128 fFeeCC, int128 fPnlCC ); event TransferFeeToexecutor( uint24 indexed perpetualId, address indexed trader, address indexed executor, int128 referralRebate ); event TransferFeeToBroker(uint24 indexed perpetualId, address indexed broker, int128 feeCC); event PerpetualLimitOrderCancelled(uint24 indexed perpetualId, bytes32 indexed orderHash); event DistributeFees( uint8 indexed poolId, uint24 indexed perpetualId, address indexed trader, int128 protocolFeeCC, int128 participationFundFeeCC ); // PerpetualManager/factory event RunLiquidityPool(uint8 _liqPoolID); event LiquidityPoolCreated( uint8 id, address marginTokenAddress, address shareTokenAddress, uint16 iTargetPoolSizeUpdateTime, int128 fBrokerCollateralLotSize ); event PerpetualCreated( uint8 poolId, uint24 id, int128[7] baseParams, int128[5] underlyingRiskParams, int128[12] defaultFundRiskParams, uint256 eCollateralCurrency ); // emit tokenAddr==0x0 if the token paid is the aggregated token, otherwise the address of the token event TokensDeposited(uint24 indexed perpetualId, address indexed trader, int128 amount); event TokensWithdrawn(uint24 indexed perpetualId, address indexed trader, int128 amount); event UpdateMarkPrice( uint24 indexed perpetualId, int128 fMidPricePremium, int128 fMarkPricePremium, int128 fSpotIndexPrice ); event UpdateFundingRate(uint24 indexed perpetualId, int128 fFundingRate); event UpdateAMMFundTargetSize( uint24 indexed perpetualId, uint8 indexed liquidityPoolId, int128 fTargetAMMFundSizeInPerpetual, int128 fTargetAMMFundSizeInPool ); event UpdateDefaultFundTargetSize( uint8 indexed liquidityPoolId, int128 fDefaultFundCashCC, int128 fTargetDFSize ); event UpdateReprTradeSizes( uint24 indexed perpetualId, int128 fCurrentTraderExposureEMA, int128 fCurrentAMMExposureEMAShort, int128 fCurrentAMMExposureEMALong ); event TransferEarningsToTreasury(uint8 _poolId, int128 fEarnings, int128 newDefaultFundSize); event SetDelegate(address trader, address delegate); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "../interfaces/IPerpetualOrder.sol"; import "../../interface/ISpotOracle.sol"; interface IPerpetualBrokerFeeLogic { function determineExchangeFee(IPerpetualOrder.Order memory _order) external view returns (uint16); function updateVolumeEMAOnNewTrade( uint24 _iPerpetualId, address _traderAddr, address _brokerAddr, int128 _tradeAmountBC ) external; function queryExchangeFee( uint8 _poolId, address _traderAddr, address _brokerAddr ) external view returns (uint16); function splitProtocolFee(uint16 fee) external pure returns (int128, int128); function setFeesForDesignation(uint32[] calldata _designations, uint16[] calldata _fees) external; function getLastPerpetualBaseToUSDConversion(uint24 _iPerpetualId) external view returns (int128); function getFeeForTraderVolume(uint8 _poolId, address _traderAddr) external view returns (uint16); function getFeeForBrokerVolume(uint8 _poolId, address _brokerAddr) external view returns (uint16); function setOracleFactoryForPerpetual(uint24 _iPerpetualId, address _oracleAddr) external; function setBrokerTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external; function setTraderTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external; function setTraderVolumeTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external; function setBrokerVolumeTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external; function setUtilityTokenAddr(address tokenAddr) external; function getBrokerInducedFee(uint8 _poolId, address _brokerAddr) external view returns (uint16); function getBrokerDesignation(uint8 _poolId, address _brokerAddr) external view returns (uint32); function getFeeForBrokerDesignation(uint32 _brokerDesignation) external view returns (uint16); function getFeeForBrokerStake(address brokerAddr) external view returns (uint16); function getFeeForTraderStake(address traderAddr) external view returns (uint16); function getCurrentTraderVolume(uint8 _poolId, address _traderAddr) external view returns (int128); function getCurrentBrokerVolume(uint8 _poolId, address _brokerAddr) external view returns (int128); function transferBrokerLots( uint8 _poolId, address _transferToAddr, uint32 _lots ) external; function transferBrokerOwnership(uint8 _poolId, address _transferToAddr) external; function setInitialVolumeForFee( uint8 _poolId, address _brokerAddr, uint16 _feeTbps ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IPerpetualDepositManager { function deposit( uint24 _iPerpetualId, address _traderAddr, int128 _fAmount, bytes[] calldata _updateData, uint64[] calldata _publishTimes ) external payable; function depositToDefaultFund(uint8 _poolId, int128 _fAmount) external; function depositBrokerLots(uint8 _poolId, uint32 _iLots) external; function withdrawFromDefaultFund(uint8 _poolId, int128 _fAmount) external; function transferEarningsToTreasury(uint8 _poolId, int128 _fAmount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IPerpetualFactory { function createPerpetual( uint8 _iPoolId, bytes4[2] calldata _baseQuoteS2, bytes4[2] calldata _baseQuoteS3, int128[7] calldata _baseParams, int128[5] calldata _underlyingRiskParams, int128[12] calldata _defaultFundRiskParams, uint256 _eCollateralCurrency ) external; function activatePerpetual(uint24 _perpetualId) external; function setPerpetualOracles( uint24 _iPerpetualId, bytes4[2] calldata _baseQuoteS2, bytes4[2] calldata _baseQuoteS3 ) external; function setPerpetualBaseParams(uint24 _iPerpetualId, int128[7] calldata _baseParams) external; function setPerpetualRiskParams( uint24 _iPerpetualId, int128[5] calldata _underlyingRiskParams, int128[12] calldata _defaultFundRiskParams ) external; function setPerpetualParam( uint24 _iPerpetualId, string memory _varName, int128 _value ) external; function setPerpetualParamPair( uint24 _iPerpetualId, string memory _name, int128 _value1, int128 _value2 ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "../core/PerpStorage.sol"; import "../../interface/IShareTokenFactory.sol"; interface IPerpetualGetter { struct PerpetualStaticInfo { uint24 id; address limitOrderBookAddr; int32 fInitialMarginRate; int32 fMaintenanceMarginRate; uint8 perpetualState; AMMPerpLogic.CollateralCurrency collCurrencyType; bytes4 S2BaseCCY; //base currency of S2 bytes4 S2QuoteCCY; //quote currency of S2 bytes4 S3BaseCCY; //base currency of S3 bytes4 S3QuoteCCY; //quote currency of S3 int128 fLotSizeBC; int128 fReferralRebateCC; bytes32[] priceIds; bool[] isPyth; } function getPoolCount() external view returns (uint8); function getPerpetualId(uint8 _poolId, uint8 _perpetualIndex) external view returns (uint24); function getLiquidityPool(uint8 _poolId) external view returns (PerpStorage.LiquidityPoolData memory); function getLiquidityPools(uint8 _poolIdFrom, uint8 _poolIdTo) external view returns (PerpStorage.LiquidityPoolData[] memory); function getPoolIdByPerpetualId(uint24 _perpetualId) external view returns (uint8); function getPerpetual(uint24 _perpetualId) external view returns (PerpStorage.PerpetualData memory); function getPerpetuals(uint24[] calldata perpetualIds) external view returns (PerpStorage.PerpetualData[] memory); function queryMidPrices(uint24[] calldata perpetualIds, int128[] calldata idxPriceDataPairs) external view returns (int128[] memory); function getMarginAccount(uint24 _perpetualId, address _traderAddress) external view returns (PerpStorage.MarginAccount memory); function isActiveAccount(uint24 _perpetualId, address _traderAddress) external view returns (bool); function getAMMPerpLogic() external view returns (address); function getShareTokenFactory() external view returns (IShareTokenFactory); function getActivePerpAccounts(uint24 _perpetualId) external view returns (address[] memory); function getPerpetualCountInPool(uint8 _poolId) external view returns (uint8); function getAMMState(uint24 _perpetualId, int128[2] calldata _fIndexPrice) external view returns (int128[15] memory); function getTraderState( uint24 _perpetualId, address _traderAddress, int128[2] calldata _fIndexPrice ) external view returns (int128[11] memory); function getActivePerpAccountsByChunks( uint24 _perpetualId, uint256 _from, uint256 _to ) external view returns (address[] memory); function countActivePerpAccounts(uint24 _perpetualId) external view returns (uint256); function getOraclePrice(bytes4[2] memory _baseQuote) external view returns (int128 fPrice); function getOracleFactory() external view returns (address); function getTreasuryAddress() external view returns (address); function getOrderBookFactoryAddress() external view returns (address); function getOrderBookAddress(uint24 _perpetualId) external view returns (address); function isMarketClosed(bytes4 _baseCurrency, bytes4 _quoteCurrency) external view returns (bool); function isPerpMarketClosed(uint24 _perpetualId) external view returns (bool isClosed); function getPriceInfo(uint24 _perpetualId) external view returns (bytes32[] memory, bool[] memory); function getPoolStaticInfo(uint8 _poolFromIdx, uint8 _poolToIdx) external view returns ( uint24[][] memory, address[] memory, address[] memory, address _oracleFactoryAddress ); function getPerpetualStaticInfo(uint24[] calldata perpetualIds) external view returns (PerpetualStaticInfo[] memory); function getLiquidatableAccounts(uint24 _perpetualId, int128[2] calldata _fIndexPrice) external view returns (address[] memory unsafeAccounts); function getNextLiquidatableTrader(uint24 _perpetualId, int128[2] calldata _fIndexPrice) external view returns (address traderAddr); function getOracleUpdateTime(uint24 _perpetualId) external view returns (uint256); function isDelegate(address _trader, address _delegate) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "./IPerpetualOrder.sol"; interface IPerpetualLimitTradeManager is IPerpetualOrder { function tradeViaOrderBook(Order memory _order, bool _isApprovedExecutor) external returns (bool); function chargePostingFee(Order memory _order, uint16 _feeTbps) external; function rebatePostingFee(Order memory _order, uint16 _feeTbps) external; function executeCancelOrder(uint24 _perpetualId, bytes32 _digest) external; function isOrderExecuted(bytes32 digest) external view returns (bool); function isOrderCanceled(bytes32 digest) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IPerpetualLiquidator { function liquidateByAMM( uint24 _perpetualIndex, address _liquidatorAddr, address _traderAddr, bytes[] calldata _updateData, uint64[] calldata _publishTimes ) external payable returns (int128 liquidatedAmount); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "./IPerpetualFactory.sol"; import "./IPerpetualPoolFactory.sol"; import "./IPerpetualDepositManager.sol"; import "./IPerpetualWithdrawManager.sol"; import "./IPerpetualWithdrawAllManager.sol"; import "./IPerpetualLiquidator.sol"; import "./IPerpetualTreasury.sol"; import "./IPerpetualSettlement.sol"; import "./IPerpetualGetter.sol"; import "./ILibraryEvents.sol"; import "./IPerpetualTradeLogic.sol"; import "./IAMMPerpLogic.sol"; import "./IPerpetualUpdateLogic.sol"; import "./IPerpetualRebalanceLogic.sol"; import "./IPerpetualMarginLogic.sol"; import "./IPerpetualLimitTradeManager.sol"; import "./IPerpetualBrokerFeeLogic.sol"; interface IPerpetualManager is IPerpetualFactory, IPerpetualPoolFactory, IPerpetualDepositManager, IPerpetualWithdrawManager, IPerpetualWithdrawAllManager, IPerpetualLiquidator, IPerpetualTreasury, IPerpetualTradeLogic, IPerpetualBrokerFeeLogic, IPerpetualLimitTradeManager, IPerpetualSettlement, IPerpetualGetter, IAMMPerpLogic, ILibraryEvents, IPerpetualUpdateLogic, IPerpetualRebalanceLogic, IPerpetualMarginLogic {}
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "./IPerpetualOrder.sol"; interface IPerpetualMarginLogic is IPerpetualOrder { function depositMarginForOpeningTrade( uint24 _iPerpetualId, int128 _fDepositRequired, Order memory _order ) external returns (bool); function withdrawDepositFromMarginAccount(uint24 _iPerpetualId, address _traderAddr) external; function reduceMarginCollateral( uint24 _iPerpetualId, address _traderAddr, int128 _fAmountToWithdraw ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IPerpetualOrder { struct Order { uint16 leverageTDR; // 12.43x leverage is represented by 1243 (two-digit integer representation); 0 if deposit and trade separate uint16 brokerFeeTbps; // broker can set their own fee uint24 iPerpetualId; // global id for perpetual address traderAddr; // address of trader uint32 executionTimestamp; // normally set to current timestamp; order will not be executed prior to this timestamp. address brokerAddr; // address of the broker or zero uint32 submittedTimestamp; uint32 flags; // order flags uint32 iDeadline; //deadline for price (seconds timestamp) address executorAddr; // address of the executor set by contract int128 fAmount; // amount in base currency to be traded int128 fLimitPrice; // limit price int128 fTriggerPrice; //trigger price. Non-zero for stop orders. bytes brokerSignature; //signature of broker (or 0) } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IPerpetualPoolFactory { function setPerpetualPoolFactory(address _shareTokenFactory) external; function createLiquidityPool( address _marginTokenAddress, uint16 _iTargetPoolSizeUpdateTime, int128 _fBrokerCollateralLotSize, int128 _fCeilPnLShare ) external returns (uint8); function runLiquidityPool(uint8 _liqPoolID) external; function setAMMPerpLogic(address _AMMPerpLogic) external; function setPoolParam( uint8 _poolId, string memory _name, int128 _value ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IPerpetualRebalanceLogic { function rebalance(uint24 _iPerpetualId) external; function decreasePoolCash(uint8 _iPoolIdx, int128 _fAmount) external; function increasePoolCash(uint8 _iPoolIdx, int128 _fAmount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IPerpetualSettlement { function adjustSettlementPrice( uint24 _perpetualId, int128 _fSettlementS2, int128 _fSettlementS3 ) external; function togglePerpEmergencyState(uint24 _perpetualId) external; function settleNextTraderInPool(uint8 _id) external returns (bool); function settle(uint24 _perpetualID, address _traderAddr) external; function getSettleableAccounts( uint24 _perpetualId, uint256 start, uint256 count ) external view returns (address[] memory); function transferValueToTreasury() external returns (bool success); function deactivatePerp(uint24 _perpetualId) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "../interfaces/IPerpetualOrder.sol"; interface IPerpetualTradeLogic { function executeTrade( uint24 _iPerpetualId, address _traderAddr, int128 _fTraderPos, int128 _fTradeAmount, int128 _fPrice, bool _isClose ) external returns (int128); function preTrade(IPerpetualOrder.Order memory _order) external returns (int128, int128); function distributeFeesLiquidation( uint24 _iPerpetualId, address _traderAddr, int128 _fDeltaPositionBC ) external returns (int128); function distributeFees(IPerpetualOrder.Order memory _order, bool _hasOpened) external returns (int128); function validateStopPrice( bool _isLong, int128 _fMarkPrice, int128 _fTriggerPrice ) external pure; function getMaxSignedOpenTradeSizeForPos( uint24 _perpetualId, int128 _fCurrentTraderPos, bool _isBuy ) external view returns (int128); function queryPerpetualPrice( uint24 _iPerpetualId, int128 _fTradeAmountBC, int128[2] calldata _fIndexPrice ) external view returns (int128); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IPerpetualTreasury { struct WithdrawRequest { address lp; uint256 shareTokens; uint64 withdrawTimestamp; } function addLiquidity(uint8 _iPoolIndex, uint256 _tokenAmount) external; function pauseLiquidityProvision(uint8 _poolId, bool _pauseOn) external; function withdrawLiquidity(uint8 _iPoolIndex, uint256 _shareAmount) external; function executeLiquidityWithdrawal(uint8 _poolId, address _lpAddr) external; function getCollateralTokenAmountForPricing(uint8 _poolId) external view returns (int128); function getShareTokenPriceD18(uint8 _poolId) external view returns (uint256 price); function getTokenAmountToReturn(uint8 _poolId, uint256 _shareAmount) external view returns (uint256); function getWithdrawRequests( uint8 poolId, uint256 _fromIdx, uint256 numRequests ) external view returns (WithdrawRequest[] memory); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IPerpetualUpdateLogic { function updateAMMTargetFundSize(uint24 _iPerpetualId) external; function updateDefaultFundTargetSizeRandom(uint8 _iPoolIndex) external; function updateDefaultFundTargetSize(uint24 _iPerpetualId) external; function updateFundingAndPricesBefore(uint24 _iPerpetualId, bool _revertIfClosed) external; function updateFundingAndPricesAfter(uint24 _iPerpetualId) external; function setNormalState(uint24 _iPerpetualId) external; /** * Set emergency state * @param _iPerpetualId Perpetual id */ function setEmergencyState(uint24 _iPerpetualId) external; /** * @notice Set external treasury (DAO) * @param _treasury treasury address */ function setTreasury(address _treasury) external; /** * @notice Set order book factory (DAO) * @param _orderBookFactory order book factory address */ function setOrderBookFactory(address _orderBookFactory) external; /** * @notice Set oracle factory (DAO) * @param _oracleFactory oracle factory address */ function setOracleFactory(address _oracleFactory) external; /** * @notice Set delay for trades to be executed * @param _delay delay in number of blocks */ function setBlockDelay(uint8 _delay) external; /** * @notice Submits price updates to the feeds used by a given perpetual. * @dev Reverts if the submission does not match the perpetual or * if the feed rejects it for a reason other than being unnecessary. * If this function returns false, sender is not charged msg.value. * @param _perpetualId Perpetual Id * @param _updateData Data to send to price feeds * @param _publishTimes Publish timestamps * @param _maxAcceptableFeedAge Maximum age of update in seconds */ function updatePriceFeeds( uint24 _perpetualId, bytes[] calldata _updateData, uint64[] calldata _publishTimes, uint256 _maxAcceptableFeedAge ) external payable; /** * @notice Links the message sender to a delegate to manage orders on their behalf. * @param delegate Address of delegate */ function setDelegate(address delegate) external; /** * @notice Removes the sender's delegate, and reverts if no delegate has been set.s */ function removeDelegate() external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IPerpetualWithdrawAllManager { function withdrawAll( uint24 _iPerpetualId, address _traderAddr, bytes[] calldata _updateData, uint64[] calldata _publishTimes ) external payable; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IPerpetualWithdrawManager { function withdraw( uint24 _iPerpetualId, address _traderAddr, int128 _fAmount, bytes[] calldata _updateData, uint64[] calldata _publishTimes ) external payable; }
{ "optimizer": { "enabled": true, "runs": 400 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_perpetualManagerAddr","type":"address"},{"internalType":"uint24","name":"_perpetualId","type":"uint24"},{"internalType":"uint8","name":"_iCancelDelaySec","type":"uint8"},{"internalType":"uint16","name":"_postingFeeTbps","type":"uint16"},{"internalType":"uint32","name":"_callbackGasLimit","type":"uint32"}],"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"callbackTarget","type":"address"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"Callback","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"bytes32","name":"digest","type":"bytes32"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"ExecutionFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"address","name":"brokerAddr","type":"address"},{"components":[{"internalType":"uint16","name":"leverageTDR","type":"uint16"},{"internalType":"uint16","name":"brokerFeeTbps","type":"uint16"},{"internalType":"uint24","name":"iPerpetualId","type":"uint24"},{"internalType":"address","name":"traderAddr","type":"address"},{"internalType":"uint32","name":"executionTimestamp","type":"uint32"},{"internalType":"address","name":"brokerAddr","type":"address"},{"internalType":"uint32","name":"submittedTimestamp","type":"uint32"},{"internalType":"uint32","name":"flags","type":"uint32"},{"internalType":"uint32","name":"iDeadline","type":"uint32"},{"internalType":"address","name":"executorAddr","type":"address"},{"internalType":"int128","name":"fAmount","type":"int128"},{"internalType":"int128","name":"fLimitPrice","type":"int128"},{"internalType":"int128","name":"fTriggerPrice","type":"int128"},{"internalType":"bytes","name":"brokerSignature","type":"bytes"}],"indexed":false,"internalType":"struct IPerpetualOrder.Order","name":"order","type":"tuple"},{"indexed":false,"internalType":"bytes32","name":"digest","type":"bytes32"}],"name":"PerpetualLimitOrderCreated","type":"event"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"actvDigestPos","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_executor","type":"address"}],"name":"addExecutor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allDigests","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"page","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"allLimitDigests","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedExecutor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"callbackFunctions","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"callbackGasLimit","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_digest","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"bytes[]","name":"_updateData","type":"bytes[]"},{"internalType":"uint64[]","name":"_publishTimes","type":"uint64[]"}],"name":"cancelOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"digestsOfTrader","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_digests","type":"bytes32[]"},{"internalType":"address","name":"_executorAddr","type":"address"},{"internalType":"bytes[]","name":"_updateData","type":"bytes[]"},{"internalType":"uint64[]","name":"_publishTimes","type":"uint64[]"}],"name":"executeOrders","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_digest","type":"bytes32"}],"name":"getOrderStatus","outputs":[{"internalType":"enum LimitOrderBook.OrderStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getOrders","outputs":[{"components":[{"internalType":"uint24","name":"iPerpetualId","type":"uint24"},{"internalType":"int128","name":"fLimitPrice","type":"int128"},{"internalType":"uint16","name":"leverageTDR","type":"uint16"},{"internalType":"uint32","name":"executionTimestamp","type":"uint32"},{"internalType":"uint32","name":"flags","type":"uint32"},{"internalType":"uint32","name":"iDeadline","type":"uint32"},{"internalType":"address","name":"brokerAddr","type":"address"},{"internalType":"int128","name":"fTriggerPrice","type":"int128"},{"internalType":"int128","name":"fAmount","type":"int128"},{"internalType":"bytes32","name":"parentChildDigest1","type":"bytes32"},{"internalType":"address","name":"traderAddr","type":"address"},{"internalType":"bytes32","name":"parentChildDigest2","type":"bytes32"},{"internalType":"uint16","name":"brokerFeeTbps","type":"uint16"},{"internalType":"bytes","name":"brokerSignature","type":"bytes"},{"internalType":"address","name":"callbackTarget","type":"address"}],"internalType":"struct IClientOrder.ClientOrder[]","name":"orders","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"digest","type":"bytes32"}],"name":"getTrader","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastOrderHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"page","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"limitDigestsOfTrader","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketCloseSwitchTimestamp","outputs":[{"internalType":"int64","name":"","type":"int64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfAllDigests","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"}],"name":"numberOfDigestsOfTrader","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"orderCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"orderDependency","outputs":[{"internalType":"bytes32","name":"parentChildDigest1","type":"bytes32"},{"internalType":"bytes32","name":"parentChildDigest2","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"orderOfDigest","outputs":[{"internalType":"uint16","name":"leverageTDR","type":"uint16"},{"internalType":"uint16","name":"brokerFeeTbps","type":"uint16"},{"internalType":"uint24","name":"iPerpetualId","type":"uint24"},{"internalType":"address","name":"traderAddr","type":"address"},{"internalType":"uint32","name":"executionTimestamp","type":"uint32"},{"internalType":"address","name":"brokerAddr","type":"address"},{"internalType":"uint32","name":"submittedTimestamp","type":"uint32"},{"internalType":"uint32","name":"flags","type":"uint32"},{"internalType":"uint32","name":"iDeadline","type":"uint32"},{"internalType":"address","name":"executorAddr","type":"address"},{"internalType":"int128","name":"fAmount","type":"int128"},{"internalType":"int128","name":"fLimitPrice","type":"int128"},{"internalType":"int128","name":"fTriggerPrice","type":"int128"},{"internalType":"bytes","name":"brokerSignature","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"orderSignature","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"perpManager","outputs":[{"internalType":"contract IPerpetualManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"perpetualId","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_numElements","type":"uint256"}],"name":"pollRange","outputs":[{"components":[{"internalType":"uint24","name":"iPerpetualId","type":"uint24"},{"internalType":"int128","name":"fLimitPrice","type":"int128"},{"internalType":"uint16","name":"leverageTDR","type":"uint16"},{"internalType":"uint32","name":"executionTimestamp","type":"uint32"},{"internalType":"uint32","name":"flags","type":"uint32"},{"internalType":"uint32","name":"iDeadline","type":"uint32"},{"internalType":"address","name":"brokerAddr","type":"address"},{"internalType":"int128","name":"fTriggerPrice","type":"int128"},{"internalType":"int128","name":"fAmount","type":"int128"},{"internalType":"bytes32","name":"parentChildDigest1","type":"bytes32"},{"internalType":"address","name":"traderAddr","type":"address"},{"internalType":"bytes32","name":"parentChildDigest2","type":"bytes32"},{"internalType":"uint16","name":"brokerFeeTbps","type":"uint16"},{"internalType":"bytes","name":"brokerSignature","type":"bytes"},{"internalType":"address","name":"callbackTarget","type":"address"}],"internalType":"struct IClientOrder.ClientOrder[]","name":"orders","type":"tuple[]"},{"internalType":"bytes32[]","name":"orderHashes","type":"bytes32[]"},{"internalType":"uint32[]","name":"submittedTs","type":"uint32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint24","name":"iPerpetualId","type":"uint24"},{"internalType":"int128","name":"fLimitPrice","type":"int128"},{"internalType":"uint16","name":"leverageTDR","type":"uint16"},{"internalType":"uint32","name":"executionTimestamp","type":"uint32"},{"internalType":"uint32","name":"flags","type":"uint32"},{"internalType":"uint32","name":"iDeadline","type":"uint32"},{"internalType":"address","name":"brokerAddr","type":"address"},{"internalType":"int128","name":"fTriggerPrice","type":"int128"},{"internalType":"int128","name":"fAmount","type":"int128"},{"internalType":"bytes32","name":"parentChildDigest1","type":"bytes32"},{"internalType":"address","name":"traderAddr","type":"address"},{"internalType":"bytes32","name":"parentChildDigest2","type":"bytes32"},{"internalType":"uint16","name":"brokerFeeTbps","type":"uint16"},{"internalType":"bytes","name":"brokerSignature","type":"bytes"},{"internalType":"address","name":"callbackTarget","type":"address"}],"internalType":"struct IClientOrder.ClientOrder[]","name":"_orders","type":"tuple[]"},{"internalType":"bytes[]","name":"_signatures","type":"bytes[]"}],"name":"postOrders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_executor","type":"address"}],"name":"removeExecutor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_gasLimit","type":"uint32"}],"name":"setCallbackGasLimit","outputs":[],"stateMutability":"payable","type":"function"}]
Deployed Bytecode
0x6080604052600436106101af575f3560e01c806379b8783c116100e7578063b67f761311610087578063da5c207811610062578063da5c2078146105a5578063e568264e146105e0578063e905f3d11461060c578063fe2912061461061f575f80fd5b8063b67f76131461053f578063cccc2ede14610552578063d770f16614610567575f80fd5b806395fafddb116100c257806395fafddb146104ae57806399578534146104da578063a4eb718c1461050d578063a9a5e49c14610520575f80fd5b806379b8783c1461042b57806382227d871461044a5780639023dc5b14610475575f80fd5b806324f74697116101525780635c0c05531161012d5780635c0c05531461037e57806361fe71c61461039d57806367be6a64146103cb578063787588b914610417575f80fd5b806324f74697146102f657806327b62ad91461032657806346423aa714610352575f80fd5b80631f5a0bbe1161018d5780631f5a0bbe1461027357806320ef8157146102885780632453ffa8146102cf57806324788429146102e3575f80fd5b80630ee27e34146101b35780631a604cba146101fa5780631e8dd78a1461022c575b5f80fd5b3480156101be575f80fd5b506101e76101cd3660046146ae565b6001600160a01b03165f9081526002602052604090205490565b6040519081526020015b60405180910390f35b348015610205575f80fd5b505f5461021990600160201b900460070b81565b60405160079190910b81526020016101f1565b348015610237575f80fd5b5061025e6102463660046146d0565b60056020525f90815260409020805460019091015482565b604080519283526020830191909152016101f1565b6102866102813660046146ae565b61063e565b005b348015610293575f80fd5b506102bb7f0000000000000000000000000000000000000000000000000000000000030d4181565b60405162ffffff90911681526020016101f1565b3480156102da575f80fd5b506006546101e7565b6102866102f13660046146ae565b6107a0565b348015610301575f80fd5b505f546103119063ffffffff1681565b60405163ffffffff90911681526020016101f1565b348015610331575f80fd5b506103456103403660046146e7565b6108ea565b6040516101f191906148b1565b34801561035d575f80fd5b5061037161036c3660046146d0565b6109e5565b6040516101f191906148d7565b348015610389575f80fd5b506101e76103983660046146d0565b610b40565b3480156103a8575f80fd5b506103bc6103b73660046148fd565b610b5f565b6040516101f193929190614956565b3480156103d6575f80fd5b506103ff6103e53660046146d0565b60096020525f90815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016101f1565b348015610422575f80fd5b506001546101e7565b348015610436575f80fd5b506101e76104453660046149c3565b610d90565b348015610455575f80fd5b506101e76104643660046146d0565b60076020525f908152604090205481565b348015610480575f80fd5b5061049461048f3660046146d0565b610dbb565b6040516101f19e9d9c9b9a999897969594939291906149ed565b3480156104b9575f80fd5b506104cd6104c83660046146e7565b610eef565b6040516101f19190614ac3565b3480156104e5575f80fd5b506103ff7f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f81565b61028661051b366004614ad5565b610f7d565b34801561052b575f80fd5b5061028661053a366004614b39565b61106a565b61028661054d366004614ba0565b61115c565b34801561055d575f80fd5b506101e7600a5481565b348015610572575f80fd5b506105956105813660046146ae565b60086020525f908152604090205460ff1681565b60405190151581526020016101f1565b3480156105b0575f80fd5b506103ff6105bf3660046146d0565b5f90815260036020526040902054600160381b90046001600160a01b031690565b3480156105eb575f80fd5b506105ff6105fa3660046146d0565b611793565b6040516101f19190614c67565b61028661061a366004614c79565b61182a565b34801561062a575f80fd5b506104cd6106393660046148fd565b611bcd565b7f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b03166340239dc56040518163ffffffff1660e01b8152600401602060405180830381865afa15801561069a573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106be9190614cdc565b6001600160a01b0316336001600160a01b0316146107155760405162461bcd60e51b815260206004820152600f60248201526e6f6e6c79206f622d666163746f727960881b60448201526064015b60405180910390fd5b6001600160a01b0381165f9081526008602052604090205460ff161561077d5760405162461bcd60e51b815260206004820152601260248201527f616c726561647920617574686f72697a65640000000000000000000000000000604482015260640161070c565b6001600160a01b03165f908152600860205260409020805460ff19166001179055565b7f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b03166340239dc56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107fc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108209190614cdc565b6001600160a01b0316336001600160a01b0316146108725760405162461bcd60e51b815260206004820152600f60248201526e6f6e6c79206f622d666163746f727960881b604482015260640161070c565b6001600160a01b0381165f9081526008602052604090205460ff166108ca5760405162461bcd60e51b815260206004820152600e60248201526d1b9bdd08185d5d1a1bdc9a5e995960921b604482015260640161070c565b6001600160a01b03165f908152600860205260409020805460ff19169055565b60608167ffffffffffffffff81111561090557610905614cf7565b60405190808252806020026020018201604052801561093e57816020015b61092b6145d6565b8152602001906001900390816109235790505b506001600160a01b0385165f9081526002602052604081209192505b838110156109dc57815461096e8683614d1f565b10156109d4575f826109808784614d1f565b8154811061099057610990614d32565b905f5260205f20015490506109b560035f8381526020019081526020015f2082611c39565b8483815181106109c7576109c7614d32565b6020026020010181905250505b60010161095a565b50509392505050565b604051631625bfef60e31b8152600481018290525f907f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b03169063b12dff7890602401602060405180830381865afa158015610a4a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a6e9190614d46565b15610a7a57505f919050565b604051634bbec66760e01b8152600481018390527f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b031690634bbec66790602401602060405180830381865afa158015610add573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b019190614d46565b15610b0e57506001919050565b5f82815260036020526040902054600160381b90046001600160a01b0316610b3857506003919050565b506002919050565b60018181548110610b4f575f80fd5b5f91825260209091200154905081565b606080805f610b6e8587614d1f565b600654909150811115610b8057506006545b8467ffffffffffffffff811115610b9957610b99614cf7565b604051908082528060200260200182016040528015610bd257816020015b610bbf6145d6565b815260200190600190039081610bb75790505b5093508467ffffffffffffffff811115610bee57610bee614cf7565b604051908082528060200260200182016040528015610c17578160200160208202803683370190505b5092508467ffffffffffffffff811115610c3357610c33614cf7565b604051908082528060200260200182016040528015610c5c578160200160208202803683370190505b509150855b81811015610d87575f610c748883614d65565b905060068281548110610c8957610c89614d32565b905f5260205f200154858281518110610ca457610ca4614d32565b602002602001018181525050610cfc60035f878481518110610cc857610cc8614d32565b602002602001015181526020019081526020015f20868381518110610cef57610cef614d32565b6020026020010151611c39565b868281518110610d0e57610d0e614d32565b602002602001018190525060035f868381518110610d2e57610d2e614d32565b602002602001015181526020019081526020015f2060010160149054906101000a900463ffffffff16848281518110610d6957610d69614d32565b63ffffffff9092166020928302919091019091015250600101610c61565b50509250925092565b6002602052815f5260405f208181548110610da9575f80fd5b905f5260205f20015f91509150505481565b600360208190525f918252604090912080546001820154600283015493830154600484015460058501805461ffff808716986201000088049091169762ffffff600160201b890416976001600160a01b03600160381b820481169863ffffffff600160d81b90930483169881831698600160a01b8304851698600160c01b8404861698600160e01b9094049095169690931694600f82810b95600160801b909304810b94900b92909190610e6e90614d78565b80601f0160208091040260200160405190810160405280929190818152602001828054610e9a90614d78565b8015610ee55780601f10610ebc57610100808354040283529160200191610ee5565b820191905f5260205f20905b815481529060010190602001808311610ec857829003601f168201915b505050505090508e565b6060610f75838360025f886001600160a01b03166001600160a01b031681526020019081526020015f20805480602002602001604051908101604052809291908181526020018280548015610f6157602002820191905f5260205f20905b815481526020019060010190808311610f4d575b5050505050611db69092919063ffffffff16565b949350505050565b7f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b03166340239dc56040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fd9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ffd9190614cdc565b6001600160a01b0316336001600160a01b03161461104f5760405162461bcd60e51b815260206004820152600f60248201526e6f6e6c79206f622d666163746f727960881b604482015260640161070c565b5f805463ffffffff191663ffffffff92909216919091179055565b821580159061107857508281145b6110b65760405162461bcd60e51b815260206004820152600f60248201526e0c2e4e4c2f2e640dad2e6dac2e8c6d608b1b604482015260640161070c565b6110be611e94565b5f5b838110156111555761114c8585838181106110dd576110dd614d32565b90506020028101906110ef9190614db0565b84848481811061110157611101614d32565b90506020028101906111139190614dcf565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250611fe192505050565b506001016110c0565b5050505050565b5f87815260036020818152604080842081516101c081018352815461ffff8082168352620100008204169482019490945262ffffff600160201b850416928101929092526001600160a01b03600160381b84048116606084015263ffffffff600160d81b90940484166080840152600182015480821660a0850152600160a01b8104851660c0850152600160c01b8104851660e0850152600160e01b9004909316610100830152600281015490921661012082015291810154600f81810b610140850152600160801b909104810b6101608401526004820154900b6101808301526005810180546101a08401919061125390614d78565b80601f016020809104026020016040519081016040528092919081815260200182805461127f90614d78565b80156112ca5780601f106112a1576101008083540402835291602001916112ca565b820191905f5260205f20905b8154815290600101906020018083116112ad57829003601f168201915b5050505050815250509050806040015162ffffff167f0000000000000000000000000000000000000000000000000000000000030d4162ffffff16146113445760405162461bcd60e51b815260206004820152600f60248201526e1bdc99195c881b9bdd08199bdd5b99608a1b604482015260640161070c565b604051633bb510c960e01b81526001600160a01b037f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f1690633bb510c99034906113bc907f0000000000000000000000000000000000000000000000000000000000030d41908a908a908a908a905f90600401614f08565b5f604051808303818588803b1580156113d3575f80fd5b505af11580156113e5573d5f803e3d5ffd5b50505050506113f2611e94565b5f8054600160201b900460070b811315611420575f5461141b90600160201b900460070b614f4d565b61142d565b5f54600160201b900460070b5b90508160c0015163ffffffff168167ffffffffffffffff161015611458575060c081015163ffffffff165b5f8054600160201b900460070b12806114a6575061149960ff7f000000000000000000000000000000000000000000000000000000000000000a1682614f72565b67ffffffffffffffff1642105b156115525781606001516001600160a01b03167f0000000000000000000000000000000000000000000000000000000000030d4162ffffff167faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a08b6040516115459181526040602082018190526015908201527f63616e63656c2064656c61792072657175697265640000000000000000000000606082015260800190565b60405180910390a3611788565b81606001516001600160a01b0316336001600160a01b03161415801561160a57506060820151604051635fec5d0b60e01b81526001600160a01b0391821660048201523360248201527f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f90911690635fec5d0b90604401602060405180830381865afa1580156115e4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116089190614d46565b155b156116d5575f61167961163e847f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f846128d8565b8a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250612a1b92505050565b905082606001516001600160a01b0316816001600160a01b0316146116d35760405162461bcd60e51b815260206004820152601060248201526f3a3930b232b91036bab9ba1039b4b3b760811b604482015260640161070c565b505b604051637200c7c560e01b815262ffffff7f0000000000000000000000000000000000000000000000000000000000030d41166004820152602481018a90527f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b031690637200c7c5906044015f604051808303815f87803b15801561175f575f80fd5b505af1158015611771573d5f803e3d5ffd5b5050505061177e89612a35565b611788895f612c43565b505050505050505050565b60046020525f9081526040902080546117ab90614d78565b80601f01602080910402602001604051908101604052809291908181526020018280546117d790614d78565b80156118225780601f106117f957610100808354040283529160200191611822565b820191905f5260205f20905b81548152906001019060200180831161180557829003601f168201915b505050505081565b85806118645760405162461bcd60e51b81526020600482015260096024820152686e6f206f726465727360b81b604482015260640161070c565b5f60035f8a8a5f81811061187a5761187a614d32565b9050602002013581526020019081526020015f2060010160149054906101000a900463ffffffff1663ffffffff1690505f60035f8b8b5f8181106118c0576118c0614d32565b602090810292909201358352508101919091526040015f90812054600160d81b900463ffffffff1691508183116118f757816118f9565b825b90505f428210611909575f611913565b6119138242614d65565b905060015b858110156119e75760035f8e8e8481811061193557611935614d32565b9050602002013581526020019081526020015f2060010160149054906101000a900463ffffffff1663ffffffff16945060035f8e8e8481811061197a5761197a614d32565b602090810292909201358352508101919091526040015f2054600160d81b900463ffffffff1693508385116119af57836119b1565b845b92505f4284106119c1575f6119cb565b6119cb8442614d65565b90508083106119da57806119dc565b825b925050600101611918565b508715611a9557604051633bb510c960e01b81526001600160a01b037f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f1690633bb510c9903490611a66907f0000000000000000000000000000000000000000000000000000000000030d41908e908e908e908e908a90600401614f08565b5f604051808303818588803b158015611a7d575f80fd5b505af1158015611a8f573d5f803e3d5ffd5b50505050505b604051637a02797760e11b815262ffffff7f0000000000000000000000000000000000000000000000000000000000030d411660048201527f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b03169063f404f2ee90602401602060405180830381865afa158015611b1c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b409190614f9a565b821115611b825760405162461bcd60e51b815260206004820152601060248201526f6f75746461746564206f7261636c657360801b604482015260640161070c565b611b8a611e94565b5f5b85811015611bbe57611bb68d8d83818110611ba957611ba9614d32565b905060200201358c612d4c565b600101611b8c565b50505050505050505050505050565b6060611c3083836001805480602002602001604051908101604052809291908181526020018280548015610f6157602002820191905f5260205f2090815481526020019060010190808311610f4d575050505050611db69092919063ffffffff16565b90505b92915050565b611c416145d6565b600183015463ffffffff600160c01b8204166080830152835462ffffff600160201b820416835261ffff620100008204166101808401526001600160a01b03600160381b90910481166101408401521660c0820152600583018054611ca590614d78565b80601f0160208091040260200160405190810160405280929190818152602001828054611cd190614d78565b8015611d1c5780601f10611cf357610100808354040283529160200191611d1c565b820191905f5260205f20905b815481529060010190602001808311611cff57829003601f168201915b50505050506101a08201526003830154600f81810b610100840152600160801b909104810b602080840191909152600485015490910b60e0830152835461ffff8116604080850191909152600195860154600160e01b900463ffffffff90811660a0860152600160d81b90920490911660608401525f84815260058084529181208054610120860152949052905291015461016082015290565b60608167ffffffffffffffff811115611dd157611dd1614cf7565b604051908082528060200260200182016040528015611dfa578160200160208202803683370190505b5090505f5b82811015611e8c57845181611e148587614fb1565b611e1e9190614d1f565b1015611e75578481611e308587614fb1565b611e3a9190614d1f565b81518110611e4a57611e4a614d32565b6020026020010151828281518110611e6457611e64614d32565b602002602001018181525050611e7a565b611e8c565b80611e8481614fc8565b915050611dff565b509392505050565b604051630f971fe360e31b815262ffffff7f0000000000000000000000000000000000000000000000000000000000030d411660048201525f907f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b031690637cb8ff1890602401602060405180830381865afa158015611f1d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f419190614d46565b5f8054919250600160201b90910460070b138015611f5c5750805b15611f9a57611f6a42614f4d565b5f805467ffffffffffffffff92909216600160201b026bffffffffffffffff000000001990921691909117905550565b5f8054600160201b900460070b128015611fb2575080155b15611fde575f80546bffffffffffffffff000000001916600160201b4267ffffffffffffffff16021790555b50565b5f611fef6020840184614fe0565b62ffffff167f0000000000000000000000000000000000000000000000000000000000030d4162ffffff161461205a5760405162461bcd60e51b815260206004820152601060248201526f77726f6e67206f7264657220626f6f6b60801b604482015260640161070c565b5f61206d610160850161014086016146ae565b6001600160a01b0316036120b45760405162461bcd60e51b815260206004820152600e60248201526d34b73b30b634b216ba3930b232b960911b604482015260640161070c565b6120c661012084016101008501615002565b600f0b5f036121085760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a5908185b5bdd5b9d60921b604482015260640161070c565b4261211960c0850160a08601614ad5565b63ffffffff161161215f5760405162461bcd60e51b815260206004820152601060248201526f696e76616c69642d646561646c696e6560801b604482015260640161070c565b61216f60c0840160a08501614ad5565b63ffffffff166121856080850160608601614ad5565b63ffffffff161080156121b857506121a04262093a80614d1f565b6121b06080850160608601614ad5565b63ffffffff16105b6121f65760405162461bcd60e51b815260206004820152600f60248201526e696e76616c6964206578656320747360881b604482015260640161070c565b61221461220960a0850160808601614ad5565b632000000016151590565b1561227b575f61222b610100850160e08601615002565b600f0b1361227b5760405162461bcd60e51b815260206004820152601560248201527f696e76616c696420747269676765722070726963650000000000000000000000604482015260640161070c565b5f61228584613616565b63ffffffff421660c0820152905061229c816137fa565b6122c8817f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f60016128d8565b915081846101200135141580156122e457508184610160013514155b6123305760405162461bcd60e51b815260206004820152601160248201527f6f726465722073656c662d6c696e6b6564000000000000000000000000000000604482015260640161070c565b61233a84836138ee565b5f61234d6101e086016101c087016146ae565b6001600160a01b03161415801561238357506123836123746101e086016101c087016146ae565b6001600160a01b03163b151590565b156123c65761239a6101e085016101c086016146ae565b5f83815260096020526040902080546001600160a01b0319166001600160a01b03929092169190911790555b6123d8610160850161014086016146ae565b6001600160a01b0316336001600160a01b0316141580156124bb57506001600160a01b037f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f16635fec5d0b612435610160870161014088016146ae565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152336024820152604401602060405180830381865afa158015612495573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124b99190614d46565b155b15612572575f6124cb8385612a1b565b90506001600160a01b0381166125175760405162461bcd60e51b8152602060048201526011602482015270696e76616c6964207369676e617475726560781b604482015260640161070c565b81606001516001600160a01b0316816001600160a01b0316146125705760405162461bcd60e51b8152602060048201526011602482015270696e76616c6964207369676e617475726560781b604482015260640161070c565b505b5f82815260036020526040902054600160381b90046001600160a01b0316156125cc5760405162461bcd60e51b815260206004820152600c60248201526b6f726465722d65786973747360a01b604482015260640161070c565b60608101516001600160a01b03165f908152600260205260409020546032116126295760405162461bcd60e51b815260206004820152600f60248201526e746f6f206d616e79206f726465727360881b604482015260640161070c565b604051634bbec66760e01b8152600481018390527f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b031690634bbec66790602401602060405180830381865afa15801561268c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126b09190614d46565b156126ee5760405162461bcd60e51b815260206004820152600e60248201526d1bdc99195c88195e1958dd5d195960921b604482015260640161070c565b604051631625bfef60e31b8152600481018390527f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b03169063b12dff7890602401602060405180830381865afa158015612751573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127759190614d46565b156127b35760405162461bcd60e51b815260206004820152600e60248201526d1bdc99195c8818d85b98d95b195960921b604482015260640161070c565b6127be828285613944565b6040516316b5668760e21b81526001600160a01b037f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f1690635ad59a1c9061282c9084907f000000000000000000000000000000000000000000000000000000000000000590600401615156565b5f604051808303815f87803b158015612843575f80fd5b505af1158015612855573d5f803e3d5ffd5b5061286c92505050610160850161014086016146ae565b6001600160a01b03166128826020860186614fe0565b62ffffff167fb8ace0e652054ba9160467efc4bd026f89b793c887035f7d88d7b7b0615c4c5f6128b860e0880160c089016146ae565b84866040516128c99392919061517b565b60405180910390a35092915050565b604080518082018252601781527f50657270657475616c205472616465204d616e6167657200000000000000000060209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f17c95922bd75dac3f3356997c3388ddf83efb4b0267c590453d691334c1c5bbb818401524660608201526001600160a01b0385166080808301919091528351808303909101815260a090910190925281519101205f908161299486613c04565b6040805160208082019590955280820192909252941515606080830191909152855180830390910181526080820186528051908401207f19457468657265756d205369676e6564204d6573736167653a0a33320000000060a083015260bc808301919091528551808303909101815260dc9091019094525082519201919091209392505050565b5f805f612a288585613d14565b91509150611e8c81613d7f565b80612a3d5750565b5f81815260036020908152604080832054600160381b90046001600160a01b03168352600290915281208054909103612a74575050565b5f5b8154811015612b175782828281548110612a9257612a92614d32565b905f5260205f20015403612b0f5781548290612ab090600190614d65565b81548110612ac057612ac0614d32565b905f5260205f200154828281548110612adb57612adb614d32565b905f5260205f20018190555081805480612af757612af76151ac565b600190038181905f5260205f20015f90559055603290505b600101612a76565b5f838152600360208190526040822080547fff00000000000000000000000000000000000000000000000000000000000000168155600181018390556002810180546001600160a01b03191690559081018290556004810180546fffffffffffffffffffffffffffffffff1916905590612b946005830182614650565b50505f8381526007602052604081205460068054919291612bb790600190614d65565b81548110612bc757612bc7614d32565b5f918252602080832090910154808352600790915260408083208590558783528220919091559050806006612bfd600185614d65565b81548110612c0d57612c0d614d32565b5f918252602090912001556006805480612c2957612c296151ac565b600190038181905f5260205f20015f905590555050505050565b5f82815260096020526040902080546001600160a01b031981169091556001600160a01b031680612c7357505050565b5f805463ffffffff169003612c8757505050565b5f80546040516322ad65db60e01b81526004810186905284151560248201526001600160a01b038416916322ad65db9163ffffffff909116906044015f604051808303815f88803b158015612cda575f80fd5b5087f193505050508015612cec575060015b15612cf5575060015b5f54604080516001600160a01b0385168152831515602082015263ffffffff90921682820152517f1d4b3142757d5200ad5437c0348f6047f6f312b4ccadda70ecf72cd93ea1e2609181900360600190a150505050565b5f82815260036020818152604080842081516101c081018352815461ffff8082168352620100008204169482019490945262ffffff600160201b850416928101929092526001600160a01b03600160381b84048116606084015263ffffffff600160d81b90940484166080840152600182015480821660a0850152600160a01b8104851660c0850152600160c01b8104851660e0850152600160e01b9004909316610100830152600281015490921661012082015291810154600f81810b610140850152600160801b909104810b6101608401526004820154900b6101808301526005810180546101a084019190612e4390614d78565b80601f0160208091040260200160405190810160405280929190818152602001828054612e6f90614d78565b8015612eba5780601f10612e9157610100808354040283529160200191612eba565b820191905f5260205f20905b815481529060010190602001808311612e9d57829003601f168201915b5050509190925250505060608101519091506001600160a01b038116612f145760405162461bcd60e51b815260206004820152600f60248201526e1bdc99195c881b9bdd08199bdd5b99608a1b604482015260640161070c565b6001600160a01b038316610120830152612f2d84613f34565b15612f7a5760405162461bcd60e51b815260206004820152601260248201527f64706379206e6f742066756c66696c6c65640000000000000000000000000000604482015260640161070c565b5f8083610100015163ffffffff16421161356c57836080015163ffffffff16421015612fd95760405162461bcd60e51b815260206004820152600e60248201526d6578656320746f6f206561726c7960901b604482015260640161070c565b335f9081526008602052604090819020549051637b20b03960e01b81526001600160a01b037f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f1691637b20b0399161303b91889160ff909116906004016151c0565b6020604051808303815f875af1925050508015613075575060408051601f3d908101601f1916820190925261307291810190614d46565b60015b613424576130816151e3565b806308c379a00361341a5750613095615229565b806130a0575061341c565b6130df816040518060400160405280602081526020017f547261646520616d743e6d617820616d7420666f72207472616465722f414d4d8152506140af565b156131885760405163aa0a907960e01b81526001600160a01b037f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f169063aa0a9079906131529088907f000000000000000000000000000000000000000000000000000000000000000590600401615156565b5f604051808303815f87803b158015613169575f80fd5b505af115801561317b573d5f803e3d5ffd5b50505050600192506133ab565b6131b8816040518060400160405280600e81526020016d19195b185e481c995c5d5a5c995960921b8152506140af565b80613313575060e08501516310000000161580156133135750613203816040518060400160405280601081526020016f1b585c9ad95d081a5cc818db1bdcd95960821b8152506140af565b806132485750613248816040518060400160405280601481526020017f7472696767657220636f6e64206e6f74206d65740000000000000000000000008152506140af565b80613313575060e085015163400000001615801561331357506132a0816040518060400160405280601381526020017f747261646520697320636c6f7365206f6e6c79000000000000000000000000008152506140af565b806132db57506132db81604051806040016040528060138152602001721c1c9a58d948195e18d959591cc81b1a5b5a5d606a1b8152506140af565b806133135750613313816040518060400160405280601081526020016f6f75746461746564206f7261636c657360801b8152506140af565b15613332578060405162461bcd60e51b815260040161070c9190614c67565b613363816040518060400160405280600f81526020016e1bdc99195c8818d85b98d95b1b1959608a1b8152506140af565b806133995750613399816040518060400160405280600e81526020016d1bdc99195c88195e1958dd5d195960921b8152506140af565b156133a6575f92506133ab565b600192505b836001600160a01b03167f0000000000000000000000000000000000000000000000000000000000030d4162ffffff167faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a0898460405161340c9291906152b2565b60405180910390a3506135fa565b505b3d5f803e3d5ffd5b60405163aa0a907960e01b81526001600160a01b037f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f169063aa0a9079906134929088907f000000000000000000000000000000000000000000000000000000000000000590600401615156565b5f604051808303815f87803b1580156134a9575f80fd5b505af11580156134bb573d5f803e3d5ffd5b505050508061356157836001600160a01b03167f0000000000000000000000000000000000000000000000000000000000030d4162ffffff167faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a089604051613550918152604060208201819052601390820152721c1c9a58d948195e18d959591cc81b1a5b5a5d606a1b606082015260800190565b60405180910390a360019250613566565b600191505b506135fa565b826001600160a01b03167f0000000000000000000000000000000000000000000000000000000000030d4162ffffff167faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a0886040516135ed91815260406020820181905260089082015267646561646c696e6560c01b606082015260800190565b60405180910390a3600191505b61360486836140d3565b61360e8682612c43565b505050505050565b604080516101c0810182525f8082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e0820183905261010082018390526101208201839052610140820183905261016082018390526101808201929092526101a081019190915261369860a0830160808401614ad5565b63ffffffff1660e08201526136b06020830183614fe0565b62ffffff1660408201526136cc6101a0830161018084016152ca565b61ffff1660208201526136e7610160830161014084016146ae565b6001600160a01b0316606082015261370560e0830160c084016146ae565b6001600160a01b031660a08201526137216101a0830183614dcf565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505050506101a082015261376c61012083016101008401615002565b600f0b6101408201526137856040830160208401615002565b600f0b61016082015261379f610100830160e08401615002565b600f0b6101808201526137b860608301604084016152ca565b61ffff1681526137ce60c0830160a08401614ad5565b63ffffffff166101008201526137ea6080830160608401614ad5565b63ffffffff166080820152919050565b806101a00151515f0361380a5750565b5f613843613838837f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f61422e565b836101a00151612a1b565b90506001600160a01b0381166138905760405162461bcd60e51b8152602060048201526012602482015271696e76616c69642062726f6b65722073696760701b604482015260640161070c565b8160a001516001600160a01b0316816001600160a01b0316146138ea5760405162461bcd60e51b8152602060048201526012602482015271696e76616c69642062726f6b65722073696760701b604482015260640161070c565b5050565b6101208201351580156139045750610160820135155b1561390d575050565b60408051808201825261012084013581526101609093013560208085019182525f9384526005905291209151825551600190910155565b5f838152600360208181526040928390208551815492870151948701516060880151608089015161ffff93841663ffffffff1990961695909517620100009390971692909202959095177fffffffffff0000000000000000000000000000000000000000000000ffffffff16600160201b62ffffff909616959095027fffffffffff0000000000000000000000000000000000000000ffffffffffffff1694909417600160381b6001600160a01b03958616021763ffffffff60d81b1916600160d81b63ffffffff9384160217815560a086015160018201805460c089015160e08a01516101008b015194891677ffffffffffffffffffffffffffffffffffffffffffffffff1990931692909217600160a01b918716919091021777ffffffffffffffffffffffffffffffffffffffffffffffff16600160c01b918616919091027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1617600160e01b9290941691909102929092179091556101208501516002820180546001600160a01b03191691909416179092556101408401516101608501516fffffffffffffffffffffffffffffffff918216600160801b9183169190910217918301919091556101808401516004830180546fffffffffffffffffffffffffffffffff1916919092161790556101a08301518391906005820190613b489082615330565b5050505f838152600460205260409020613b628282615330565b50506001805480820182555f8281527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf69091018490556060909201516001600160a01b0316825260026020908152604080842080548085018255908552828520018590556006805493840181557ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f909301859055915493835260079052902055565b5f807fe5599e89712387846e6878c8cac8abdf5d6051ccbc4a6cfa5efe389720300ec85f1b8360400151846020015185606001518660a001518761014001518861016001518961018001518a61010001518b60e001518c5f01518d60800151604051602001613cf59c9b9a999897969594939291909b8c5262ffffff9a909a1660208c015261ffff98891660408c01526001600160a01b0397881660608c01529590961660808a0152600f93840b60a08a015291830b60c089015290910b60e087015263ffffffff908116610100870152918216610120860152909116610140840152166101608201526101800190565b60408051601f1981840301815291905280516020909101209392505050565b5f808251604103613d48576020830151604084015160608501515f1a613d3c87828585614376565b94509450505050613d78565b8251604003613d715760208301516040840151613d6686838361445b565b935093505050613d78565b505f905060025b9250929050565b5f816004811115613d9257613d926148c3565b03613d9a5750565b6001816004811115613dae57613dae6148c3565b03613dfb5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161070c565b6002816004811115613e0f57613e0f6148c3565b03613e5c5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161070c565b6003816004811115613e7057613e706148c3565b03613ec85760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161070c565b6004816004811115613edc57613edc6148c3565b03611fde5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b606482015260840161070c565b5f818152600560205260408120805415613f5057505f92915050565b6001810154801580613fe45750604051631625bfef60e31b8152600481018290527f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b03169063b12dff7890602401602060405180830381865afa158015613fc0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613fe49190614d46565b806140715750604051634bbec66760e01b8152600481018290527f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b031690634bbec66790602401602060405180830381865afa15801561404d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906140719190614d46565b8061409757505f81815260036020526040902054600160381b90046001600160a01b0316155b156140a557505f9392505050565b5060019392505050565b5f81518351148015611c305750508051602091820120825192909101919091201490565b6140dc82612a35565b604051634bbec66760e01b8152600481018390527f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b031690634bbec66790602401602060405180830381865afa15801561413f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141639190614d46565b61420857604051637200c7c560e01b815262ffffff7f0000000000000000000000000000000000000000000000000000000000030d41166004820152602481018390527f000000000000000000000000ab7794ecd2c8e9decc6b577864b40ebf9204720f6001600160a01b031690637200c7c5906044015f604051808303815f87803b1580156141f1575f80fd5b505af1158015614203573d5f803e3d5ffd5b505050505b80156142175761421782614493565b505f90815260056020526040812081815560010155565b604080518082018252601781527f50657270657475616c205472616465204d616e6167657200000000000000000060209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f17c95922bd75dac3f3356997c3388ddf83efb4b0267c590453d691334c1c5bbb818401524660608201526001600160a01b0384166080808301919091528351808303909101815260a090910190925281519101205f90816142ea856144c8565b90505f8282604051602001614309929190918252602082015260400190565b60408051601f1981840301815282825280516020918201207f19457468657265756d205369676e6564204d6573736167653a0a33320000000084830152603c80850182905283518086039091018152605c90940190925282519201919091209091505b9695505050505050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156143ab57505f90506003614452565b8460ff16601b141580156143c357508460ff16601c14155b156143d357505f90506004614452565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614424573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b03811661444c575f60019250925050614452565b91505f90505b94509492505050565b5f806001600160ff1b0383168161447760ff86901c601b614d1f565b905061448587828885614376565b935093505050935093915050565b5f8061449e8361454c565b909250905081156144b3576144b3825f6140d3565b80156144c3576144c3815f6140d3565b505050565b5f807f1aae56290d242a9c761ca2ef80072ffe2a6171793ad9f88e04426b2acc5e730d5f1b836040015184602001518560600151866101000151604051602001613cf595949392919094855262ffffff93909316602085015261ffff9190911660408401526001600160a01b0316606083015263ffffffff16608082015260a00190565b5f81815260056020526040812081906145658154151590565b61457457505f93849350915050565b80545f81815260056020526040902060010154851461459057505f5b60018201546145a357945f945092505050565b6001808301545f8181526005602052604090209091015486146145cb5750945f945092505050565b909590945092505050565b604080516101e0810182525f8082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a08201526101c081019190915290565b50805461465c90614d78565b5f825580601f1061466b575050565b601f0160209004905f5260205f2090810190611fde91905b80821115614696575f8155600101614683565b5090565b6001600160a01b0381168114611fde575f80fd5b5f602082840312156146be575f80fd5b81356146c98161469a565b9392505050565b5f602082840312156146e0575f80fd5b5035919050565b5f805f606084860312156146f9575f80fd5b83356147048161469a565b95602085013595506040909401359392505050565b5f81518084525f5b8181101561473d57602081850181015186830182015201614721565b505f602082860101526020601f19601f83011685010191505092915050565b5f81518084526020808501808196508360051b810191508286015f5b858110156148a45782840389528151805162ffffff1685526101e0868201516147a588880182600f0b9052565b5060408281015161ffff169087015260608083015163ffffffff9081169188019190915260808084015182169088015260a0808401519091169087015260c0808301516001600160a01b03169087015260e080830151600f81900b8289015250506101008083015161481b82890182600f0b9052565b50506101208281015190870152610140808301516001600160a01b03169087015261016080830151908701526101808083015161ffff16908701526101a08083015181880183905261486f83890182614719565b925050506101c0808301519250614890818801846001600160a01b03169052565b509986019994505090840190600101614778565b5091979650505050505050565b602081525f611c30602083018461475c565b634e487b7160e01b5f52602160045260245ffd5b60208101600483106148f757634e487b7160e01b5f52602160045260245ffd5b91905290565b5f806040838503121561490e575f80fd5b50508035926020909101359150565b5f8151808452602080850194508084015f5b8381101561494b5781518752958201959082019060010161492f565b509495945050505050565b606081525f614968606083018661475c565b60208382038185015261497b828761491d565b848103604086015285518082528287019350908201905f5b818110156149b557845163ffffffff1683529383019391830191600101614993565b509098975050505050505050565b5f80604083850312156149d4575f80fd5b82356149df8161469a565b946020939093013593505050565b61ffff8f811682528e16602082015262ffffff8d1660408201526001600160a01b038c16606082015263ffffffff8b1660808201526001600160a01b038a1660a082015263ffffffff891660c082015263ffffffff881660e082015263ffffffff87166101008201526001600160a01b038616610120820152614a76610140820186600f0b9052565b614a86610160820185600f0b9052565b614a96610180820184600f0b9052565b6101c06101a08201525f614aae6101c0830184614719565b90509f9e505050505050505050505050505050565b602081525f611c30602083018461491d565b5f60208284031215614ae5575f80fd5b813563ffffffff811681146146c9575f80fd5b5f8083601f840112614b08575f80fd5b50813567ffffffffffffffff811115614b1f575f80fd5b6020830191508360208260051b8501011115613d78575f80fd5b5f805f8060408587031215614b4c575f80fd5b843567ffffffffffffffff80821115614b63575f80fd5b614b6f88838901614af8565b90965094506020870135915080821115614b87575f80fd5b50614b9487828801614af8565b95989497509550505050565b5f805f805f805f6080888a031215614bb6575f80fd5b87359650602088013567ffffffffffffffff80821115614bd4575f80fd5b818a0191508a601f830112614be7575f80fd5b813581811115614bf5575f80fd5b8b6020828501011115614c06575f80fd5b6020830198508097505060408a0135915080821115614c23575f80fd5b614c2f8b838c01614af8565b909650945060608a0135915080821115614c47575f80fd5b50614c548a828b01614af8565b989b979a50959850939692959293505050565b602081525f611c306020830184614719565b5f805f805f805f6080888a031215614c8f575f80fd5b873567ffffffffffffffff80821115614ca6575f80fd5b614cb28b838c01614af8565b909950975060208a01359150614cc78261469a565b90955060408901359080821115614c23575f80fd5b5f60208284031215614cec575f80fd5b81516146c98161469a565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b80820180821115611c3357611c33614d0b565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215614d56575f80fd5b815180151581146146c9575f80fd5b81810381811115611c3357611c33614d0b565b600181811c90821680614d8c57607f821691505b602082108103614daa57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f82356101de19833603018112614dc5575f80fd5b9190910192915050565b5f808335601e19843603018112614de4575f80fd5b83018035915067ffffffffffffffff821115614dfe575f80fd5b602001915036819003821315613d78575f80fd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b8183525f6020808501808196508560051b81019150845f5b878110156148a45782840389528135601e19883603018112614e72575f80fd5b8701858101903567ffffffffffffffff811115614e8d575f80fd5b803603821315614e9b575f80fd5b614ea6868284614e12565b9a87019a9550505090840190600101614e52565b8183525f60208085019450825f805b86811015614efc57823567ffffffffffffffff8116808214614ee9578384fd5b8952509683019691830191600101614ec9565b50959695505050505050565b62ffffff87168152608060208201525f614f26608083018789614e3a565b8281036040840152614f39818688614eba565b915050826060830152979650505050505050565b5f8160070b677fffffffffffffff198103614f6a57614f6a614d0b565b5f0392915050565b67ffffffffffffffff818116838216019080821115614f9357614f93614d0b565b5092915050565b5f60208284031215614faa575f80fd5b5051919050565b8082028115828204841417611c3357611c33614d0b565b5f60018201614fd957614fd9614d0b565b5060010190565b5f60208284031215614ff0575f80fd5b813562ffffff811681146146c9575f80fd5b5f60208284031215615012575f80fd5b813580600f0b81146146c9575f80fd5b805161ffff1682525f6101c06020830151615043602086018261ffff169052565b50604083015161505a604086018262ffffff169052565b50606083015161507560608601826001600160a01b03169052565b50608083015161508d608086018263ffffffff169052565b5060a08301516150a860a08601826001600160a01b03169052565b5060c08301516150c060c086018263ffffffff169052565b5060e08301516150d860e086018263ffffffff169052565b506101008381015163ffffffff1690850152610120808401516001600160a01b03169085015261014080840151600f81900b8287015250506101608084015161512582870182600f0b9052565b50506101808084015161513c82870182600f0b9052565b50506101a080840151828287015261436c83870182614719565b604081525f6151686040830185615022565b905061ffff831660208301529392505050565b6001600160a01b0384168152606060208201525f61519c6060830185615022565b9050826040830152949350505050565b634e487b7160e01b5f52603160045260245ffd5b604081525f6151d26040830185615022565b905082151560208301529392505050565b5f60033d11156151f95760045f803e505f5160e01c5b90565b601f8201601f1916810167ffffffffffffffff8111828210171561522257615222614cf7565b6040525050565b5f60443d10156152365790565b6040516003193d81016004833e81513d67ffffffffffffffff816024840111818411171561526657505050505090565b828501915081518181111561527e5750505050505090565b843d87010160208285010111156152985750505050505090565b6152a7602082860101876151fc565b509095945050505050565b828152604060208201525f610f756040830184614719565b5f602082840312156152da575f80fd5b813561ffff811681146146c9575f80fd5b601f8211156144c3575f81815260208120601f850160051c810160208610156153115750805b601f850160051c820191505b8181101561360e5782815560010161531d565b815167ffffffffffffffff81111561534a5761534a614cf7565b61535e816153588454614d78565b846152eb565b602080601f831160018114615391575f841561537a5750858301515b5f19600386901b1c1916600185901b17855561360e565b5f85815260208120601f198616915b828110156153bf578886015182559484019460019091019084016153a0565b50858210156153dc57878501515f19600388901b60f8161c191681555b5050505050600190811b0190555056fea264697066735822122022147029f0749012652faf2d5d7b71dcc0731a08f882287b4236245de32add2064736f6c63430008150033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.