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
|
|||||
---|---|---|---|---|---|---|---|---|---|
Submit Order Wit... | 21702292 | 17 hrs ago | IN | 0.0382216 ETH | 0.00007044 | ||||
Submit Order | 21673645 | 43 hrs ago | IN | 0.05987147 ETH | 0.00000857 | ||||
Cancel Order | 21673589 | 43 hrs ago | IN | 0 ETH | 0.00000039 | ||||
Submit Order | 21656721 | 2 days ago | IN | 0.11063 ETH | 0.00000857 | ||||
Submit Order | 21656570 | 2 days ago | IN | 0.0001 ETH | 0.00000345 | ||||
Cancel Order | 21655150 | 2 days ago | IN | 0 ETH | 0.00000071 | ||||
Submit Order | 21654767 | 2 days ago | IN | 0.05950243 ETH | 0.00000882 | ||||
Cancel Order | 21654759 | 2 days ago | IN | 0 ETH | 0.00000065 | ||||
Cancel Order | 21654559 | 2 days ago | IN | 0 ETH | 0.00000078 | ||||
Cancel Order | 21654472 | 2 days ago | IN | 0 ETH | 0.00000078 | ||||
Submit Order | 21654232 | 2 days ago | IN | 0.0594449 ETH | 0.00000877 | ||||
Submit Order | 21653972 | 2 days ago | IN | 0.05987253 ETH | 0.00000882 | ||||
Submit Order | 21568523 | 5 days ago | IN | 0.06291937 ETH | 0.00000882 | ||||
Submit Order | 21555842 | 6 days ago | IN | 0.06301957 ETH | 0.00000882 | ||||
Submit Order Wit... | 21490653 | 8 days ago | IN | 0.06477208 ETH | 0.00005021 | ||||
Cancel Order | 21469271 | 9 days ago | IN | 0 ETH | 0.00000039 | ||||
Cancel Order | 21469271 | 9 days ago | IN | 0 ETH | 0.00000087 | ||||
Submit Order | 21469193 | 9 days ago | IN | 0.0001 ETH | 0.00000433 | ||||
Submit Order | 21341774 | 14 days ago | IN | 0.0001 ETH | 0.00000365 | ||||
Submit Order Wit... | 21340650 | 14 days ago | IN | 0.0003 ETH | 0.00000961 | ||||
Submit Order | 21262294 | 17 days ago | IN | 0.0001 ETH | 0.00000057 | ||||
Submit Order | 21262282 | 17 days ago | IN | 0.0001 ETH | 0.00000365 | ||||
Submit Order | 21262174 | 17 days ago | IN | 0.0001 ETH | 0.00000433 | ||||
Submit Order | 21172695 | 20 days ago | IN | 0.0001 ETH | 0.00000365 | ||||
Submit Order | 21172548 | 20 days ago | IN | 0.0001 ETH | 0.00000433 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
21702292 | 17 hrs ago | 0.0382216 ETH | ||||
21673645 | 43 hrs ago | 0.0001 ETH | ||||
21673645 | 43 hrs ago | 0.0001 ETH | ||||
21673645 | 43 hrs ago | 0.05967147 ETH | ||||
21656721 | 2 days ago | 0.0001 ETH | ||||
21656721 | 2 days ago | 0.0001 ETH | ||||
21656721 | 2 days ago | 0.11043 ETH | ||||
21656570 | 2 days ago | 0.0001 ETH | ||||
21654767 | 2 days ago | 0.0001 ETH | ||||
21654767 | 2 days ago | 0.0001 ETH | ||||
21654767 | 2 days ago | 0.05930243 ETH | ||||
21654232 | 2 days ago | 0.0001 ETH | ||||
21654232 | 2 days ago | 0.0001 ETH | ||||
21654232 | 2 days ago | 0.0592449 ETH | ||||
21653972 | 2 days ago | 0.0001 ETH | ||||
21653972 | 2 days ago | 0.0001 ETH | ||||
21653972 | 2 days ago | 0.05967253 ETH | ||||
21568523 | 5 days ago | 0.0001 ETH | ||||
21568523 | 5 days ago | 0.0001 ETH | ||||
21568523 | 5 days ago | 0.06271937 ETH | ||||
21555842 | 6 days ago | 0.0001 ETH | ||||
21555842 | 6 days ago | 0.0001 ETH | ||||
21555842 | 6 days ago | 0.06281957 ETH | ||||
21490653 | 8 days ago | 0.0001 ETH | ||||
21490653 | 8 days ago | 0.0001 ETH |
Loading...
Loading
Contract Name:
OrderBook
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.22; import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; import '@openzeppelin/contracts/utils/Address.sol'; import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import '@openzeppelin/contracts/utils/math/SafeCast.sol'; import './utils/AddressStorage.sol'; import './Store.sol'; import './utils/interfaces/IStore.sol'; import './utils/TradingValidator.sol'; import './PositionManager.sol'; import './interfaces/IReferralStorage.sol'; import './utils/Governable.sol'; /** * @title OrderBook * @notice Implementation of order related logic, i.e. submitting orders / cancelling them */ contract OrderBook is Governable { // Libraries using EnumerableSet for EnumerableSet.UintSet; using Address for address payable; using SafeCast for uint256; // Constants uint256 public constant UNIT = 10 ** 18; uint256 public constant BPS_DIVIDER = 10000; uint256 public constant MAX_TRAILING_STOP_PERCENTAGE = 2000; // Order struct struct OrderDetail{ uint8 orderType; // 0 = market, 1 = limit, 2 = stop , 3 = trailing stop bool isReduceOnly; // Whether the order is reduce-only uint96 price; // The order's price if its a trigger or protected order uint32 expiry; // block.timestamp at which the order expires uint32 cancelOrderId; // orderId to cancel when this order executes uint64 executionFee; //Fee paid with native token for keeper's execution uint16 trailingStopPercentage; //Trailing stop percentage in bps e.g. 200 for %2 } struct Order { address user; // user that submitted the order uint96 margin; // Collateral tied to this order. In wei address asset; // Asset address, e.g. address(0) for ETH bytes10 market; // Market this order was submitted on bool isLong; // Whether the order is a buy or sell order uint96 size; // Order size (margin * leverage). In wei uint96 fee; // Fee amount paid. In wei uint32 timestamp; // block.timestamp at which the order was submitted uint32 orderId; // incremental order id OrderDetail orderDetail; } uint32 public oid; // incremental order id mapping(uint32 => Order) private orders; // order id => Order mapping(address => EnumerableSet.UintSet) private userOrderIds; // user => [order ids..] EnumerableSet.UintSet private marketOrderIds; // [order ids..] EnumerableSet.UintSet private triggerOrderIds; // [order ids..] uint256 public maxMarketOrderTTL = 5 minutes; // duration until market orders expire uint256 public maxTriggerOrderTTL = 180 days; // duration until trigger orders expire uint64 public orderExecutionFee; // fee with native token required for the execution of the order by keepers bool public areNewOrdersPaused; bool public isProcessingPaused; bytes32 public ethSignedMessageHash; // Message Hash equivalent of UI sign message of enable orders, remix.ethereum.org can be used for message hash mapping(address => bool) public whitelistedFundingAccount; // accounts authorized to open positions on behalf of another user mapping(address => bool) public approvedAccounts; // accounts approved to submit order // Events // Order of function / event params: id, user, asset, market event OrderCreated( uint32 indexed orderId, address indexed user, address indexed asset, bytes10 market, uint96 margin, uint96 size, uint96 price, uint96 fee, bool isLong, uint8 orderType, bool isReduceOnly, uint32 expiry, uint32 cancelOrderId, address fundingAccount, uint64 executionFee, uint16 trailingStopPercentage ); event OrderCancelled(uint32 indexed orderId, address indexed user, address executionFeeReceiver, string reason); event AccountApproved(address indexed user, bool signed, bool byGov); event EthSignedMessageHashUpdated(bytes32 ethSignedMessageHash); event WhitelistedFundingAccountUpdated(address indexed account, bool isActive); event MaxMarketOrderTTLUpdated(uint256 maxMarketOrderTTL); event MaxTriggerOrderTTLUpdated(uint256 maxTriggerOrderTTL); event OrderExecutionFeeUpdated(uint64 orderExecutionFee); event Link(address store, address tradingValidator, address referralStorage, address executor, address positionManager); event NewOrdersPaused(bool orderPaused); event ProcessingPaused(bool processingPaused); event AddOrder(uint32 indexed orderId, uint8 orderType); event RemoveOrder(uint32 indexed orderId, uint8 orderType); // Contracts AddressStorage public immutable addressStorage; Store public store; PositionManager public positionManager; TradingValidator public tradingValidator; IReferralStorage public referralStorage; address public executorAddress; /// @dev Reverts if order processing is paused modifier ifNotPaused() { require(!areNewOrdersPaused, '!paused'); _; } /// @dev Only callable by Executor contract modifier onlyExecutor() { require(msg.sender == executorAddress, "!unauthorized"); _; } /// @dev Only callable by PositionManager contract modifier onlyPositionManager() { require(msg.sender == address(positionManager), "!unauthorized"); _; } /// @dev Initializes addressStorage address constructor(AddressStorage _addressStorage) { addressStorage = _addressStorage; } /// @notice Set EthSignedMessageHash /// @dev Only callable by governance /// @param _messageHash Message Hash,remix.ethereum.org can be used function setEthSignedMessageHash(bytes32 _messageHash) external onlyGov { ethSignedMessageHash = _messageHash; emit EthSignedMessageHashUpdated(_messageHash); } /// @notice Set whitelisted Funding Account /// @dev Only callable by governance /// @param _account accounts authorized to open positions on behalf of another user /// @param _isActive whether account is active function setWhitelistedFundingAccount(address _account, bool _isActive) external onlyGov { whitelistedFundingAccount[_account] = _isActive; emit WhitelistedFundingAccountUpdated(_account,_isActive); } /// @notice Disable submitting new orders /// @dev Only callable by governance function setAreNewOrdersPaused(bool _areNewOrdersPaused) external onlyGov { areNewOrdersPaused = _areNewOrdersPaused; emit NewOrdersPaused(areNewOrdersPaused); } /// @notice Disable processing new orders /// @dev Only callable by governance function setIsProcessingPaused(bool _isProcessingPaused) external onlyGov { isProcessingPaused = _isProcessingPaused; emit ProcessingPaused(isProcessingPaused); } /// @notice Set duration until market orders expire /// @dev Only callable by governance /// @param _maxMarketOrderTTL Duration in seconds function setMaxMarketOrderTTL(uint256 _maxMarketOrderTTL) external onlyGov { require(_maxMarketOrderTTL > 0, '!gt_zero'); // greater than zero require(_maxMarketOrderTTL < maxTriggerOrderTTL, '!lt_triggerttl'); // must be less than trigger ttl value maxMarketOrderTTL = _maxMarketOrderTTL; emit MaxMarketOrderTTLUpdated(_maxMarketOrderTTL); } /// @notice Set duration until trigger orders expire /// @dev Only callable by governance /// @param _maxTriggerOrderTTL Duration in seconds function setMaxTriggerOrderTTL(uint256 _maxTriggerOrderTTL) external onlyGov { require(_maxTriggerOrderTTL > 0, '!gtzero'); // greater than zero require(_maxTriggerOrderTTL > maxMarketOrderTTL, '!gt_marketttl'); // must be greater than market ttl value maxTriggerOrderTTL = _maxTriggerOrderTTL; emit MaxTriggerOrderTTLUpdated(_maxTriggerOrderTTL); } /// @notice Set keeper gas fee for order execution /// @dev Only callable by governance /// @param _orderExecutionFee Fee with native token e.g. ETH function setOrderExecutionFee(uint64 _orderExecutionFee) external onlyGov { orderExecutionFee = _orderExecutionFee; emit OrderExecutionFeeUpdated(_orderExecutionFee); } /// @notice Enable order with onchain function function enableOrder() external { approvedAccounts[msg.sender] = true; emit AccountApproved(msg.sender,false,false); } /// @notice Enable order function for accounts like contract /// @dev Only callable by governance /// @param _account EOA or contract account function enableOrderByGov(address _account) external onlyGov{ approvedAccounts[_account] = true; emit AccountApproved(msg.sender, false, true); } /// @notice Initializes protocol contracts /// @dev Only callable by governance function link() external onlyGov { store = Store(payable(addressStorage.getAddress('Store'))); tradingValidator = TradingValidator(addressStorage.getAddress('TradingValidator')); referralStorage = IReferralStorage(addressStorage.getAddress('ReferralStorage')); executorAddress = addressStorage.getAddress('Executor'); positionManager = PositionManager(addressStorage.getAddress('PositionManager')); emit Link( address(store), address(tradingValidator), address(referralStorage), executorAddress, address(positionManager) ); } /// @notice Submits a new order with signature, /// @dev will be called from the UI only the first time because of gas saving /// @param _params Order to submit /// @param _tpPrice 18 decimal take profit price /// @param _slPrice 18 decimal stop loss price /// @param _trailingStopPercentage trailing stop percentage /// @param _referralCode code for referral system /// @param _signature user signature for enabling order function submitOrderWithSignature( Order memory _params, uint96 _tpPrice, uint96 _slPrice, uint16 _trailingStopPercentage, bytes32 _referralCode, bytes memory _signature ) external payable ifNotPaused { require(approvedAccounts[msg.sender] || SignatureChecker.isValidSignatureNow(msg.sender,ethSignedMessageHash, _signature),"!not signed"); if(!approvedAccounts[msg.sender]){ approvedAccounts[msg.sender] = true; emit AccountApproved(msg.sender, true, false); } _submitOrder(_params,_tpPrice,_slPrice,_trailingStopPercentage,_referralCode); } /// @notice Submits a new order /// @param _params Order to submit /// @param _tpPrice 18 decimal take profit price /// @param _slPrice 18 decimal stop loss price /// @param _trailingStopPercentage trailing stop percentage /// @param _referralCode code for referral system function submitOrder( Order memory _params, uint96 _tpPrice, uint96 _slPrice, uint16 _trailingStopPercentage, bytes32 _referralCode ) external payable ifNotPaused { require(approvedAccounts[msg.sender] ,"!not approved"); _submitOrder(_params,_tpPrice,_slPrice,_trailingStopPercentage,_referralCode); } /// @notice Submits a new order /// @dev Internal function invoked by {submitOrderWithSignature,submitOrder} function _submitOrder( Order memory _params, uint96 _tpPrice, uint96 _slPrice, uint16 _trailingStopPercentage, bytes32 _referralCode ) internal { // order cant be reduce-only if take profit or stop loss order is submitted alongside main order if (_tpPrice > 0 || _slPrice > 0 || _trailingStopPercentage > 0) { _params.orderDetail.isReduceOnly = false; } if (_params.orderDetail.isReduceOnly || _params.orderDetail.orderType == 3){ if(_params.orderDetail.orderType == 3){ require(_params.orderDetail.isReduceOnly , '!trailing-stop-must-reduce'); _params.orderDetail.price = 0; // must be zero to distinguish it from a limit order ts } PositionManager.Position memory position = positionManager.getPosition(msg.sender, _params.asset, _params.market); require(position.size > 0 , '!no-position'); require(position.isLong != _params.isLong , '!reduce-wrong-direction'); } _params.user = _orderUser(_params); if(_params.user != msg.sender){ _params.orderDetail.cancelOrderId = 0; } _params.orderDetail.executionFee = orderExecutionFee; uint256 totalExecutionFee = _params.orderDetail.executionFee; // Submit order uint256 valueConsumed; (, valueConsumed) = _submitOrder(_params); if (_referralCode != bytes32(0) && address(referralStorage) != address(0)) { referralStorage.setTraderReferralCode(_params.user, _referralCode); } // tp/sl price checks if (_tpPrice > 0 || _slPrice > 0 || _trailingStopPercentage > 0) { if (_params.orderDetail.price > 0 ) { if (_tpPrice > 0) { require( (_params.isLong && _tpPrice > _params.orderDetail.price) || (!_params.isLong && _tpPrice < _params.orderDetail.price), '!tp-invalid' ); } if (_slPrice > 0 ) { require( (_params.isLong && _slPrice < _params.orderDetail.price) || (!_params.isLong && _slPrice > _params.orderDetail.price), '!sl-invalid' ); } } if (_tpPrice > 0 && _slPrice > 0) { require((_params.isLong && _tpPrice > _slPrice) || (!_params.isLong && _tpPrice < _slPrice), '!tpsl-invalid'); } // tp and sl order ids uint32 tpOrderId; uint32 slOrderId; // long -> short, short -> long for take profit / stop loss order _params.isLong = !_params.isLong; // reset order expiry for TP/SL orders if (_params.orderDetail.expiry > 0) _params.orderDetail.expiry = 0; // submit stop loss order if (_slPrice > 0 ) { _params.orderDetail.price = _slPrice; _params.orderDetail.orderType = 2; _params.orderDetail.isReduceOnly = true; totalExecutionFee += _params.orderDetail.executionFee; // Order is reduce-only so valueConsumed is always zero (slOrderId, ) = _submitOrder(_params); } else if (_trailingStopPercentage > 0){ _params.orderDetail.orderType = 3; _params.orderDetail.isReduceOnly = true; _params.orderDetail.trailingStopPercentage = _trailingStopPercentage; totalExecutionFee += _params.orderDetail.executionFee; // Order is reduce-only so valueConsumed is always zero (slOrderId, ) = _submitOrder(_params); } // submit take profit order if (_tpPrice > 0) { _params.orderDetail.price = _tpPrice; _params.orderDetail.orderType = 1; _params.orderDetail.isReduceOnly = true; _params.orderDetail.trailingStopPercentage = 0; totalExecutionFee += _params.orderDetail.executionFee; // Order is reduce-only so valueConsumed is always zero (tpOrderId, ) = _submitOrder(_params); } // Update orders to cancel each other if (tpOrderId > 0 && slOrderId > 0) { _updateCancelOrderId(tpOrderId, slOrderId); _updateCancelOrderId(slOrderId, tpOrderId); } } uint256 requiredValue = (_params.asset == address(0) ? valueConsumed : 0) + totalExecutionFee; require(msg.value >= requiredValue,"!msg-value"); uint256 diff = msg.value - requiredValue; if (diff > 0) { payable(_params.user).sendValue(diff); } } /// @notice Submits a new order /// @dev Internal function invoked by {_submitOrder} function _submitOrder(Order memory _params) internal returns (uint32, uint96) { // Validations require(_params.orderDetail.orderType < 4, '!order-type'); // execution price of trigger order cant be zero if (_params.orderDetail.orderType == 1 || _params.orderDetail.orderType == 2) { require(_params.orderDetail.price > 0, '!price'); } else if (_params.orderDetail.orderType == 3) { require(_params.orderDetail.trailingStopPercentage > 0 && _params.orderDetail.trailingStopPercentage <= MAX_TRAILING_STOP_PERCENTAGE, '!trailing-stop-invalid'); } // check if base asset is supported and order size is above min size IStore.Asset memory asset = store.getAsset(_params.asset); require(asset.minSize > 0, '!asset-exists'); require(_params.orderDetail.isReduceOnly || _params.size >= asset.minSize, '!min-size'); // check if market exists IStore.Market memory market = store.getMarket(_params.market); require(market.maxLeverage > 0, '!market-exists'); // Order expiry validations if (_params.orderDetail.expiry > 0) { // expiry value cant be in the past require(_params.orderDetail.expiry >= block.timestamp, '!expiry-value'); // _params.expiry cant be after default expiry of market and trigger orders uint256 ttl = _params.orderDetail.expiry - block.timestamp; if (_params.orderDetail.orderType == 0) require(ttl <= maxMarketOrderTTL, '!max-expiry'); else require(ttl <= maxTriggerOrderTTL, '!max-expiry'); } // cant cancel an order of another user if (_params.orderDetail.cancelOrderId > 0) { require(userOrderIds[_params.user].contains(_params.orderDetail.cancelOrderId), '!user-oco'); } // Set timestamp _params.timestamp = block.timestamp.toUint32(); _params.fee = (_params.size * market.fee / BPS_DIVIDER).toUint96(); uint96 valueConsumed; bool isSentNative; if (_params.orderDetail.isReduceOnly) { _params.margin = 0; // Existing position is checked on execution so TP/SL can be submitted as reduce-only alongside a non-executed order // In this case, valueConsumed is zero as margin is zero and fee is taken from the order's margin when position is executed } else { require(!market.isReduceOnly, '!market-reduce-only'); require(_params.margin > 0, '!margin'); uint256 leverage = (UNIT * _params.size) / _params.margin; require(leverage >= UNIT, '!min-leverage'); require(leverage <= market.maxLeverage * UNIT, '!max-leverage'); // Check against max OI if it's not reduce-only. this is not completely fail safe as user can place many // consecutive market orders of smaller size and get past the max OI limit here, because OI is not updated until // keeper picks up the order. That is why maxOI is checked on processing as well, which is fail safe. // This check is more of preemptive for user to not submit an order tradingValidator.checkMaxOI(_params.asset, _params.market, _params.size); // Transfer fee and margin to store valueConsumed = _params.margin + _params.fee; if (_params.asset == address(0)) { store.transferIn{value: valueConsumed + _params.orderDetail.executionFee }(_params.asset, msg.sender, valueConsumed + _params.orderDetail.executionFee); isSentNative = true; } else { store.transferIn(_params.asset, msg.sender, valueConsumed); } } if(_params.orderDetail.executionFee > 0 && !isSentNative){ store.transferIn{value: _params.orderDetail.executionFee}(address(0), msg.sender, _params.orderDetail.executionFee); } // Add order to store and emit event _params.orderId = _add(_params); emit OrderCreated( _params.orderId, _params.user, _params.asset, _params.market, _params.margin, _params.size, _params.orderDetail.price, _params.fee, _params.isLong, _params.orderDetail.orderType, _params.orderDetail.isReduceOnly, _params.orderDetail.expiry, _params.orderDetail.cancelOrderId, msg.sender, _params.orderDetail.executionFee, _params.orderDetail.trailingStopPercentage ); return (_params.orderId, valueConsumed); } /// @notice Cancels order /// @param _orderId Order to cancel function cancelOrder(uint32 _orderId) external ifNotPaused { Order memory order = orders[_orderId]; require(order.size > 0, '!order'); require(order.user == msg.sender, '!user'); _cancelOrder(_orderId, 'by-user', msg.sender); } /// @notice Cancel several orders /// @param _orderIds Array of orderIds to cancel function cancelOrders(uint32[] calldata _orderIds) external ifNotPaused { for (uint32 i; i < _orderIds.length; i++) { Order memory order = orders[_orderIds[i]]; if (order.size > 0 && order.user == msg.sender) { _cancelOrder(_orderIds[i], 'by-user', msg.sender); } } } /// @notice Cancels order /// @dev Only callable by Executor contract /// @param _orderId Order to cancel /// @param _reason Cancellation reason /// @param _executionFeeReceiver Address of execution fee receiver function cancelOrder(uint32 _orderId, string calldata _reason, address _executionFeeReceiver) external onlyExecutor { _cancelOrder(_orderId, _reason, _executionFeeReceiver); } /// @notice Cancels order /// @dev Internal function without access restriction /// @param _orderId Order to cancel /// @param _reason Cancellation reason /// @param _executionFeeReceiver Address of execution fee receiver function _cancelOrder(uint32 _orderId, string memory _reason, address _executionFeeReceiver) internal { Order memory order = orders[_orderId]; if (order.size == 0) return; _remove(_orderId); bool isSentNative; if (!order.orderDetail.isReduceOnly) { isSentNative = order.asset == address(0) && order.user == _executionFeeReceiver; store.transferOut(order.asset, order.user, order.margin + order.fee + (isSentNative ? order.orderDetail.executionFee : 0)); } if(order.orderDetail.executionFee > 0 && !isSentNative){ store.transferOut(address(0), _executionFeeReceiver, order.orderDetail.executionFee); } emit OrderCancelled(_orderId, order.user, _executionFeeReceiver, _reason); } /// @notice Get order user /// @dev if sender is whitelisted funding account and order is suitable, The user is being made incoming in the params /// @param _params Order params /// @return user order user function _orderUser(Order memory _params) internal view returns (address) { if(whitelistedFundingAccount[msg.sender]){ require(_params.user != address(0) && !_params.orderDetail.isReduceOnly && _params.orderDetail.orderType == 0,"funding-account-order-fail"); return _params.user; } return msg.sender; } /// @notice Adds order to storage /// @dev Only callable by PositionManager contract function add(Order memory _order) external onlyPositionManager returns (uint32) { return _add(_order); } /// @notice Adds order to storage /// @dev Internal function function _add(Order memory _order) internal returns (uint32) { uint32 nextOrderId = ++oid; _order.orderId = nextOrderId; orders[nextOrderId] = _order; userOrderIds[_order.user].add(nextOrderId); if (_order.orderDetail.orderType == 0) { marketOrderIds.add(nextOrderId); } else { triggerOrderIds.add(nextOrderId); } emit AddOrder(nextOrderId, _order.orderDetail.orderType); return nextOrderId; } /// @notice Removes order from store /// @dev Only callable by PositionManager contract /// @param _orderId Order to remove function remove(uint32 _orderId) external onlyPositionManager { _remove(_orderId); } /// @notice Removes order from store /// @dev Internal function /// @param _orderId Order to remove function _remove(uint32 _orderId) internal { Order memory order = orders[_orderId]; if (order.size == 0) return; userOrderIds[order.user].remove(_orderId); marketOrderIds.remove(_orderId); triggerOrderIds.remove(_orderId); emit RemoveOrder(_orderId, order.orderDetail.orderType); delete orders[_orderId]; } /// @notice Updates `cancelOrderId` of `orderId`, e.g. TP order cancels a SL order and vice versa /// @dev Internal function /// @param _orderId Order which cancels `cancelOrderId` on execution /// @param _cancelOrderId Order to cancel when `orderId` executes function _updateCancelOrderId(uint32 _orderId, uint32 _cancelOrderId) internal { OrderDetail storage orderDetail = orders[_orderId].orderDetail; orderDetail.cancelOrderId = _cancelOrderId; } /// @notice Returns a single order /// @param _orderId Order to get function get(uint32 _orderId) external view returns (Order memory) { return orders[_orderId]; } /// @notice Returns many orders /// @param _orderIds Orders to get, e.g. [1, 2, 5] function getMany(uint32[] calldata _orderIds) external view returns (Order[] memory) { uint256 length = _orderIds.length; Order[] memory _orders = new Order[](length); for (uint256 i; i < length; i++) { _orders[i] = orders[_orderIds[i]]; } return _orders; } /// @notice Returns market orders /// @param _length Amount of market orders to return function getMarketOrders(uint256 _length) external view returns (Order[] memory) { uint32 marketOrderlength = marketOrderIds.length().toUint32(); if (_length > marketOrderlength) _length = marketOrderlength; Order[] memory _orders = new Order[](_length); for (uint256 i; i < _length; i++) { _orders[i] = orders[marketOrderIds.at(i).toUint32()]; } return _orders; } /// @notice Returns trigger orders /// @param _length Amount of trigger orders to return /// @param _offset Offset to start function getTriggerOrders(uint256 _length, uint256 _offset) external view returns (Order[] memory) { uint32 triggerOrderlength = triggerOrderIds.length().toUint32(); if (_length + _offset > triggerOrderlength) _length = triggerOrderlength - _offset; Order[] memory _orders = new Order[](_length); for (uint256 i; i < _length ; i++) { _orders[i] = orders[triggerOrderIds.at(i+_offset).toUint32()]; } return _orders; } /// @notice Returns orders of `user` function getUserOrders(address _user) external view returns (Order[] memory) { uint32 length = userOrderIds[_user].length().toUint32(); Order[] memory _orders = new Order[](length); for (uint256 i; i < length; i++) { _orders[i] = orders[userOrderIds[_user].at(i).toUint32()]; } return _orders; } /// @notice Returns amount of market orders function getMarketOrderCount() external view returns (uint256) { return marketOrderIds.length(); } /// @notice Returns amount of trigger orders function getTriggerOrderCount() external view returns (uint256) { return triggerOrderIds.length(); } /// @notice Returns order amount of `user` function getUserOrderCount(address _user) external view returns (uint256) { return userOrderIds[_user].length(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC1271 standard signature validation method for * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. * * _Available since v4.1._ */ interface IERC1271 { /** * @dev Should return whether the signature provided is valid for the provided data * @param hash Hash of the data to be signed * @param signature Signature byte array associated with _data */ function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (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() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == _ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to * 0 before setting it to a non-zero value. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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 * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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 // Deprecated in v4.8 } 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"); } } /** * @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) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-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 { 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 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 message) { // 32 is the length in bytes of hash, // enforced by the type signature above /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") mstore(0x1c, hash) message := keccak256(0x00, 0x3c) } } /** * @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 data) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, "\x19\x01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) data := keccak256(ptr, 0x42) } } /** * @dev Returns an Ethereum Signed Data with intended validator, created from a * `validator` and `data` according to the version 0 of EIP-191. * * See {recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x00", validator, data)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/SignatureChecker.sol) pragma solidity ^0.8.0; import "./ECDSA.sol"; import "../../interfaces/IERC1271.sol"; /** * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like * Argent and Gnosis Safe. * * _Available since v4.1._ */ library SignatureChecker { /** * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`. * * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus * change through time. It could return true at block N and false at block N+1 (or the opposite). */ function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) { (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature); return (error == ECDSA.RecoverError.NoError && recovered == signer) || isValidERC1271SignatureNow(signer, hash, signature); } /** * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated * against the signer smart contract using ERC1271. * * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus * change through time. It could return true at block N and false at block N+1 (or the opposite). */ function isValidERC1271SignatureNow( address signer, bytes32 hash, bytes memory signature ) internal view returns (bool) { (bool success, bytes memory result) = signer.staticcall( abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature) ); return (success && result.length >= 32 && abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toUint248(uint256 value) internal pure returns (uint248) { require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toUint240(uint256 value) internal pure returns (uint240) { require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toUint232(uint256 value) internal pure returns (uint232) { require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.2._ */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toUint216(uint256 value) internal pure returns (uint216) { require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toUint208(uint256 value) internal pure returns (uint208) { require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toUint200(uint256 value) internal pure returns (uint200) { require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toUint192(uint256 value) internal pure returns (uint192) { require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toUint184(uint256 value) internal pure returns (uint184) { require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toUint176(uint256 value) internal pure returns (uint176) { require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toUint168(uint256 value) internal pure returns (uint168) { require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toUint160(uint256 value) internal pure returns (uint160) { require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toUint152(uint256 value) internal pure returns (uint152) { require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toUint144(uint256 value) internal pure returns (uint144) { require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toUint136(uint256 value) internal pure returns (uint136) { require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v2.5._ */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toUint120(uint256 value) internal pure returns (uint120) { require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toUint112(uint256 value) internal pure returns (uint112) { require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toUint104(uint256 value) internal pure returns (uint104) { require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.2._ */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toUint88(uint256 value) internal pure returns (uint88) { require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toUint80(uint256 value) internal pure returns (uint80) { require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toUint72(uint256 value) internal pure returns (uint72) { require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v2.5._ */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toUint56(uint256 value) internal pure returns (uint56) { require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toUint48(uint256 value) internal pure returns (uint48) { require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toUint40(uint256 value) internal pure returns (uint40) { require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v2.5._ */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toUint24(uint256 value) internal pure returns (uint24) { require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v2.5._ */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v2.5._ */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. * * _Available since v3.0._ */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.7._ */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.7._ */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. * * _Available since v3.0._ */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @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] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @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. * * ```solidity * 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. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // 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; if (lastIndex != toDeleteIndex) { 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] = valueIndex; // Replace lastValue's index to valueIndex } // 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) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // 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); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // 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)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // 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 in 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)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.22; import './utils/AddressStorage.sol'; import './Store.sol'; import './PositionManager.sol'; import './utils/Governable.sol'; /** * @title FundingTracker * @notice Funding rates are calculated hourly for each market and collateral * asset based on the real-time open interest imbalance */ contract FundingTracker is Governable { // Constants uint256 public constant UNIT = 10 ** 18; // interval used to calculate accrued funding uint256 public fundingInterval = 1 hours; // asset => market => funding tracker (long) (short is opposite) mapping(address => mapping(bytes10 => int256)) private fundingTrackers; // asset => market => last time fundingTracker was updated. In seconds. mapping(address => mapping(bytes10 => uint256)) private lastUpdated; // Events event FundingUpdated(address indexed asset, bytes10 market, int256 fundingTracker, int256 fundingIncrement); event FundingIntervalUpdated(uint256 interval); event Link(address store, address positionManager, address executor); // Contracts AddressStorage public immutable addressStorage; Store public store; PositionManager public positionManager; address public executorAddress; /// @dev Only callable by PositionManager or Executor contracts modifier onlyPositionManagerAndExecutor() { require(msg.sender == address(positionManager) || msg.sender == executorAddress, "!unauthorized"); _; } /// @dev Initializes addressStorage address constructor(AddressStorage _addressStorage) { addressStorage = _addressStorage; } /// @notice Set Funding Interval /// @dev Only callable by governance /// @param _interval Funding Interval function setFundingInterval(uint256 _interval) external onlyGov { require(_interval > 0, '!interval'); fundingInterval = _interval; emit FundingIntervalUpdated(_interval); } /// @notice Initializes protocol contracts /// @dev Only callable by governance function link() external onlyGov { store = Store(addressStorage.getAddress('Store')); positionManager = PositionManager(addressStorage.getAddress('PositionManager')); executorAddress = addressStorage.getAddress('Executor'); emit Link( address(store), address(positionManager), executorAddress ); } /// @notice Returns last update timestamp of `asset` and `market` /// @param _asset Asset address, e.g. address(0) for ETH /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" function getLastUpdated(address _asset, bytes10 _market) external view returns (uint256) { return lastUpdated[_asset][_market]; } /// @notice Returns funding tracker of `asset` and `market` /// @param _asset Asset address, e.g. address(0) for ETH /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" function getFundingTracker(address _asset, bytes10 _market) external view returns (int256) { return fundingTrackers[_asset][_market]; } /// @notice Returns funding tracker of `asset` and `market` includes unrealized funding trackers /// @param _asset Asset address, e.g. address(0) for ETH /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" function getNextFundingTracker(address _asset, bytes10 _market) external view returns (int256) { int256 fundingTracker = fundingTrackers[_asset][_market]; if(lastUpdated[_asset][_market]+fundingInterval <= block.timestamp){ int256 fundingIncrement = getAccruedFunding(_asset, _market, 0); // in UNIT * bps if (fundingIncrement != 0){ fundingTracker += fundingIncrement; } } return fundingTracker; } /// @notice Returns funding trackers of `assets` and `markets` /// @param _assets Array of asset addresses /// @param _markets Array of market bytes10s function getFundingTrackers( address[] calldata _assets, bytes10[] calldata _markets ) external view returns (int256[] memory fts) { uint256 assetLength = _assets.length; uint256 marketLength = _markets.length; fts = new int256[](assetLength * marketLength); uint index; for (uint256 i; i < assetLength; i++) { for (uint256 j; j < marketLength; j++) { index = (i * marketLength) + j; fts[index] = fundingTrackers[_assets[i]][_markets[j]]; } } return fts; } /// @notice Returns funding trackers of `assets` and `markets` includes unrealized funding trackers /// @param _assets Array of asset addresses /// @param _markets Array of market bytes10s function getNextFundingTrackers( address[] calldata _assets, bytes10[] calldata _markets ) external view returns (int256[] memory fts) { uint256 assetLength = _assets.length; uint256 marketLength = _markets.length; fts = new int256[](assetLength * marketLength); uint index; for (uint256 i; i < assetLength; i++) { for (uint256 j; j < marketLength; j++) { index = (i * marketLength) + j; fts[index] = fundingTrackers[_assets[i]][_markets[j]]; if(lastUpdated[_assets[i]][_markets[j]]+fundingInterval <= block.timestamp){ int256 fundingIncrement = getAccruedFunding(_assets[i], _markets[j], 0); // in UNIT * bps if (fundingIncrement != 0){ fts[index] += fundingIncrement; } } } } return fts; } /// @notice Updates funding tracker of `market` and `asset` /// @dev Only callable by position manager or executor contracts /// @param _asset Asset address, e.g. address(0) for ETH /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" function updateFundingTracker(address _asset, bytes10 _market) external onlyPositionManagerAndExecutor { uint256 _lastUpdated = lastUpdated[_asset][_market]; uint256 _now = block.timestamp; // condition is true only on the very first execution if (_lastUpdated == 0) { lastUpdated[_asset][_market] = _now; return; } // returns if block.timestamp - lastUpdated is less than funding interval if (_lastUpdated + fundingInterval > _now) return; // positive funding increment indicates that shorts pay longs, negative that longs pay shorts int256 fundingIncrement = getAccruedFunding(_asset, _market, 0); // in UNIT * bps lastUpdated[_asset][_market] = _now; // return if funding increment is zero if (fundingIncrement == 0) return; fundingTrackers[_asset][_market] += fundingIncrement; emit FundingUpdated(_asset, _market, fundingTrackers[_asset][_market], fundingIncrement); } /// @notice Returns accrued funding of `market` and `asset` /// @param _asset Asset address, e.g. address(0) for ETH /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" /// @param _intervals intervals , if 0, it is calculated from the last updated point until now,if fundingInterval is 3600,then 1 for 1h ,24 for 1 day function getAccruedFunding(address _asset, bytes10 _market, uint256 _intervals) public view returns (int256) { if (_intervals == 0) { _intervals = (block.timestamp - lastUpdated[_asset][_market]) / fundingInterval; } if (_intervals == 0) return 0; uint256 OILong = positionManager.getOILong(_asset, _market); uint256 OIShort = positionManager.getOIShort(_asset, _market); if (OIShort == 0 && OILong == 0) return 0; uint256 OIDiff = OIShort > OILong ? OIShort - OILong : OILong - OIShort; Store.Market memory marketInfo = store.getMarket(_market); uint256 yearlyFundingFactor = marketInfo.fundingFactor; uint256 accruedFunding = (UNIT * yearlyFundingFactor * OIDiff * _intervals) / ((365 days / fundingInterval) * (OILong + OIShort)); // in UNIT * bps if (OILong > OIShort) { // Longs pay shorts. Increase funding tracker. return int256(accruedFunding); } else { // Shorts pay longs. Decrease funding tracker. return -1 * int256(accruedFunding); } } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.22; interface IReferralStorage { function codeOwners(bytes32 _code) external view returns (address); function traderReferralCodes(address _account) external view returns (bytes32); function referrerDiscountShares(address _account) external view returns (uint256); function referrerTiers(address _account) external view returns (uint256); function getTraderReferralInfo(address _account) external view returns (bytes32, address); function setTraderReferralCode(address _account, bytes32 _code) external; function setTier(uint256 _tierId, uint256 _totalRebate, uint256 _discountShare) external; function setReferrerTier(address _referrer, uint256 _tierId) external; function govSetCodeOwner(bytes32 _code, address _newAccount) external; }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.22; import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol'; import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; import '@openzeppelin/contracts/utils/math/SafeCast.sol'; import './utils/AddressStorage.sol'; import './utils/TradingValidator.sol'; import './FundingTracker.sol'; import './Store.sol'; import './OrderBook.sol'; import './utils/interfaces/IStore.sol'; import './utils/interfaces/IReferencePriceFeed.sol'; import './interfaces/IReferralStorage.sol'; import './utils/Governable.sol'; /** * @title PositionManager * @notice Implementation of position related logic, i.e. increase positions, * decrease positions, close positions, add/remove margin */ contract PositionManager is Governable { // Constants uint256 public constant UNIT = 10 ** 18; uint256 public constant BPS_DIVIDER = 10000; // Libraries using EnumerableSet for EnumerableSet.Bytes32Set; using SafeCast for uint256; // Position struct struct Position { address user; // User that submitted the position bytes10 market; // Market this position was submitted on bool isLong; // Whether the position is long or short address asset; // Asset address, e.g. address(0) for ETH uint96 size; // The position's size (margin * leverage) uint32 timestamp; // Time at which the position was created uint96 margin; // Collateral tied to this position. In wei uint96 price; // The position's average execution price int256 fundingTracker; // Market funding rate tracker } struct OpenInterest { uint128 long; uint128 short; } // Constants uint256 public constant MAX_KEEPER_FEE_SHARE = 2000; // 20% uint256 public constant MAX_FEE = 500; // 5% uint256 public constant MAX_MIN_POSITION_HOLD_TIME = 1800; // 30 min. // State variables uint256 public removeMarginBuffer = 1000; uint256 public keeperFeeShare = 500; uint256 public trailingStopFee = 100; // 1% uint256 public minPositionHoldTime; // Mappings mapping(address => OpenInterest) private assetOI; // open interest. asset => OpenInterest mapping(address => mapping(bytes10 => OpenInterest)) private OI; // open interest. market => asset => OpenInterest mapping(bytes32 => Position) private positions; // key = asset,user,market EnumerableSet.Bytes32Set private positionKeys; // [position keys..] mapping(address => EnumerableSet.Bytes32Set) private positionKeysForUser; // user => [position keys..] // user => market => last position increase mapping(address => mapping(bytes10 => uint256)) private lastIncreased; // Contracts AddressStorage public immutable addressStorage; OrderBook public orderBook; TradingValidator public tradingValidator; IReferralStorage public referralStorage; FundingTracker public fundingTracker; Store public store; IReferencePriceFeed public referencePriceFeed; address public executorAddress; // Events event PositionIncreased( uint256 indexed orderId, address indexed user, address indexed asset, bytes10 market, bool isLong, uint256 size, uint256 margin, uint256 price, uint256 positionMargin, uint256 positionSize, uint256 positionPrice, int256 fundingTracker, uint256 fee ); event PositionDecreased( uint256 indexed orderId, address indexed user, address indexed asset, bytes10 market, bool isLong, uint256 size, uint256 margin, uint256 price, uint256 positionMargin, uint256 positionSize, uint256 positionPrice, int256 fundingTracker, uint256 fee, int256 pnl, int256 pnlUsd, int256 fundingFee, bool isTrailingStop ); event MarginIncreased( address indexed user, address indexed asset, bytes10 market, uint256 marginDiff, uint256 positionMargin ); event MarginDecreased( address indexed user, address indexed asset, bytes10 market, uint256 marginDiff, uint256 positionMargin ); event FeePaid( uint256 indexed orderId, address indexed user, address indexed asset, bytes10 market, uint256 fee, uint256 poolFee, uint256 treasuryFee, uint256 keeperFee, uint256 executionFee, bool isLiquidation ); event IncreasePositionReferral( address indexed account, address indexed asset, uint256 sizeDelta, uint256 fee, bytes32 referralCode, address referrer ); event DecreasePositionReferral( address indexed account, address indexed asset, uint256 sizeDelta, uint256 fee, bytes32 referralCode, address referrer ); event MinPositionHoldTimeUpdated(uint256 minPositionHoldTime); event RemoveMarginBufferUpdated(uint256 bps); event KeeperFeeShareUpdated(uint256 keeperFeeShare); event TrailingStopFeeUpdated(uint256 trailingStopFee); event Link(address orderBook, address tradingValidator, address fundingTracker, address store, address referencePriceFeed, address referralStorage, address executorAddress); event RemovePosition(address indexed user, address indexed asset, bytes10 indexed market); event IncrementOI(address indexed asset, bytes10 indexed market, uint96 amount, bool isLong); event DecrementOI(address indexed asset, bytes10 indexed market, uint96 amount, bool isLong); /// @dev Reverts if order processing is paused modifier ifNotPaused() { require(!orderBook.areNewOrdersPaused(), '!paused'); _; } /// @dev Only callable by Executor contract modifier onlyExecutor() { require(msg.sender == executorAddress, "!unauthorized"); _; } /// @dev Initializes addressStorage address constructor(AddressStorage _addressStorage) { addressStorage = _addressStorage; } /// @notice Initializes protocol contracts /// @dev Only callable by governance function link() external onlyGov { orderBook = OrderBook(addressStorage.getAddress('OrderBook')); tradingValidator = TradingValidator(addressStorage.getAddress('TradingValidator')); fundingTracker = FundingTracker(addressStorage.getAddress('FundingTracker')); store = Store(addressStorage.getAddress('Store')); referencePriceFeed = IReferencePriceFeed(addressStorage.getAddress('ReferencePriceFeed')); referralStorage = IReferralStorage(addressStorage.getAddress('ReferralStorage')); executorAddress = addressStorage.getAddress('Executor'); emit Link( address(orderBook), address(tradingValidator), address(fundingTracker), address(store), address(referencePriceFeed), address(referralStorage), executorAddress ); } /// @notice Set minimum position hold time /// @dev Only callable by governance /// @param _minPositionHoldTime minimum position hold time in seconds function setMinPositionHoldTime(uint256 _minPositionHoldTime) external onlyGov { require(_minPositionHoldTime <= MAX_MIN_POSITION_HOLD_TIME, '!min-position-hold-time'); minPositionHoldTime = _minPositionHoldTime; emit MinPositionHoldTimeUpdated(_minPositionHoldTime); } /// @notice Updates `removeMarginBuffer` /// @dev Only callable by governance /// @param _bps new `removeMarginBuffer` in bps function setRemoveMarginBuffer(uint256 _bps) external onlyGov { require(_bps < BPS_DIVIDER, '!bps'); removeMarginBuffer = _bps; emit RemoveMarginBufferUpdated(_bps); } /// @notice Sets keeper fee share /// @dev Only callable by governance /// @param _keeperFeeShare new `keeperFeeShare` in bps function setKeeperFeeShare(uint256 _keeperFeeShare) external onlyGov { require(_keeperFeeShare <= MAX_KEEPER_FEE_SHARE, '!keeper-fee-share'); keeperFeeShare = _keeperFeeShare; emit KeeperFeeShareUpdated(_keeperFeeShare); } /// @notice Sets trailing stop fee /// @dev Only callable by governance /// @dev Only applies if position closed by trailing stop order keeper /// @param _trailingStopFee new `trailingStopFee` in bps function setTrailingStopFee(uint256 _trailingStopFee) external onlyGov { require(_trailingStopFee <= MAX_FEE, '!trailing-stop-fee'); trailingStopFee = _trailingStopFee; emit TrailingStopFeeUpdated(_trailingStopFee); } /// @notice Opens a new position or increases existing one /// @dev Only callable by Executor contract function increasePosition(uint32 _orderId, uint256 _price, address _keeper) external onlyExecutor { _increasePosition(_orderId,_price,_keeper); } /// @notice Opens a new position or increases existing one /// @dev Internal function invoked by {increasePosition,_decreasePosition} function _increasePosition(uint32 _orderId, uint256 _price, address _keeper) internal { OrderBook.Order memory order = orderBook.get(_orderId); // Check if maximum open interest is reached tradingValidator.checkMaxOI(order.asset, order.market, order.size); // FundingTracker update must be before increment fundingTracker.updateFundingTracker(order.asset, order.market); _incrementOI(order.asset, order.market, order.size, order.isLong); Position memory position = getPosition(order.user, order.asset, order.market); uint96 averagePrice = ((uint256(position.size) * position.price + order.size * _price) / (position.size + order.size)).toUint96(); // Populate position fields if new position if (position.size == 0) { position.user = order.user; position.asset = order.asset; position.market = order.market; position.timestamp = block.timestamp.toUint32(); position.isLong = order.isLong; position.fundingTracker = fundingTracker.getFundingTracker(order.asset, order.market); } // Add or update position position.size += order.size; position.margin += order.margin; position.price = averagePrice; _addOrUpdate(position); // Remove order orderBook.remove(_orderId); // Credit fee to _keeper, pool, stakers, treasury _creditFee(_orderId, order.user, order.asset, order.market, order.fee, order.orderDetail.executionFee, false, _keeper); lastIncreased[order.user][order.market] = block.timestamp; emit PositionIncreased( _orderId, order.user, order.asset, order.market, order.isLong, order.size, order.margin, _price, position.margin, position.size, position.price, position.fundingTracker, order.fee ); _emitIncreasePositionReferral(order.user, order.asset, order.size, order.fee); } /// @notice Decreases or closes an existing position /// @dev Only callable by Executor contract function decreasePosition(uint32 _orderId, uint256 price, bool _isTrailingStop, address _keeper) external onlyExecutor { _decreasePosition(_orderId, price, _isTrailingStop, _keeper); } /// @notice Decreases or closes an existing position /// @dev Internal function invoked by {decreasePosition} function _decreasePosition(uint32 _orderId, uint256 price, bool _isTrailingStop, address _keeper) internal { OrderBook.Order memory order = orderBook.get(_orderId); Position memory position = getPosition(order.user, order.asset, order.market); // Check last increased require(lastIncreased[order.user][order.market] < block.timestamp - minPositionHoldTime, "!min-hold-time"); // If position size is less than order size, not all will be executed uint256 executedOrderSize = position.size > order.size ? order.size : position.size; uint256 remainingOrderSize = order.size - executedOrderSize; uint256 remainingOrderMargin; uint256 amountToReturnToUser; if (!order.orderDetail.isReduceOnly) { // User submitted order.margin when sending the order. Refund the portion of order.margin // that executes against the position uint256 executedOrderMargin = (order.margin * executedOrderSize) / order.size; amountToReturnToUser += executedOrderMargin; remainingOrderMargin = order.margin - executedOrderMargin; } if(position.size > executedOrderSize){ IStore.Asset memory assetInfo = store.getAsset(order.asset); require(position.size - executedOrderSize >= assetInfo.minSize,"!min-remaining-size"); } // Calculate fee based on executed order size uint256 fee = ((order.fee + (_isTrailingStop ? (executedOrderSize * trailingStopFee) / BPS_DIVIDER : 0) ) * executedOrderSize) / order.size; _creditFee(_orderId, order.user, order.asset, order.market, fee, order.orderDetail.executionFee, false, _keeper); // If an order is reduce-only, fee is taken from the position's margin. uint256 feeToPay = order.orderDetail.isReduceOnly ? fee : 0; // FundingTracker update must be before decrementOI fundingTracker.updateFundingTracker(order.asset, order.market); // Get PNL of position (int256 pnl, int256 fundingFee) = getPnL( order.asset, order.market, position.isLong, price, position.price, executedOrderSize, position.fundingTracker ); uint256 executedPositionMargin = (position.margin * executedOrderSize) / position.size; // If PNL is less than position margin, close position, else update position if (pnl <= -1 * int256(uint256(position.margin))) { pnl = -1 * int256(uint256(position.margin)); executedPositionMargin = position.margin; executedOrderSize = position.size; position.size = 0; } else { position.margin -= executedPositionMargin.toUint96(); position.size -= executedOrderSize.toUint96(); position.fundingTracker = fundingTracker.getFundingTracker(order.asset, order.market); } _decrementOI(order.asset, order.market, executedOrderSize.toUint96(), position.isLong); // Check for maximum pool drawdown tradingValidator.checkPoolDrawdown(order.asset, pnl); // Credit trader loss or debit trader profit based on pnl if (pnl < 0) { uint256 absPnl = uint256(-1 * pnl); store.creditTraderLoss(order.user, order.asset, order.market, absPnl); uint256 totalPnl = absPnl + feeToPay; // If an order is reduce-only, fee is taken from the position's margin as the order's margin is zero. if (totalPnl < executedPositionMargin) { amountToReturnToUser += executedPositionMargin - totalPnl; } } else { store.debitTraderProfit(order.user, order.asset, order.market, uint256(pnl)); // If an order is reduce-only, fee is taken from the position's margin as the order's margin is zero. amountToReturnToUser += executedPositionMargin - feeToPay; } if (position.size == 0) { // Remove position if size == 0 _remove(order.user, order.asset, order.market); } else { _addOrUpdate(position); } // Remove order and transfer funds out orderBook.remove(_orderId); store.transferOut(order.asset, order.user, amountToReturnToUser); emit PositionDecreased( _orderId, order.user, order.asset, order.market, order.isLong, executedOrderSize, executedPositionMargin, price, position.margin, position.size, position.price, position.fundingTracker, feeToPay, pnl, _getUsdAmount(order.asset, pnl), fundingFee, _isTrailingStop ); // Open position in opposite direction if size remains if (!order.orderDetail.isReduceOnly && remainingOrderSize > 0) { OrderBook.OrderDetail memory orderDetail = OrderBook.OrderDetail({ orderType: 0, isReduceOnly: false, price: 0, expiry: 0, cancelOrderId: 0, executionFee: 0, trailingStopPercentage : 0 }); OrderBook.Order memory nextOrder = OrderBook.Order({ user: order.user, margin: remainingOrderMargin.toUint96(), asset: order.asset, market: order.market, isLong: order.isLong, size: remainingOrderSize.toUint96(), fee: ((order.fee * remainingOrderSize) / order.size).toUint96(), timestamp: block.timestamp.toUint32(), orderId : 0, orderDetail:orderDetail }); uint32 nextOrderId = orderBook.add(nextOrder); _increasePosition(nextOrderId, price, _keeper); } _emitDecreasePositionReferral(order.user, order.asset, executedOrderSize, fee); } /// @notice Add margin to a position to decrease its leverage and push away its liquidation price function addMargin(address _asset, bytes10 _market, uint96 _margin) external payable ifNotPaused { address user = msg.sender; Position memory position = getPosition(user, _asset, _market); require(position.size > 0, '!position'); // Transfer additional margin in if (_asset == address(0)) { _margin = msg.value.toUint96(); store.transferIn{value: _margin}(_asset, user, _margin); } else { store.transferIn(_asset, user, _margin); } require(_margin > 0, '!margin'); // update position margin position.margin += _margin; // Check if leverage is above minimum leverage uint256 leverage = (UNIT * position.size) / position.margin; require(leverage >= UNIT, '!min-leverage'); // update position _addOrUpdate(position); emit MarginIncreased(user, _asset, _market, _margin, position.margin); } /// @notice Remove margin from a position to increase its leverage /// @dev Margin removal is only available on markets supported by referencePrice function removeMargin(address _asset, bytes10 _market, uint256 _margin) external ifNotPaused { address user = msg.sender; IStore.Market memory marketInfo = store.getMarket(_market); Position memory position = getPosition(user, _asset, _market); require(position.size > 0, '!position'); require(position.margin > _margin, '!margin'); uint256 remainingMargin = position.margin - _margin; // Leverage uint256 leverageAfterRemoval = (UNIT * position.size) / remainingMargin; require(leverageAfterRemoval <= marketInfo.maxLeverage * UNIT, '!max-leverage'); // This is not available for markets without referencePrice uint256 price = referencePriceFeed.getPrice(marketInfo.referencePriceFeed); require(price > 0, '!price'); (int256 upl, ) = getPnL( _asset, _market, position.isLong, price, position.price, position.size, position.fundingTracker ); if (upl < 0) { uint256 absUpl = uint256(-1 * upl); require( absUpl < (remainingMargin * (BPS_DIVIDER - removeMarginBuffer)) / BPS_DIVIDER, '!upl' ); } // Update position and transfer margin out position.margin = remainingMargin.toUint96(); _addOrUpdate(position); store.transferOut(_asset, user, _margin); emit MarginDecreased(user, _asset, _market, _margin, position.margin); } /// @notice Credit fee to Keeper, Pool, and Treasury /// @dev Only callable by Executor contract function creditFee( uint256 _orderId, address _user, address _asset, bytes10 _market, uint256 _fee, uint256 _executionFee, bool _isLiquidation, address _keeper ) external onlyExecutor { _creditFee(_orderId, _user, _asset, _market, _fee, _executionFee, _isLiquidation, _keeper); } /// @notice Credit fee to Keeper, Pool, and Treasury /// @dev Internal function invoked by {creditFee,_increasePosition,_decreasePosition} /// @param _orderId order id /// @param _user user address /// @param _asset Base asset of position /// @param _market Market position was submitted on /// @param _fee fee paid /// @param _executionFee execution fee with native token /// @param _isLiquidation whether position is liquidated /// @param _keeper keeper address function _creditFee( uint256 _orderId, address _user, address _asset, bytes10 _market, uint256 _fee, uint256 _executionFee, bool _isLiquidation, address _keeper ) internal { if (_fee == 0) return; // multiply _fee by UNIT (10^18) to increase position _fee = _fee * UNIT; uint256 keeperFee; uint256 netFee = _fee; if (keeperFeeShare > 0) { keeperFee = (_fee * keeperFeeShare) / BPS_DIVIDER; netFee = _fee - keeperFee; } // Calculate fees uint256 feeToPool = (netFee * store.feeShare()) / BPS_DIVIDER; uint256 feeToTreasury = netFee - feeToPool; // Increment balances, transfer fees out // Divide _fee by UNIT to get original _fee value back store.incrementBalance(_asset, feeToPool / UNIT); store.addFees(_asset, feeToTreasury / UNIT); if(keeperFee > 0){ store.transferOut(_asset, _keeper, keeperFee / UNIT); } if(_executionFee > 0){ store.transferOut(address(0), _keeper, _executionFee); } emit FeePaid( _orderId, _user, _asset, _market, _fee / UNIT, // paid by user feeToPool / UNIT, feeToTreasury / UNIT, keeperFee / UNIT, _executionFee, _isLiquidation ); } /// @notice Get pnl of a position /// @param _asset Base asset of position /// @param _market Market position was submitted on /// @param _isLong Whether position is long or short /// @param _price Current price of market /// @param _positionPrice Average execution price of position /// @param _size Positions size (margin * leverage) in wei /// @param _fundingTracker Market funding rate tracker /// @return pnl Profit and loss of position /// @return fundingFee Funding fee of position function getPnL( address _asset, bytes10 _market, bool _isLong, uint256 _price, uint256 _positionPrice, uint256 _size, int256 _fundingTracker ) public view returns (int256 pnl, int256 fundingFee) { if (_price == 0 || _positionPrice == 0 || _size == 0) return (0, 0); if (_isLong) { pnl = (int256(_size) * (int256(_price) - int256(_positionPrice))) / int256(_positionPrice); } else { pnl = (int256(_size) * (int256(_positionPrice) - int256(_price))) / int256(_positionPrice); } int256 currentFundingTracker = fundingTracker.getNextFundingTracker(_asset, _market); fundingFee = (int256(_size) * (currentFundingTracker - _fundingTracker)) / (int256(BPS_DIVIDER) * int256(UNIT)); // funding tracker is in UNIT * bps if (_isLong) { pnl -= fundingFee; // positive = longs pay, negative = longs receive } else { pnl += fundingFee; // positive = shorts receive, negative = shorts pay } return (pnl, fundingFee); } /// @dev Returns USD value of `_amount` of `_asset` /// @dev Used for PositionDecreased event function _getUsdAmount(address _asset, int256 _amount) internal view returns (int256) { IStore.Asset memory assetInfo = store.getAsset(_asset); uint256 referencePrice = referencePriceFeed.getPrice(assetInfo.referencePriceFeed); // _amount is in the _asset's decimals, convert to 18. Price is 18 decimals return (_amount * int256(referencePrice)) / int256(10 ** assetInfo.decimals); } /// @dev emit increase position referral event function _emitIncreasePositionReferral(address _account, address _asset, uint256 _sizeDelta, uint256 _fee) internal { if (address(referralStorage) == address(0)) { return; } (bytes32 referralCode, address referrer) = referralStorage.getTraderReferralInfo(_account); if (referralCode == bytes32(0)) { return; } emit IncreasePositionReferral( _account, _asset, _sizeDelta, _fee, referralCode, referrer ); } /// @dev emit decrease position referral event function _emitDecreasePositionReferral(address _account, address _asset, uint256 _sizeDelta, uint256 _fee) internal { if (address(referralStorage) == address(0)) { return; } (bytes32 referralCode, address referrer) = referralStorage.getTraderReferralInfo(_account); if (referralCode == bytes32(0)) { return; } emit DecreasePositionReferral( _account, _asset, _sizeDelta, _fee, referralCode, referrer ); } /// @notice Adds new position or updates exisiting one /// @dev Only callable by other protocol contracts /// @param _position Position to add/update function _addOrUpdate(Position memory _position) internal { bytes32 key = _getPositionKey(_position.user, _position.asset, _position.market); positions[key] = _position; positionKeysForUser[_position.user].add(key); positionKeys.add(key); } /// @notice Removes position /// @dev Only callable by Executor contract /// @param _user User address /// @param _asset Asset address, e.g. address(0) for ETH /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" function remove(address _user, address _asset, bytes10 _market) external onlyExecutor { _remove(_user, _asset, _market); } /// @notice Removes position /// @dev Internal function invoked by {remove, _decreasePosition} function _remove(address _user, address _asset, bytes10 _market) internal { bytes32 key = _getPositionKey(_user, _asset, _market); positionKeysForUser[_user].remove(key); positionKeys.remove(key); delete positions[key]; emit RemovePosition(_user,_asset,_market); } /// @notice Increments open interest /// @dev Invoked by increasePosition /// @param _asset Asset address, e.g. address(0) for ETH /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" /// @param _amount position size amount /// @param _isLong Whether position is long or short function _incrementOI(address _asset, bytes10 _market, uint96 _amount, bool _isLong) internal { if (_isLong) { OI[_asset][_market].long += _amount; assetOI[_asset].long += _amount; } else { OI[_asset][_market].short += _amount; assetOI[_asset].short += _amount; } emit IncrementOI(_asset, _market, _amount, _isLong); } /// @notice Decrements open interest /// @dev Only callable by Executor contract /// @dev Invoked whenever a position is closed or decreased function decrementOI(address _asset, bytes10 _market, uint96 _amount, bool _isLong) external onlyExecutor { _decrementOI(_asset, _market, _amount, _isLong); } /// @notice Decrements open interest /// @dev internal function /// @param _asset Asset address, e.g. address(0) for ETH /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" /// @param _amount position size amount /// @param _isLong Whether position is long or short function _decrementOI(address _asset, bytes10 _market, uint96 _amount, bool _isLong) internal { if (_isLong) { uint128 long = OI[_asset][_market].long; OI[_asset][_market].long = long <= _amount ? 0 : long - _amount; uint128 assetLong = assetOI[_asset].long; assetOI[_asset].long = assetLong <= _amount ? 0 : assetLong - _amount; } else { uint128 short = OI[_asset][_market].short; OI[_asset][_market].short = short <= _amount ? 0 : short - _amount; uint128 assetShort = assetOI[_asset].short; assetOI[_asset].short = assetShort <= _amount ? 0 : assetShort - _amount; } emit DecrementOI(_asset, _market, _amount, _isLong); } /// @notice Returns open interest of `_asset` and `_market` function getOI(address _asset,bytes10 _market) external view returns (uint256) { OpenInterest memory oi = OI[_asset][_market]; return oi.long + oi.short; } /// @notice Returns open interest of `_asset` function getAssetOI(address _asset) external view returns (uint256) { OpenInterest memory oi = assetOI[_asset]; return oi.long + oi.short; } /// @notice Returns open interest of long positions of `_asset` function getAssetOILong(address _asset) external view returns (uint256) { return assetOI[_asset].long; } /// @notice Returns open interest of short positions of `_asset` function getAssetOIShort(address _asset) external view returns (uint256) { return assetOI[_asset].short; } /// @notice Returns open interest of long positions of `_asset` and `_market` function getOILong(address _asset, bytes10 _market) external view returns (uint256) { return OI[_asset][_market].long; } /// @notice Returns open interest of short positions of `_asset` and `_market` function getOIShort(address _asset, bytes10 _market) external view returns (uint256) { return OI[_asset][_market].short; } /// @notice Returns position of `_user` /// @param _asset Base asset of position /// @param _market Market this position was submitted on function getPosition(address _user, address _asset, bytes10 _market) public view returns (Position memory) { bytes32 key = _getPositionKey(_user, _asset, _market); return positions[key]; } /// @notice Returns positions of `_users` /// @param _assets Base assets of positions /// @param _markets Markets of positions function getPositions( address[] calldata _users, address[] calldata _assets, bytes10[] calldata _markets ) external view returns (Position[] memory) { uint256 length = _users.length; Position[] memory _positions = new Position[](length); for (uint256 i; i < length; i++) { _positions[i] = getPosition(_users[i], _assets[i], _markets[i]); } return _positions; } /// @notice Returns positions /// @param _keys Position keys function getPositions(bytes32[] calldata _keys) external view returns (Position[] memory) { uint256 length = _keys.length; Position[] memory positionList = new Position[](length); for (uint256 i; i < length; i++) { positionList[i] = positions[_keys[i]]; } return positionList; } /// @notice Returns number of positions function getPositionCount() external view returns (uint256) { return positionKeys.length(); } /// @notice Returns `_length` amount of positions starting from `_offset` function getPositions(uint256 _length, uint256 _offset) external view returns (Position[] memory) { uint256 keyslength = positionKeys.length(); if (_length + _offset > keyslength) _length = keyslength - _offset; Position[] memory _positions = new Position[](_length); for (uint256 i; i < _length ; i++) { _positions[i] = positions[positionKeys.at(i+_offset)]; } return _positions; } /// @notice Returns all positions of `_user` function getUserPositions(address _user) external view returns (Position[] memory) { uint256 length = positionKeysForUser[_user].length(); Position[] memory _positions = new Position[](length); for (uint256 i; i < length; i++) { _positions[i] = positions[positionKeysForUser[_user].at(i)]; } return _positions; } /// @dev Returns position key by hashing (user, asset, market) function _getPositionKey(address _user, address _asset, bytes10 _market) internal pure returns (bytes32) { return keccak256(abi.encodePacked(_user, _asset, _market)); } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.22; import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; import '@openzeppelin/contracts/security/ReentrancyGuard.sol'; import '@openzeppelin/contracts/utils/math/SafeCast.sol'; import './utils/AddressStorage.sol'; import './utils/Governable.sol'; import './utils/interfaces/IStore.sol'; import './PositionManager.sol'; /** * @title Store * @notice Users can deposit supported assets to back trader profits and receive * a share of trader losses. Each asset pool is siloed, e.g. the ETH * pool is independent from the USDC pool. * Persistent storage of supported assets * Persistent storage of supported markets * Storage of protocol funds */ contract Store is Governable, ReentrancyGuard, IStore { // Libraries using SafeERC20 for IERC20; using Address for address payable; using EnumerableSet for EnumerableSet.UintSet; using SafeCast for uint256; // Constants uint256 public constant BPS_DIVIDER = 10000; uint256 public constant MAX_FEE = 1000; // 10% uint256 public constant MAX_DEVIATION = 1000; // 10% uint256 public constant MAX_LIQTHRESHOLD = 9800; // 98% uint256 public constant MAX_MIN_ORDER_AGE = 30; //seconds uint256 public constant MIN_PYTH_MAX_AGE = 3; //seconds uint256 public constant MAX_BUFFER_PAYOUT_PERIOD = 7 days; // Liquidity struct enum LiquidityType { DEPOSIT, WITHDRAW } struct LiquidityOrder { address asset; // Asset address, e.g. address(0) for ETH uint96 amount; // liquidity order amount address user; // user that submitted the order uint32 liquidityOrderId; // incremental order id LiquidityType orderType; // 0- Deposit, 1- Withdraw uint32 timestamp; // block.timestamp at which the order was submitted uint96 minAmountMinusTax; // realised amount minus tax must be greater than this value uint64 executionFee; //Fee paid with native token for keeper's execution } // State variables uint256 public feeShare = 500; uint256 public bufferPayoutPeriod = 7 days; bool public isPublicDeposit; uint256 public maxLiquidityOrderTTL = 5 minutes; uint32 public liquidityOid; // incremental liquidity order id mapping(uint32 => LiquidityOrder) private liquidityOrders; // order id => Order mapping(address => EnumerableSet.UintSet) private userLiquidityOrderIds; // user => [order ids..] EnumerableSet.UintSet private liquidityOrderIds; // [order ids..] uint64 public orderExecutionFee; // fee with native token required for the execution of the order by keepers // Asset list address[] public assetList; mapping(address => Asset) private assets; // Market list bytes10[] public marketList; mapping(bytes10 => Market) private markets; mapping(address => uint256) private utilizationMultipliers; // asset => utilization multiplier , for maxOI control mapping(address => uint256) private lpSupply; // asset => lp supply mapping(address => uint256) private balances; // asset => balance mapping(address => mapping(address => uint256)) private userLpBalances; // asset => account => lp amount mapping(address => uint256) private bufferBalances; // asset => balance mapping(address => uint256) private lastPaid; // asset => timestamp mapping(address => uint256) public currentEpochRemainingBuffer; // asset => buffer amount mapping(address => bool) public whitelistedKeepers; mapping(address => bool) public whitelistedDepositer; mapping(address => bool) public whitelistedFundingAccount; mapping(address => int256) private globalUPLs; // asset => upl mapping(address => uint256) public feeReserves; //treasury fees // Contracts AddressStorage public immutable addressStorage; PositionManager public positionManager; address public orderBookAddress; address public executorAddress; // Events event PoolDeposit( address indexed user, address indexed asset, uint256 amount, uint256 feeAmount, uint256 lpAmount, uint256 poolBalance ); event DirectPoolDeposit( address indexed user, address indexed asset, uint256 amount, uint256 bufferToPoolAmount, uint256 poolBalance, uint256 bufferBalance ); event PoolWithdrawal( address indexed user, address indexed asset, uint256 amount, uint256 feeAmount, uint256 lpAmount, uint256 poolBalance ); event PoolPayIn( address indexed user, address indexed asset, bytes10 market, uint256 amount, uint256 bufferToPoolAmount, uint256 poolBalance, uint256 bufferBalance ); event PoolPayOut( address indexed user, address indexed asset, bytes10 market, uint256 amount, uint256 bufferToPoolAmount, uint256 poolBalance, uint256 bufferBalance ); event FeeShareUpdated(uint256 feeShare); event BufferPayoutPeriodUpdated(uint256 period); event MaxLiquidityOrderTTLUpdated(uint256 maxLiquidityOrderTTL); event UtilizationMultiplierUpdated(address indexed asset, uint256 utilizationMultiplier); event PublicDepositUpdated(bool isPublicDeposit); event WhitelistedKeeperUpdated(address indexed keeper, bool isActive); event WhitelistedFundingAccountUpdated(address indexed account, bool isActive); event WhitelistedDepositerUpdated(address indexed account, bool isActive); event Link(address orderBook, address positionManager, address executor); event WithdrawFees(address indexed asset, uint256 amount); event AssetSet(address indexed asset,Asset assetInfo); event MarketSet(bytes10 indexed market,Market marketInfo); event BalanceIncrement(address indexed asset, uint256 amount); event FeeAdded(address indexed asset, uint256 amount); event GlobalUPLSet(address indexed asset, int256 upl); event TransferIn(address indexed asset, address indexed from, uint256 amount); event TransferOut(address indexed asset, address indexed to, uint256 amount); event BufferToPool(address indexed asset,uint256 lastPaid,uint256 amountToSendPool); event AddOrder(uint32 indexed orderId, LiquidityType orderType); event RemoveOrder(uint32 indexed orderId, LiquidityType orderType); event OrderCancelled(uint32 indexed orderId, address indexed user, address executionFeeReceiver, string reason); event OrderCreated( uint32 indexed liquidityOrderId, address indexed user, address indexed asset, LiquidityType orderType, uint256 amount, uint256 minAmountMinusTax, uint256 executionFee, address fundingAccount ); event OrderSkipped(uint32 indexed orderId, string reason); event OrderExecuted(uint32 indexed orderId, address indexed keeper, uint256 executionFee); event OrderExecutionFeeUpdated(uint64 orderExecutionFee); error Unauthorized(address account); /// @dev Only callable by PositionManager contract modifier onlyPositionManager() { if(msg.sender != address(positionManager)) revert Unauthorized(msg.sender); _; } /// @dev Only callable by PositionManager or Executor contracts modifier onlyPositionManagerAndExecutor() { if(msg.sender != address(positionManager) && msg.sender != executorAddress) revert Unauthorized(msg.sender); _; } /// @dev Only callable by PositionManager or OrderBook contracts modifier onlyPositionManagerAndOrderBook() { if(msg.sender != address(positionManager) && msg.sender != orderBookAddress) revert Unauthorized(msg.sender); _; } /// @dev Initializes addressStorage address constructor(AddressStorage _addressStorage) { addressStorage = _addressStorage; } /// @notice Initializes protocol contracts /// @dev Only callable by governance function link() external onlyGov { orderBookAddress = addressStorage.getAddress('OrderBook'); positionManager = PositionManager(addressStorage.getAddress('PositionManager')); executorAddress = addressStorage.getAddress('Executor'); emit Link(orderBookAddress, address(positionManager), executorAddress); } /// @notice withdraw treasury fees /// @dev Only callable by governance /// @param _asset address of asset function withdrawFees(address _asset) external override onlyGov { uint256 amount = feeReserves[_asset]; if (amount == 0) { return; } feeReserves[_asset] = 0; _transferOut(_asset, addressStorage.getAddress('treasury'), amount); emit WithdrawFees(_asset, amount); } /// @notice Set or update an asset /// @dev Only callable by governance /// @param _asset Asset address, e.g. address(0) for ETH /// @param _assetInfo Struct containing minSize and referencePriceFeed function setAsset(address _asset, Asset memory _assetInfo) external override onlyGov { assets[_asset] = _assetInfo; emit AssetSet(_asset, _assetInfo); uint256 length = assetList.length; for (uint256 i; i < length; i++) { if (assetList[i] == _asset) return; } assetList.push(_asset); } /// @notice Set or update a market /// @dev Only callable by governance /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" /// @param _marketInfo Market struct containing required market data function setMarket(bytes10 _market, Market memory _marketInfo) external override onlyGov { require(_marketInfo.fee <= MAX_FEE, '!max-fee'); require(_marketInfo.maxLeverage >= 1, '!max-leverage'); require(_marketInfo.maxDeviation <= MAX_DEVIATION, '!max-deviation'); require(_marketInfo.liqThreshold <= MAX_LIQTHRESHOLD, '!max-liqthreshold'); require(_marketInfo.minOrderAge <= MAX_MIN_ORDER_AGE, '!max-minorderage'); require(_marketInfo.pythMaxAge >= MIN_PYTH_MAX_AGE, '!min-pythmaxage'); markets[_market] = _marketInfo; emit MarketSet(_market, _marketInfo); uint256 length = marketList.length; for (uint256 i; i < length; i++) { // check if _market already exists, if yes return if (marketList[i] == _market) return; } marketList.push(_market); } /// @notice Set store fee /// @dev Only callable by governance /// @param _bps fee share in bps function setFeeShare(uint256 _bps) external override onlyGov { require(_bps < BPS_DIVIDER, '!bps'); feeShare = _bps; emit FeeShareUpdated(_bps); } /// @notice Set buffer payout period /// @dev Only callable by governance /// @param _period Buffer payout period in seconds, default is 7 days (604800 seconds) function setBufferPayoutPeriod(uint256 _period) external override onlyGov { require(_period > 0, '!min-period'); require(_period <= MAX_BUFFER_PAYOUT_PERIOD, '!max-period'); bufferPayoutPeriod = _period; emit BufferPayoutPeriodUpdated(_period); } /// @notice Set utilization multiplier /// @dev Only callable by governance /// @param _asset Asset address, e.g. address(0) for ETH /// @param _utilizationMultiplier utilization multiplier in bps ,e.g. if it is 5000 , maxOI available = asset balance x %50, it can be greater than bps according to use function setUtilizationMultiplier(address _asset, uint256 _utilizationMultiplier) external override onlyGov { require(_utilizationMultiplier > 0, '!min-utilization-multiplier'); utilizationMultipliers[_asset] = _utilizationMultiplier; emit UtilizationMultiplierUpdated(_asset,_utilizationMultiplier); } /// @notice Set buffer payout period /// @dev Only callable by governance /// @param _maxLiquidityOrderTTL Buffer payout period in seconds, default is 7 days (604800 seconds) function setMaxLiquidityOrderTTL(uint256 _maxLiquidityOrderTTL) external override onlyGov { require(_maxLiquidityOrderTTL > 0, '!min-order-ttl'); require(_maxLiquidityOrderTTL <= 1 hours, '!max-order-ttl'); maxLiquidityOrderTTL = _maxLiquidityOrderTTL; emit MaxLiquidityOrderTTLUpdated(_maxLiquidityOrderTTL); } /// @notice Set depositing public or private /// @dev Only callable by governance /// @param _isPublicDeposit whether depositin is public function setIsPublicDeposit(bool _isPublicDeposit) external override onlyGov { isPublicDeposit = _isPublicDeposit; emit PublicDepositUpdated(_isPublicDeposit); } /// @notice Whitelisted keeper that can set global upl /// @dev Only callable by governance /// @param _keeper Keeper address /// @param _isActive whether keeper is active function setWhitelistedKeeper(address _keeper, bool _isActive) external onlyGov { whitelistedKeepers[_keeper] = _isActive; emit WhitelistedKeeperUpdated(_keeper,_isActive); } /// @notice Set whitelisted Funding Account /// @dev Only callable by governance /// @param _account accounts authorized to deposit on behalf of another user /// @param _isActive whether account is active function setWhitelistedFundingAccount(address _account, bool _isActive) external onlyGov { whitelistedFundingAccount[_account] = _isActive; emit WhitelistedFundingAccountUpdated(_account,_isActive); } /// @notice Set whitelisted depositer /// @dev Only callable by governance /// @param _account accounts authorized to deposit /// @param _isActive whether account is active function setWhitelistedDepositer(address _account, bool _isActive) external override onlyGov { whitelistedDepositer[_account] = _isActive; emit WhitelistedDepositerUpdated(_account,_isActive); } /// @notice Set keeper gas fee for order execution /// @dev Only callable by governance /// @param _orderExecutionFee Fee with native token e.g. ETH function setOrderExecutionFee(uint64 _orderExecutionFee) external onlyGov { orderExecutionFee = _orderExecutionFee; emit OrderExecutionFeeUpdated(_orderExecutionFee); } /// @notice Increments pool balance /// @dev Only callable by PositionManager contract function incrementBalance(address _asset, uint256 _amount) external onlyPositionManager { balances[_asset] += _amount; emit BalanceIncrement(_asset, _amount); } /// @notice Increments treasury fees /// @dev Only callable by PositionManager contract function addFees(address _asset, uint256 _amount) external onlyPositionManager { feeReserves[_asset] += _amount; emit FeeAdded(_asset, _amount); } /// @notice Set global UPL, called by whitelisted keeper /// @param _assets Asset addresses /// @param _upls Corresponding total unrealized profit / loss function setGlobalUPLs(address[] calldata _assets, int256[] calldata _upls) external { if(!whitelistedKeepers[msg.sender]) revert Unauthorized(msg.sender); _setGlobalUPLs(_assets, _upls); } /// @notice Set global UPL, called by whitelisted keeper /// @param _assets Asset addresses /// @param _upls Corresponding total unrealized profit / loss function _setGlobalUPLs(address[] memory _assets, int256[] memory _upls) internal { for (uint256 i; i < _assets.length; i++) { globalUPLs[_assets[i]] = _upls[i]; emit GlobalUPLSet(_assets[i], _upls[i]); } } /// @notice Credit trader loss to buffer and pay pool from buffer amount based on time and payout rate /// @dev Only callable by PositionManager and Executor contracts /// @param _user User which incurred trading loss /// @param _asset Asset address, e.g. address(0) for ETH /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" /// @param _amount Amount of trader loss function creditTraderLoss(address _user, address _asset, bytes10 _market, uint256 _amount) external onlyPositionManagerAndExecutor { // pending buffer transfer to the pool uint256 amountToSendPool = _sendBufferToPool(_asset); // credit trader loss to buffer _incrementBufferBalance(_asset, _amount); // emit event emit PoolPayIn( _user, _asset, _market, _amount, amountToSendPool, balances[_asset], bufferBalances[_asset] ); } /// @notice Pay out trader profit, from buffer first then pool if buffer is depleted /// @dev Only callable by PositionManager contract /// @param _user Address to send funds to /// @param _asset Asset address, e.g. address(0) for ETH /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" /// @param _amount Amount of trader profit function debitTraderProfit( address _user, address _asset, bytes10 _market, uint256 _amount ) external onlyPositionManager { // return if profit = 0 if (_amount == 0) return; uint256 bufferBalance = bufferBalances[_asset]; // decrement buffer balance first _decrementBufferBalance(_asset, _amount); // decrement first next epoch buffer then current epoch remaining // if new buffer less than currentEpochRemaining, reduce currentEpochRemaining to buffer. if(bufferBalance < currentEpochRemainingBuffer[_asset] + _amount) { currentEpochRemainingBuffer[_asset] = bufferBalances[_asset]; } // if _amount is greater than available in the buffer, pay remaining from the pool if (_amount > bufferBalance) { uint256 diffToPayFromPool = _amount - bufferBalance; uint256 poolBalance = balances[_asset]; require(diffToPayFromPool < poolBalance, '!pool-balance'); _decrementBalance(_asset, diffToPayFromPool); } // pending buffer transfer to the pool uint256 amountToSendPool = _sendBufferToPool(_asset); // transfer profit out _transferOut(_asset, _user, _amount); // emit event emit PoolPayOut(_user, _asset, _market, _amount, amountToSendPool, balances[_asset], bufferBalances[_asset]); } /// @notice Transfers `_amount` of `_asset` in /// @dev Only callable by PositionManager or OrderBook contracts /// @param _asset Asset address, e.g. address(0) for ETH /// @param _from Address where asset is transferred from function transferIn(address _asset, address _from, uint256 _amount) external payable onlyPositionManagerAndOrderBook { _transferIn(_asset,_from,_amount); } /// @notice Transfers `_amount` of `_asset` out /// @dev Only callable by PositionManager or OrderBook contracts /// @param _asset Asset address, e.g. address(0) for ETH /// @param _to Address where asset is transferred to function transferOut(address _asset, address _to, uint256 _amount) external nonReentrant onlyPositionManagerAndOrderBook { _transferOut(_asset,_to,_amount); } /// @notice Direct Pool Deposit `_amount` of `_asset` into the pool via buffer /// @param _asset Asset address, e.g. address(0) for ETH /// @param _amount Amount to be deposited function directPoolDeposit(address _asset, uint256 _amount) external payable { require(_amount > 0, '!_amount'); require(isSupported(_asset), '!_asset'); // if _asset is ETH (address(0)), set _amount to msg.value if (_asset == address(0)) { require(msg.value > 0, '!msg.value'); _amount = msg.value; } else { _transferIn(_asset, msg.sender, _amount); } // first the pending buffer will be transferred to the pool uint256 amountToSendPool = _sendBufferToPool(_asset); // direct deposit to buffer _incrementBufferBalance(_asset, _amount); // emit event emit DirectPoolDeposit( msg.sender, _asset, _amount, amountToSendPool, balances[_asset], bufferBalances[_asset] ); } /// @notice Cancels order /// @param _orderId Order to cancel function cancelOrder(uint32 _orderId) external { LiquidityOrder memory order = liquidityOrders[_orderId]; require(order.amount > 0, '!order'); require(order.user == msg.sender, '!user'); _cancelOrder(_orderId, 'by-user', msg.sender); } /// @notice Submits a new deposit order /// @param _params Liquidity order to submit function depositRequest( LiquidityOrder memory _params ) external payable { if(!isPublicDeposit){ require(whitelistedDepositer[msg.sender], '!whitelisted'); } require(_params.amount > 0, '!_amount'); require(_params.amount >= _params.minAmountMinusTax, '!min-amount'); require(isSupported(_params.asset), '!_asset'); _params.user = _orderUser(_params); _params.orderType = LiquidityType.DEPOSIT; _params.timestamp = block.timestamp.toUint32(); _params.executionFee = orderExecutionFee; // if _asset is ETH (address(0)), msg.value must be equal to amount + execution fee if (_params.asset == address(0)) { require(msg.value == _params.amount + _params.executionFee, '!msg.value'); } else { require(msg.value == _params.executionFee, '!msg.value'); _transferIn(_params.asset, msg.sender, _params.amount); } // Add order to store and emit event _params.liquidityOrderId = _add(_params); emit OrderCreated( _params.liquidityOrderId, _params.user, _params.asset, _params.orderType, _params.amount, _params.minAmountMinusTax, _params.executionFee, msg.sender ); } /// @notice Submits a new withdraw order /// @param _params Liquidity order to submit function withdrawRequest( LiquidityOrder memory _params ) external payable { require(_params.amount > 0, '!_amount'); require(msg.value == orderExecutionFee, '!msg.value'); require(isSupported(_params.asset), '!_asset'); _params.user = msg.sender; _params.orderType = LiquidityType.WITHDRAW; _params.timestamp = block.timestamp.toUint32(); _params.executionFee = orderExecutionFee; // check pool balance and lp supply uint256 balance = balances[_params.asset]; uint256 lpAssetSupply = lpSupply[_params.asset]; require(balance > 0 && lpAssetSupply > 0, '!empty'); // check user balance uint256 userBalance = getUserBalance(_params.asset, _params.user); if (_params.amount > userBalance) _params.amount = userBalance.toUint96(); require(_params.amount >= _params.minAmountMinusTax, '!min-amount'); // check available liquidity for open interests // if utilizationMultiplier is defined less than BPS_DIVIDER, allow user to withdraw with 1:1 ratio uint256 utilizationMultiplier = utilizationMultipliers[_params.asset]; if(utilizationMultiplier < BPS_DIVIDER) utilizationMultiplier = BPS_DIVIDER; require(positionManager.getAssetOI(_params.asset) <= (getAvailable(_params.asset) - _params.amount) * utilizationMultiplier / BPS_DIVIDER,"!not-available-liquidity"); // Add order to store and emit event _params.liquidityOrderId = _add(_params); emit OrderCreated( _params.liquidityOrderId, _params.user, _params.asset, _params.orderType, _params.amount, _params.minAmountMinusTax, _params.executionFee, address(0) ); } /// @notice Order execution by keeper with global upls /// @dev Only callable by whitelistedKeepers /// @param _orderIds order id's to execute /// @param _assets Array of Asset /// @param _upls Array of Asset upls function executeOrders( uint32[] calldata _orderIds, address[] calldata _assets, int256[] calldata _upls ) external nonReentrant { if(!whitelistedKeepers[msg.sender]) revert Unauthorized(msg.sender); _setGlobalUPLs(_assets,_upls); for (uint256 i; i < _assets.length; i++) { // pending buffer transfer to the pool _sendBufferToPool(_assets[i]); } for (uint256 i; i < _orderIds.length; i++) { (bool status, string memory reason) = _executeOrder(_orderIds[i], msg.sender); if (!status) _cancelOrder(_orderIds[i], reason, msg.sender); } } /// @notice Returns asset struct of `_asset` /// @param _asset Asset address, e.g. address(0) for ETH function getAsset(address _asset) external view returns (Asset memory) { return assets[_asset]; } /// @notice Get a list of all supported assets function getAssetList() external view returns (address[] memory) { return assetList; } /// @notice Get number of supported assets function getAssetCount() external view returns (uint256) { return assetList.length; } /// @notice Returns asset address at `_index` /// @param _index index of asset function getAssetByIndex(uint256 _index) external view returns (address) { return assetList[_index]; } /// @notice Returns market struct of `market` /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" function getMarket(bytes10 _market) external view override returns (Market memory) { return markets[_market]; } /// @notice Returns market struct array of specified markets /// @param _markets Array of market bytes10, e.g. ["0x4554482D555344000000", "0x4254432D555344000000"] for ETH-USD and BTC-USD function getMarketMany(bytes10[] calldata _markets) external view returns (Market[] memory) { uint256 length = _markets.length; Market[] memory _marketInfos = new Market[](length); for (uint256 i; i < length; i++) { _marketInfos[i] = markets[_markets[i]]; } return _marketInfos; } /// @notice Returns market identifier at `index` /// @param index index of marketList function getMarketByIndex(uint256 index) external view returns (bytes10) { return marketList[index]; } /// @notice Get a list of all supported markets function getMarketList() external view returns (bytes10[] memory) { return marketList; } /// @notice Get number of supported markets function getMarketCount() external view returns (uint256) { return marketList.length; } /// @notice Returns the sum of buffer and pool balance of `_asset` /// @param _asset Asset address, e.g. address(0) for ETH function getAvailable(address _asset) public view returns (uint256) { return balances[_asset] + bufferBalances[_asset]; } /// @notice Returns the sum of buffer and pool balance of `_asset` multiplied by utilization multiplier /// @dev For available OI control for new position /// @param _asset Asset address, e.g. address(0) for ETH function getAvailableForOI(address _asset) external view returns (uint256) { return ((balances[_asset] + bufferBalances[_asset]) * utilizationMultipliers[_asset]) / BPS_DIVIDER; } /// @notice Returns amount of `_asset` in pool /// @param _asset Asset address, e.g. address(0) for ETH function getBalance(address _asset) external view returns (uint256) { return balances[_asset]; } /// @notice Returns pool balances of `_assets` function getBalances(address[] calldata _assets) external view returns (uint256[] memory) { uint256 length = _assets.length; uint256[] memory _balances = new uint256[](length); for (uint256 i; i < length; i++) { _balances[i] = balances[_assets[i]]; } return _balances; } /// @notice Returns buffer balances of `_assets` function getBufferBalances(address[] calldata _assets) external view returns (uint256[] memory) { uint256 length = _assets.length; uint256[] memory _balances = new uint256[](length); for (uint256 i; i < length; i++) { _balances[i] = bufferBalances[_assets[i]]; } return _balances; } /// @notice Returns `_assets` balance of `account` function getUserBalances(address[] calldata _assets, address account) external view returns (uint256[] memory) { uint256 length = _assets.length; uint256[] memory _balances = new uint256[](length); for (uint256 i; i < length; i++) { _balances[i] = getUserBalance(_assets[i], account); } return _balances; } /// @notice Returns last time pool was paid /// @param _asset Asset address, e.g. address(0) for ETH function getLastPaid(address _asset) external view returns (uint256) { return lastPaid[_asset]; } /// @notice Returns total amount of LP for `_asset` function getLpSupply(address _asset) external view returns (uint256) { return lpSupply[_asset]; } /// @notice Returns total unrealized p/l for `_asset` function getGlobalUPL(address _asset) external view returns (int256) { return globalUPLs[_asset]; } /// @notice Returns liquidity orders /// @param _length Amount of liquidity orders to return function getLiquidityOrders(uint256 _length) external view returns (LiquidityOrder[] memory) { uint32 orderlength = liquidityOrderIds.length().toUint32(); if (_length > orderlength) _length = orderlength; LiquidityOrder[] memory _orders = new LiquidityOrder[](_length); for (uint256 i; i < _length; i++) { _orders[i] = liquidityOrders[liquidityOrderIds.at(i).toUint32()]; } return _orders; } /// @notice Returns orders of `user` function getUserOrders(address _user) external view returns (LiquidityOrder[] memory) { uint32 length = userLiquidityOrderIds[_user].length().toUint32(); LiquidityOrder[] memory _orders = new LiquidityOrder[](length); for (uint256 i; i < length; i++) { _orders[i] = liquidityOrders[userLiquidityOrderIds[_user].at(i).toUint32()]; } return _orders; } /// @notice Returns amount of market orders function getLiquidityOrderCount() external view returns (uint256) { return liquidityOrderIds.length(); } /// @notice Returns order amount of `user` function getUserOrderCount(address _user) external view returns (uint256) { return userLiquidityOrderIds[_user].length(); } /// @notice Get order user /// @dev if sender is whitelisted funding account and order is suitable, The user is being made incoming in the params /// @param _params Order params /// @return user order user function _orderUser(LiquidityOrder memory _params) internal view returns (address) { if(whitelistedFundingAccount[msg.sender]){ require(_params.user != address(0) ,"funding-account-order-fail"); return _params.user; } return msg.sender; } /// @notice Returns pool deposit tax for `asset` and amount in bps function getDepositTaxBps(address _asset, uint256 _amount) public view returns (uint256) { uint256 taxBps; uint256 balance = balances[_asset]; uint256 bufferBalance = bufferBalances[_asset]; if (globalUPLs[_asset] - int256(bufferBalance) < 0) { taxBps = uint256(int256(BPS_DIVIDER) * (int256(bufferBalance) - globalUPLs[_asset]) / (int256(balance) + int256(_amount))); } return taxBps; } /// @notice Returns pool withdrawal tax for `asset` and amount in bps function getWithdrawalTaxBps(address _asset, uint256 _amount) public view returns (uint256) { uint256 taxBps; uint256 balance = balances[_asset]; if (_amount > balance) return BPS_DIVIDER; uint256 bufferBalance = bufferBalances[_asset]; if (globalUPLs[_asset] - int256(bufferBalance) > 0 ) { if(_amount < balance) taxBps = uint256(int256(BPS_DIVIDER) * (globalUPLs[_asset] - int256(bufferBalance)) / (int256(balance) - int256(_amount))); else // _amount = balance => in fact if there is an open position, the last depositor cannot withdraw the entire balance, so this will not happen, but it may remain as a calculation taxBps = uint256(int256(BPS_DIVIDER) * (globalUPLs[_asset] - int256(bufferBalance)) / int256(_amount)); } return taxBps; } /// @notice Returns `_asset` balance of `_account` function getUserBalance(address _asset, address _account) public view returns (uint256) { if (lpSupply[_asset] == 0) return 0; return (userLpBalances[_asset][_account] * balances[_asset]) / lpSupply[_asset]; } /// @notice Returns amount of LP of `_account` for `_asset` function getUserLpBalance(address _asset, address _account) external view returns (uint256) { return userLpBalances[_asset][_account]; } /// @notice Returns true if `_asset` is supported /// @param _asset Asset address, e.g. address(0) for ETH function isSupported(address _asset) public view returns (bool) { return assets[_asset].minSize > 0; } /// @notice Returns amount of `_asset` in buffer function getBufferBalance(address _asset) external view returns (uint256) { return bufferBalances[_asset]; } /// @notice Increments pool balance /// @dev Internal function function _incrementBalance(address _asset, uint256 _amount) internal { balances[_asset] += _amount; } /// @notice Decrements pool balance /// @dev Internal function function _decrementBalance(address _asset, uint256 _amount) internal { balances[_asset] = balances[_asset] <= _amount ? 0 : balances[_asset] - _amount; } /// @notice Increments buffer balance /// @dev Internal function function _incrementBufferBalance(address _asset, uint256 _amount) internal { bufferBalances[_asset] += _amount; } /// @notice Decrements buffer balance /// @dev Internal function function _decrementBufferBalance(address _asset, uint256 _amount) internal { bufferBalances[_asset] = bufferBalances[_asset] <= _amount ? 0 : bufferBalances[_asset] - _amount; } /// @notice Updates `lastPaid` /// @dev Internal function function _setLastPaid(address _asset, uint256 _timestamp) internal { lastPaid[_asset] = _timestamp; } /// @notice Increments `lpSupply` and `userLpBalances` /// @dev Internal function function _incrementUserLpBalance(address _asset, address _user, uint256 _amount) internal { lpSupply[_asset] += _amount; unchecked { // Overflow not possible: balance + _amount is at most lpSupply + _amount, which is checked above. userLpBalances[_asset][_user] += _amount; } } /// @notice Decrements `lpSupply` and `userLpBalances` /// @dev Internal function function _decrementUserLpBalance(address asset, address _user, uint256 amount) internal { lpSupply[asset] = lpSupply[asset] <= amount ? 0 : lpSupply[asset] - amount; userLpBalances[asset][_user] = userLpBalances[asset][_user] <= amount ? 0 : userLpBalances[asset][_user] - amount; } /// @notice Stream buffer balance progressively into the pool /// @dev Internal function function _sendBufferToPool(address _asset) internal returns (uint256) { // local variables uint256 bufferBalance = bufferBalances[_asset]; uint256 lpAsset = lastPaid[_asset]; uint256 currentTimestamp = block.timestamp; uint256 amountToSendPool; if(bufferBalance > 0 ){ uint256 currentEpochStart = currentTimestamp / bufferPayoutPeriod * bufferPayoutPeriod; uint256 currentEpochRemaining = currentEpochRemainingBuffer[_asset]; if(lpAsset < currentEpochStart - bufferPayoutPeriod){ // if the last transfer is before 2 periods, all buffer must be transferred amountToSendPool = bufferBalance; currentEpochRemaining = 0; }else{ if(lpAsset < currentEpochStart){ // previous epoch remaining amountToSendPool = currentEpochRemaining; currentEpochRemaining = bufferBalance - amountToSendPool ; //new epoch buffer amount lpAsset = currentEpochStart; } if(currentEpochRemaining > 0 ){ uint256 transferAmount = currentEpochRemaining * (currentTimestamp - lpAsset) / (currentEpochStart + bufferPayoutPeriod - lpAsset); if(transferAmount > currentEpochRemaining) transferAmount = currentEpochRemaining; amountToSendPool += transferAmount; currentEpochRemaining -= transferAmount; } if (amountToSendPool >= bufferBalance) { //if true, currentEpochRemainingBuffer must be empty amountToSendPool = bufferBalance; currentEpochRemaining = 0; } } currentEpochRemainingBuffer[_asset] = currentEpochRemaining; // update storage if(amountToSendPool > 0){ _incrementBalance(_asset, amountToSendPool); _decrementBufferBalance(_asset, amountToSendPool); } } _setLastPaid(_asset, currentTimestamp); emit BufferToPool(_asset, currentTimestamp, amountToSendPool); return amountToSendPool; } /// @notice Transfers `_amount` of `_asset` in /// @dev Internal function /// @param _asset Asset address, e.g. address(0) for ETH /// @param _from Address where asset is transferred from function _transferIn(address _asset, address _from, uint256 _amount) internal { if (_amount == 0 || _asset == address(0)) return; IERC20(_asset).safeTransferFrom(_from, address(this), _amount); emit TransferIn(_asset, _from, _amount); } /// @notice Transfers `_amount` of `_asset` out /// @dev Internal function /// @param _asset Asset address, e.g. address(0) for ETH /// @param _to Address where asset is transferred to function _transferOut(address _asset, address _to, uint256 _amount) internal{ if (_amount == 0 || _to == address(0)) return; if (_asset == address(0)) { payable(_to).sendValue(_amount); } else { IERC20(_asset).safeTransfer(_to, _amount); } emit TransferOut(_asset, _to, _amount); } /// @dev Executes submitted order /// @param _orderId Order to execute /// @param _keeper keeper address /// @return status if true, order is not canceled. /// @return message if not blank, includes order revert message. function _executeOrder( uint32 _orderId, address _keeper ) internal returns (bool, string memory) { LiquidityOrder memory order = liquidityOrders[_orderId]; if (order.amount == 0) { return (false, '!order'); } if (order.timestamp + maxLiquidityOrderTTL < block.timestamp ) { return (false, '!expired'); } if(order.orderType == LiquidityType.DEPOSIT ){ uint256 taxBps = getDepositTaxBps(order.asset, order.amount); if(taxBps >= BPS_DIVIDER){ return (false, '!tax'); } uint256 tax = (order.amount * taxBps) / BPS_DIVIDER; uint256 amountMinusTax = order.amount - tax; if(amountMinusTax < order.minAmountMinusTax){ return(false, '!min-amount'); } // pool share is equal to pool balance of _user divided by the total balance uint256 balance = balances[order.asset]; uint256 lpAssetSupply = lpSupply[order.asset]; uint256 lpAmount = balance == 0 || lpAssetSupply == 0 ? amountMinusTax : (amountMinusTax * lpAssetSupply) / balance; // increment balances _incrementUserLpBalance(order.asset, order.user, lpAmount); _incrementBalance(order.asset, order.amount); // emit event emit PoolDeposit(order.user, order.asset, order.amount, tax, lpAmount, balances[order.asset]); } else if(order.orderType == LiquidityType.WITHDRAW ){ // check available liquidity for open interests // if utilizationMultiplier is defined less than BPS_DIVIDER, allow user to withdraw with 1:1 ratio uint256 utilizationMultiplier = utilizationMultipliers[order.asset]; if(utilizationMultiplier < BPS_DIVIDER) utilizationMultiplier = BPS_DIVIDER; if((getAvailable(order.asset) - order.amount) * utilizationMultiplier / BPS_DIVIDER < positionManager.getAssetOI(order.asset)){ return (false, '!not-available-liquidity'); } // withdrawal tax uint256 taxBps = getWithdrawalTaxBps(order.asset, order.amount); if(taxBps >= BPS_DIVIDER){ return (false, '!tax'); } uint256 tax = (order.amount * taxBps) / BPS_DIVIDER; uint256 amountMinusTax = order.amount - tax; if(amountMinusTax < order.minAmountMinusTax){ return(false, '!min-amount'); } // LP amount uint256 balance = balances[order.asset]; uint256 lpAssetSupply = lpSupply[order.asset]; uint256 lpAmount = (order.amount * lpAssetSupply) / balance; // decrement balances _decrementUserLpBalance(order.asset, order.user, lpAmount); _decrementBalance(order.asset, amountMinusTax); // transfer funds out _transferOut(order.asset, order.user, amountMinusTax); // emit event emit PoolWithdrawal(order.user, order.asset, order.amount, tax, lpAmount, balances[order.asset]); } _remove(_orderId); if(order.executionFee > 0){ _transferOut(address(0), _keeper, order.executionFee); } emit OrderExecuted(_orderId, _keeper, order.executionFee); return (true, ''); } /// @notice Adds order to storage /// @dev Internal function function _add(LiquidityOrder memory _order) internal returns (uint32) { uint32 nextOrderId = ++liquidityOid; _order.liquidityOrderId = nextOrderId; liquidityOrders[nextOrderId] = _order; userLiquidityOrderIds[_order.user].add(nextOrderId); liquidityOrderIds.add(nextOrderId); emit AddOrder(nextOrderId, _order.orderType); return nextOrderId; } /// @notice Removes order from store /// @dev Internal function /// @param _orderId Order to remove function _remove(uint32 _orderId) internal { LiquidityOrder memory order = liquidityOrders[_orderId]; if (order.amount == 0) return; userLiquidityOrderIds[order.user].remove(_orderId); liquidityOrderIds.remove(_orderId); emit RemoveOrder(_orderId, order.orderType); delete liquidityOrders[_orderId]; } /// @notice Cancels order /// @dev Internal function without access restriction /// @param _orderId Order to cancel /// @param _reason Cancellation reason /// @param _executionFeeReceiver Address of execution fee receiver function _cancelOrder(uint32 _orderId, string memory _reason, address _executionFeeReceiver) internal { LiquidityOrder memory order = liquidityOrders[_orderId]; if (order.amount == 0) return; _remove(_orderId); bool isSentNative; if (order.orderType == LiquidityType.DEPOSIT) { isSentNative = order.asset == address(0) && order.user == _executionFeeReceiver; _transferOut(order.asset, order.user, order.amount + (isSentNative ? order.executionFee : 0)); } if(order.executionFee > 0 && !isSentNative){ _transferOut(address(0), _executionFeeReceiver, order.executionFee); } emit OrderCancelled(_orderId, order.user, _executionFeeReceiver, _reason); } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.22; import '../utils/Governable.sol'; import './interfaces/IAddressStorage.sol'; /// @title AddressStorage /// @notice General purpose address storage contract /// @dev Access is restricted to governance contract AddressStorage is Governable,IAddressStorage { mapping(bytes32 => address) public addressValues; event AddressSet(string indexed key, address value); /// @param key The key for the record /// @param value address to store /// @param overwrite Overwrites existing value if set to true function setAddress(string calldata key, address value, bool overwrite) external override onlyGov returns (bool) { require(value != address(0), "!zero address"); bytes32 hash = getHash(key); if (overwrite || addressValues[hash] == address(0)) { addressValues[hash] = value; emit AddressSet(key, value); return true; } return false; } /// @param key The key for the record function getAddress(string calldata key) external view returns (address) { return addressValues[getHash(key)]; } /// @param key string to hash function getHash(string memory key) public pure returns (bytes32) { return keccak256(abi.encodePacked(key)); } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.22; /// @title Governable /// @notice Basic access control mechanism, gov has access to certain functions contract Governable { address public gov; event SetGov(address prevGov, address nextGov); error NotGovernance(address account); /// @dev Initializes the contract setting the deployer address as governance constructor() { _setGov(msg.sender); } /// @dev Reverts if called by any account other than gov modifier onlyGov() { if(msg.sender != gov) revert NotGovernance(msg.sender); _; } /// @notice Sets a new governance address /// @dev Only callable by governance function setGov(address _gov) external onlyGov { require(_gov != address(0), "!zero address"); _setGov(_gov); } /// @notice Sets a new governance address /// @dev Internal function without access restriction function _setGov(address _gov) internal { address prevGov = gov; gov = _gov; emit SetGov(prevGov, _gov); } }
//SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.22; interface IAddressStorage { function setAddress(string calldata key, address value, bool overwrite) external returns (bool) ; }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.22; interface IReferencePriceFeed { function getPrice(address _feed) external view returns (uint256); }
//SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.22; interface IStore { // Structs struct Asset { uint8 decimals; uint88 minSize; // minimum position size address referencePriceFeed; } struct Market { bytes32 name; // Market's full name, e.g. Bitcoin / U.S. Dollar bytes12 category; // crypto, fx, commodities, or indices address referencePriceFeed; // Reference Price feed contract address bytes32 pythFeed; // Pyth price feed id uint16 maxLeverage; // No decimals uint16 maxDeviation; // In bps, max price difference from oracle to referencePrice uint16 fee; // In bps. 10 = 0.1% uint16 liqThreshold; // In bps uint16 fundingFactor; // Yearly funding rate if OI is completely skewed to one side. In bps. uint8 minOrderAge; // Min order age before is can be executed. In seconds uint8 pythMaxAge; // Max Pyth submitted price age, in seconds bool isReduceOnly; // accepts only reduce only orders uint16 priceConfidenceThresholds; // in bps. if threshold is higher than pyth price confidence, pyth price is used uint16 priceConfidenceMultipliers; // in bps. if threshold is lower than pyth price confidence, pyth price confidence multiplied by multiplier and add or remove pyth price according to min max price // e.g pyth price = 1000, pyth confidence = 2 (0.2%), threshold = 10 (0.1%), multiplier= 20000 (200%) => max price = 1000 + (2*2) = 1004, min price = 1000 - (2*2) = 996 } function setAsset(address _asset, Asset memory _assetInfo) external; function setMarket(bytes10 _market, Market memory _marketInfo) external; function setFeeShare(uint256 _bps) external; function setBufferPayoutPeriod(uint256 _period) external; function setMaxLiquidityOrderTTL(uint256 _maxLiquidityOrderTTL) external; function setUtilizationMultiplier(address _asset, uint256 _utilizationMultiplier) external; function withdrawFees(address _asset) external; function setIsPublicDeposit(bool _isPublicDeposit) external; function setWhitelistedDepositer(address _account, bool _isActive) external; function getMarket(bytes10 _market) external view returns (Market memory); }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.22; import '../utils/Governable.sol'; import './AddressStorage.sol'; import '../Store.sol'; import '../PositionManager.sol'; /// @title TradingValidator /// @notice Implementation of risk mitigation measures such as maximum open interest and maximum pool drawdown contract TradingValidator is Governable { // Constants uint256 public constant BPS_DIVIDER = 10000; mapping(bytes10 => mapping(address => uint256)) private maxOI; // market => asset => amount // Pool Risk Measures uint256 public poolHourlyDecay = 417; // bps 4.17% hourly, disappears after 24 hours mapping(address => int256) private poolProfitTracker; // asset => amount (amortized) mapping(address => uint256) private poolProfitLimit; // asset => bps mapping(address => uint256) private poolLastChecked; // asset => timestamp event Link(address store, address positionManager); event MaxOISet(address indexed asset, bytes10 indexed market, uint256 amount); event PoolHourlyDecaySet(uint256 poolHourlyDecay); event PoolProfitLimitSet(address indexed asset, uint256 poolProfitLimit); // Contracts AddressStorage public immutable addressStorage; Store public store; PositionManager public positionManager; /// @dev Only callable by PositionManager contract modifier onlyPositionManager() { require(msg.sender == address(positionManager), "!unauthorized"); _; } /// @dev Initializes addressStorage address constructor(AddressStorage _addressStorage) { addressStorage = _addressStorage; } /// @notice Initializes protocol contracts /// @dev Only callable by governance function link() external onlyGov { store = Store(addressStorage.getAddress('Store')); positionManager = PositionManager(addressStorage.getAddress('PositionManager')); emit Link(address(store), address(positionManager)); } /// @notice Set maximum open interest /// @notice Once current open interest exceeds this value, orders are no longer accepted /// @dev Only callable by governance /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" /// @param _asset Address of base asset, e.g. address(0) for ETH /// @param _amount Max open interest to set function setMaxOI(bytes10 _market, address _asset, uint256 _amount) external onlyGov { require(_amount > 0, '!amount'); maxOI[_market][_asset] = _amount; emit MaxOISet(_asset, _market, _amount); } /// @notice Set hourly pool decay /// @dev Only callable by governance /// @param _poolHourlyDecay Hourly pool decay in bps function setPoolHourlyDecay(uint256 _poolHourlyDecay) external onlyGov { require(_poolHourlyDecay < BPS_DIVIDER, '!poolHourlyDecay'); poolHourlyDecay = _poolHourlyDecay; emit PoolHourlyDecaySet(_poolHourlyDecay); } /// @notice Set pool profit limit of `asset` /// @dev Only callable by governance /// @param _asset Address of asset, e.g. address(0) for ETH /// @param _poolProfitLimit Pool profit limit in bps function setPoolProfitLimit(address _asset, uint256 _poolProfitLimit) external onlyGov { require(_poolProfitLimit < BPS_DIVIDER, '!poolProfitLimit'); poolProfitLimit[_asset] = _poolProfitLimit; emit PoolProfitLimitSet(_asset, _poolProfitLimit); } /// @notice Measures the net loss of a pool over time /// @notice Reverts if time-weighted drawdown is higher than the allowed profit limit /// @dev Only callable by other protocol contracts /// @dev Invoked by Positions.decreasePosition /// @param _asset Address of asset, e.g. address(0) for ETH /// @param _pnl Profit Loss amount function checkPoolDrawdown(address _asset, int256 _pnl) external onlyPositionManager { // Get available amount of `_asset` in the pool (pool balance + buffer balance) uint256 poolAvailable = store.getAvailable(_asset); // Get profit tracker, _pnl > 0 means trader win int256 profitTracker = getPoolProfitTracker(_asset) + _pnl; // get profit limit of pool uint256 profitLimit = poolProfitLimit[_asset]; // update storage vars poolProfitTracker[_asset] = profitTracker; poolLastChecked[_asset] = block.timestamp; // return if profit limit or profit tracker is zero / less than zero if (profitLimit == 0 || profitTracker <= 0) return; // revert if profitTracker > profitLimit * available funds require(uint256(profitTracker) < (profitLimit * poolAvailable) / BPS_DIVIDER, '!pool-risk'); } /// @notice Checks if maximum open interest is reached /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" /// @param _asset Address of base asset, e.g. address(0) for ETH function checkMaxOI(address _asset, bytes10 _market, uint256 _size) external view { uint256 openInterest = positionManager.getOI(_asset, _market); uint256 _maxOI = maxOI[_market][_asset]; if (_maxOI > 0 && openInterest + _size > _maxOI) revert('!max-oi'); uint256 openAssetInterest = positionManager.getAssetOI(_asset); uint256 assetMaxUtilization = store.getAvailableForOI(_asset); if (openAssetInterest + _size > assetMaxUtilization) revert('!_asset-max-oi'); } /// @notice Get maximum open interest of `_market` /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" /// @param _asset Address of base asset, e.g. address(0) for ETH function getMaxOI(bytes10 _market, address _asset) external view returns (uint256) { return maxOI[_market][_asset]; } /// @notice Returns pool profit tracker of `_asset` /// @dev Amortized every hour by 4.16% unless otherwise set function getPoolProfitTracker(address _asset) public view returns (int256) { int256 profitTracker = poolProfitTracker[_asset]; uint256 lastCheckedHourId = poolLastChecked[_asset] / (1 hours); uint256 currentHourId = block.timestamp / (1 hours); if (currentHourId > lastCheckedHourId) { // hours passed since last check uint256 hoursPassed = currentHourId - lastCheckedHourId; if (hoursPassed >= (BPS_DIVIDER + poolHourlyDecay -1) / poolHourlyDecay) { profitTracker = 0; } else { profitTracker = (profitTracker * (int256(BPS_DIVIDER) - int256(poolHourlyDecay) * int256(hoursPassed))) / int256(BPS_DIVIDER); } } return profitTracker; } /// @notice Returns pool profit limit of `_asset` function getPoolProfitLimit(address _asset) external view returns (uint256) { return poolProfitLimit[_asset]; } }
{ "optimizer": { "enabled": true, "runs": 200 }, "viaIR": true, "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":"contract AddressStorage","name":"_addressStorage","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"NotGovernance","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"signed","type":"bool"},{"indexed":false,"internalType":"bool","name":"byGov","type":"bool"}],"name":"AccountApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"orderId","type":"uint32"},{"indexed":false,"internalType":"uint8","name":"orderType","type":"uint8"}],"name":"AddOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"ethSignedMessageHash","type":"bytes32"}],"name":"EthSignedMessageHashUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"store","type":"address"},{"indexed":false,"internalType":"address","name":"tradingValidator","type":"address"},{"indexed":false,"internalType":"address","name":"referralStorage","type":"address"},{"indexed":false,"internalType":"address","name":"executor","type":"address"},{"indexed":false,"internalType":"address","name":"positionManager","type":"address"}],"name":"Link","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxMarketOrderTTL","type":"uint256"}],"name":"MaxMarketOrderTTLUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxTriggerOrderTTL","type":"uint256"}],"name":"MaxTriggerOrderTTLUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"orderPaused","type":"bool"}],"name":"NewOrdersPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"orderId","type":"uint32"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"executionFeeReceiver","type":"address"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"orderId","type":"uint32"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"bytes10","name":"market","type":"bytes10"},{"indexed":false,"internalType":"uint96","name":"margin","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"size","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"price","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"fee","type":"uint96"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint8","name":"orderType","type":"uint8"},{"indexed":false,"internalType":"bool","name":"isReduceOnly","type":"bool"},{"indexed":false,"internalType":"uint32","name":"expiry","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"indexed":false,"internalType":"address","name":"fundingAccount","type":"address"},{"indexed":false,"internalType":"uint64","name":"executionFee","type":"uint64"},{"indexed":false,"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"name":"OrderCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"orderExecutionFee","type":"uint64"}],"name":"OrderExecutionFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"processingPaused","type":"bool"}],"name":"ProcessingPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"orderId","type":"uint32"},{"indexed":false,"internalType":"uint8","name":"orderType","type":"uint8"}],"name":"RemoveOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"prevGov","type":"address"},{"indexed":false,"internalType":"address","name":"nextGov","type":"address"}],"name":"SetGov","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"isActive","type":"bool"}],"name":"WhitelistedFundingAccountUpdated","type":"event"},{"inputs":[],"name":"BPS_DIVIDER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TRAILING_STOP_PERCENTAGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order","name":"_order","type":"tuple"}],"name":"add","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addressStorage","outputs":[{"internalType":"contract AddressStorage","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedAccounts","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"areNewOrdersPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_orderId","type":"uint32"}],"name":"cancelOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_orderId","type":"uint32"},{"internalType":"string","name":"_reason","type":"string"},{"internalType":"address","name":"_executionFeeReceiver","type":"address"}],"name":"cancelOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"_orderIds","type":"uint32[]"}],"name":"cancelOrders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enableOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"enableOrderByGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ethSignedMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"executorAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_orderId","type":"uint32"}],"name":"get","outputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"_orderIds","type":"uint32[]"}],"name":"getMany","outputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarketOrderCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_length","type":"uint256"}],"name":"getMarketOrders","outputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTriggerOrderCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_length","type":"uint256"},{"internalType":"uint256","name":"_offset","type":"uint256"}],"name":"getTriggerOrders","outputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getUserOrderCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getUserOrders","outputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isProcessingPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"link","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxMarketOrderTTL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTriggerOrderTTL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oid","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"orderExecutionFee","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionManager","outputs":[{"internalType":"contract PositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referralStorage","outputs":[{"internalType":"contract IReferralStorage","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_orderId","type":"uint32"}],"name":"remove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_areNewOrdersPaused","type":"bool"}],"name":"setAreNewOrdersPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_messageHash","type":"bytes32"}],"name":"setEthSignedMessageHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gov","type":"address"}],"name":"setGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isProcessingPaused","type":"bool"}],"name":"setIsProcessingPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxMarketOrderTTL","type":"uint256"}],"name":"setMaxMarketOrderTTL","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxTriggerOrderTTL","type":"uint256"}],"name":"setMaxTriggerOrderTTL","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_orderExecutionFee","type":"uint64"}],"name":"setOrderExecutionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_isActive","type":"bool"}],"name":"setWhitelistedFundingAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"store","outputs":[{"internalType":"contract Store","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order","name":"_params","type":"tuple"},{"internalType":"uint96","name":"_tpPrice","type":"uint96"},{"internalType":"uint96","name":"_slPrice","type":"uint96"},{"internalType":"uint16","name":"_trailingStopPercentage","type":"uint16"},{"internalType":"bytes32","name":"_referralCode","type":"bytes32"}],"name":"submitOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order","name":"_params","type":"tuple"},{"internalType":"uint96","name":"_tpPrice","type":"uint96"},{"internalType":"uint96","name":"_slPrice","type":"uint96"},{"internalType":"uint16","name":"_trailingStopPercentage","type":"uint16"},{"internalType":"bytes32","name":"_referralCode","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"submitOrderWithSignature","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"tradingValidator","outputs":[{"internalType":"contract TradingValidator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedFundingAccount","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60a034620000c757601f620046ed38819003918201601f19168301916001600160401b03831184841017620000cb57808492602094604052833981010312620000c757516001600160a01b038082168203620000c75760407f53351836099c03ffc3b1727d8abd4b0222afa87d4ed76ae3102d51369ef7f785915f54903360018060a01b03198316175f55825191168152336020820152a161012c60075562ed4e0060085560805260405161460d9081620000e082396080518181816113b601526116830152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe608060409080825260049081361015610016575f80fd5b60e05f35811c9182626cc35e14611d9457508162d84fd814611d5e5781630b87d88614611d055781630c5644af14611cc9578163101ce0e714611bff57816312d43a5114611bd857816315b0114114611bba578163180a98ed14611b925781631b92468d146119f65781631c4695f41461165a5781631d66bd881461163c57816320f510ca1461149d5781632769c28f146114765781632cc759671461143f578163346e89f114611421578163571fe991146113e5578163612e1488146113a257816363c69f081461120b57816368a3a1771461102157816369cb191414610e05578163791b98bc14610ddd5781637c4283bc14610dc157816380de66b714610da35781638ae7fea214610cdb5781638efd471614610b17578163975057e714610aef5781639a27994a14610a545781639b96eae514610a385781639d8e217714610a16578163a060a4dc146109be578163a8e9a53914610996578163bde4acda14610916578163c2582669146108f0578163cfad57a214610830578163d023fd5c146107a0578163d46a8d3d14610782578163d60c345c146106f5578163d8a26e3a146105c6578163e4223c5b1461052b578163e4fe5719146103c457508063e80225d01461039e578063e8ff863514610312578063eb4bb30d146102c9578063ed33a582146102a55763ef0bcc141461020f575f80fd5b346102a15760203660031901126102a157610228612010565b5f549091906001600160a01b0316330361028a577fbe784d108e8e78fa9f258361956e20859a59b9b3d092da0baa7fe208c181fbe860208460ff8560ff60401b600954911515841b169060ff60401b191617806009558251921c1615158152a1005b60249083519063988d1f0360e01b82523390820152fd5b5f80fd5b82346102a1575f3660031901126102a1576009548151911c60ff1615158152602090f35b82346102a1576102003660031901126102a15760209063ffffffff61030a6102f036612031565b61030560018060a01b03600e54163314612200565b6131fb565b915191168152f35b5090346102a15760203660031901126102a1578135916001600160401b0383168093036102a1575f546001600160a01b03163303610388577fe36ba27db2956162f9f3add30c33e17f0feb383d009583c4cd7867da7dde42ba60208484816001600160401b0319600954161760095551908152a1005b602491519063988d1f0360e01b82523390820152fd5b82346102a1575f3660031901126102a15760209060ff60095460481c1690519015158152f35b8390346102a1576103d436611de0565b916103de836123ab565b925f5b8181106103f9578551806103f58782611f1a565b0390f35b63ffffffff8061040a8385886122e2565b61041390612306565b165f52600190602090828252885f20918980519461043086611f63565b845490600160a01b6001900390818316885260a092831c858901528601549081168388015260109460606001600160501b0360b01b83881b16818a015260ff948c60f09680608096891c161515868d015260028b0154998c8b6001600160601b03948582168b8401528860c09e8f88828c1c16818701521c16818401521c90610100015283519b6104c08d611f93565b6003015499828d8c94851690528360081c161515908d01521c1690890152818660701c16908801528460901c16908601528260b01c6001600160401b0316908501521c9082015261012082015261051782876123fa565b5261052281866123fa565b506001016103e1565b5050346102a157816003193601126102a157610545611dca565b90602435918215158093036102a1575f546001600160a01b039290831633036105af5750916020917ff372b7fc2b1716fcc45bef3ebaad418b80fbda3771a0dbbef64cfa7050093e74931693845f52600b8352805f2060ff1981541660ff841617905551908152a2005b60249085519063988d1f0360e01b82523390820152fd5b8390346102a15760203660031901126102a1576106f3610200926105e8611db7565b926105f161232e565b5063ffffffff8094165f526001602052815f209082519461061186611f63565b60ff835460018060a01b0390818116895260a01c60208901526001850154908116868901526001600160501b0360b01b8160101b16606089015260f01c161515608087015260028301546001600160601b039283821660a0890152838260601c1660c0890152828260c01c16818901521c610100870152600384519361069685611f93565b01549160ff8316845260ff8360081c16151560208501528260101c1684840152808260701c1660608401528160901c1660808301526001600160401b038160b01c1660a083015260f01c60c0820152610120840152518092611e2c565bf35b82346102a15760603660031901126102a15761070f611db7565b602435906001600160401b03928383116102a157366023840112156102a1578201359283116102a15736602484840101116102a157604435916001600160a01b039081841684036102a1576107809461077061077a93601154163314612200565b60243692016121ca565b9061260e565b005b83346102a1575f3660031901126102a157602090600a549051908152f35b5050346102a15760203660031901126102a1576107bb611dca565b5f546001600160a01b039290831633036108195750165f52600c602052805f20600160ff198254161790557fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47648151915f8352600160208401523392a2005b60249084519063988d1f0360e01b82523390820152fd5b8284346102a15760203660031901126102a15761084b611dca565b5f546001600160a01b0380821692338490036108da57169081156108a7576001600160a01b03191681175f55825191825260208201527f53351836099c03ffc3b1727d8abd4b0222afa87d4ed76ae3102d51369ef7f7859250a1005b835162461bcd60e51b8152602081870152600d60248201526c217a65726f206164647265737360981b6044820152606490fd5b845163988d1f0360e01b81523381880152602490fd5b83346102a1575f3660031901126102a15760209063ffffffff5f5460a01c169051908152f35b5050346102a15760203660031901126102a157610931612010565b5f549091906001600160a01b0316330361028a577f6cc75cfd4b33bf26bdf4d64e2fe43562e8749917bf393cc51247676d98dd520360208460ff8560ff60481b60095491151560481b169060ff60481b1916179182600955519160481c1615158152a1005b83346102a1575f3660031901126102a15760115490516001600160a01b039091168152602090f35b8284346102a15760203660031901126102a1575f54823592906001600160a01b03163303610388577f31af123d7934bb99718a85cffb614a7bc155de49132ad4d79a2916ee85f240166020848481600a5551908152a1005b83346102a1575f3660031901126102a15760209051670de0b6b3a76400008152f35b83346102a1575f3660031901126102a157602090516107d08152f35b50506102803660031901126102a157610a6c36612031565b90610a75611fe2565b610a7d611ff9565b90610a8661201f565b92610a9860ff600954881c161561223c565b335f52600c60205260ff865f20541615610abc5750610780945061026435936129a6565b606490602087519162461bcd60e51b8352820152600d60248201526c085b9bdd08185c1c1c9bdd9959609a1b6044820152fd5b83346102a1575f3660031901126102a157600d5490516001600160a01b039091168152602090f35b905082346102a15760209160206003193601126102a157833590600380549463ffffffff9283610b4688612920565b16808611610cd3575b50610b59856123ab565b965f5b868110610b70578751806103f58b82611f1a565b81811015610cc057845f5285817fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154610ba990612920565b165f526001808552885f2084868b5193610bc285611f63565b8c8b8b865492600160a01b60019003948585168a5260a094851c878b0152880154948516818a01526010926060926001600160501b0360b01b87861b16848c015260ff9485918c60f09960809889918c1c1615159101528c60028d0154809c6001600160601b03968783168c85015260c09e8f89828d1c16818701521c16818401521c90610100015284519b610c578d611f93565b015498828a9384168d528360081c161515908c01521c16908801528d8560701c16908701528c8460901c16908601528260b01c6001600160401b0316908501521c90820152610120820152610cac828b6123fa565b52610cb7818a6123fa565b50600101610b5c565b60328a634e487b7160e01b5f525260245ffd5b945088610b4f565b5050346102a15760203660031901126102a1575f54813591906001600160a01b0316330361028a578115610d7657600754821115610d43577ffc0ec2b149e86649398f2116f5e7196cfc456494a2ff5d37c7cedd9c46ca128c602083858160085551908152a1005b606490602084519162461bcd60e51b8352820152600d60248201526c0859dd17db585c9ad95d1d1d1b609a1b6044820152fd5b606490602084519162461bcd60e51b835282015260076024820152662167747a65726f60c81b6044820152fd5b83346102a1575f3660031901126102a1576020906005549051908152f35b83346102a1575f3660031901126102a157602090516127108152f35b83346102a1575f3660031901126102a157600e5490516001600160a01b039091168152602090f35b50506102a03660031901126102a157610e1d36612031565b90610e26611fe2565b610e2e611ff9565b90610e3761201f565b92610284356001600160401b0381116102a157366023820112156102a157610e6890369060248185013591016121ca565b90610e7a60ff600954891c161561223c565b335f52602091600c835260ff885f205416908115610f35575b5015610f06575061078095335f52600c8252805f209081549160ff831615610ec4575b5050505061026435936129a6565b60017fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47649360ff19161790555f815193600185528401523392a25f808080610eb6565b60649187519162461bcd60e51b8352820152600b60248201526a085b9bdd081cda59db995960aa1b6044820152fd5b9050600a54610f4482826134a2565b600581949294101561100e57908a91159384610ffb575b508315610f6c575b5050505f610e93565b5f92935090610fa2610fb0849383519283918a830195630b135d3f60e11b998a88526024850152604484015260648301906125d0565b03601f198101835282611fc1565b5190335afa90610fbe6134d7565b82610fee575b82610fd4575b50505f8881610f63565b90915083818051810103126102a157830151145f80610fca565b9150838251101591610fc4565b6001600160a01b0316331493505f610f5b565b602185634e487b7160e01b5f525260245ffd5b8284346102a157806003193601126102a157813592602493602435938195600590600554926110598861105386612920565b9661240e565b63ffffffff8096168091116111f7575b50611073896123ab565b975f5b8a811061108a578851806103f58c82611f1a565b611094828261240e565b868110156111e5578790865f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001546110cd90612920565b165f5260016020818152888b5f20918b8d8051956110ea87611f63565b855491600160a01b6001900391828416895260a093841c868a0152870154918216818901526010956060916001600160501b0360b01b84891b16838b015260ff9560f09680608096891c161515868d015260028b0154998c8b6001600160601b03948582168b8401528760c09e8f88828d1c16818701521c16818401521c90610100015283519b61117a8d611f93565b6003015499828d8c94851690528360081c161515908d01521c16908901528560701c16908701528d8460901c16908601528260b01c6001600160401b0316908501521c908201526101208201526111d1828c6123fa565b526111dc818b6123fa565b50600101611076565b84603285634e487b7160e01b5f52525ffd5b611204919950889061241b565b9789611069565b83346102a157602090816003193601126102a1576001600160a01b0380611230611dca565b1692835f526002906002815263ffffffff908161124f865f2054612920565b169361125a856123ab565b965f5b868110611271578751806103f58b82611f1a565b815f528584528481895f20906112869161348d565b919054600392831b1c61129890612920565b165f5260018086528b86898c5f20938d8051966112b488611f63565b8654928b8416895260a093841c868a0152870154918b8316818a01528d6010926060936001600160501b0360b01b86821b16858d015260ff918c60f099846080998c1c161515898301528d0154809c6001600160601b03968783168c85015260c09e8f89828d1c16818701521c16818401521c90610100015284519b6113398d611f93565b015498828a9384168d528360081c161515908c01521c16908801528c8560701c16908701528b8460901c16908601528260b01c6001600160401b0316908501521c9082015261012082015261138e828b6123fa565b52611399818a6123fa565b5060010161125d565b83346102a1575f3660031901126102a157517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b83346102a15760203660031901126102a1576020906001600160a01b0361140a611dca565b165f52600c825260ff815f20541690519015158152f35b83346102a1575f3660031901126102a1576020906008549051908152f35b83346102a15760203660031901126102a1576020906001600160a01b03611464611dca565b165f5260028252805f20549051908152f35b83346102a1575f3660031901126102a1576020906001600160401b03600954169051908152f35b8390346102a1576114ad36611de0565b60ff92916114c260ff600954871c161561223c565b5f5b63ffffffff908181168481101561078057826114e18287876122e2565b6114ea90612306565b165f526001888760208381528a835f20918451809961150882611f63565b845490600160a01b6001900398898316845260a092831c8585015286015490898216898501526010986060906001600160501b0360b01b848c1b168287015260f09588608095881c1615158582015260028a0154986001600160601b039c828b8f948a019e8f86831690528860c09e8f88828c1c16818701521c16818401521c90610100015283519b61159a8d611f93565b6003015499828d8c94851690528360081c161515908d01521c1690890152818660701c16908801528460901c16908601528260b01c6001600160401b0316908501521c908201526101208701525116151590611603948261162e575b5050611608575b506122b9565b6114c4565b61161e6116196116289287876122e2565b612306565b339061077a612272565b876115fd565b9091505116331489806115f6565b83346102a1575f3660031901126102a1576020906003549051908152f35b8284346102a1575f3660031901126102a1575f546001600160a01b0392908316330361038857827f00000000000000000000000000000000000000000000000000000000000000001682519163bf40fac160e01b938484526020938483820152600560248201526453746f726560d81b60448201528481606481875afa9081156119ec579087915f916119b4575b5016946001600160601b0360a01b948686600d541617600d5582518281528185820152601060248201526f2a3930b234b733ab30b634b230ba37b960811b60448201528181606481895afa9081156118e4579089915f9161197c575b5016918287600f541617600f5583518181528286820152600f60248201526e526566657272616c53746f7261676560881b604482015282816064818a5afa90811561197257908a915f9161193a575b5016948588601054161760105584518281528382820152600860248201526722bc32b1baba37b960c11b604482015283816064818b5afa908115611930578492918c915f916118ee575b5093606491849516998a8c6011541617601155885195869485938452830152600f60248301526e2837b9b4ba34b7b726b0b730b3b2b960891b60448301525afa9081156118e4575f9161187b575b50907ff4aa8c967b4ad917e7d98f8e11fe6ab3e28b2eec7f6423ba8229c8f21375116f9860a098979695949392168096600e541617600e55825196875286015284015260608301526080820152a1005b9796959493929180915088813d83116118dd575b6118998183611fc1565b810103126102a1577ff4aa8c967b4ad917e7d98f8e11fe6ab3e28b2eec7f6423ba8229c8f21375116f986118ce60a0996122a5565b9192939495969798509861182b565b503d61188f565b84513d5f823e3d90fd5b94938092508591503d8311611929575b6119088183611fc1565b810103126102a15760648493928c61192086956122a5565b919550916117dd565b503d6118fe565b86513d5f823e3d90fd5b809250848092503d831161196b575b6119538183611fc1565b810103126102a1576119658a916122a5565b8b611793565b503d611949565b85513d5f823e3d90fd5b809250838092503d83116119ad575b6119958183611fc1565b810103126102a1576119a789916122a5565b8a611744565b503d61198b565b809250868092503d83116119e5575b6119cd8183611fc1565b810103126102a1576119df87916122a5565b886116e8565b503d6119c3565b82513d5f823e3d90fd5b8284346102a157602090816003193601126102a157611a13611db7565b93611a2560ff600954841c161561223c565b63ffffffff908186165f5260018452825f2091835191611a4483611f63565b83549360018060a01b0394858116855260a01c8785015260ff6001820154868116888701526001600160501b0360b01b8160101b16606087015260f01c16151560808501526002810154926001600160601b039360a08601938582168552858260601c1660c0880152828260c01c16818801521c6101008601526003875192611acc84611f93565b01549060ff8216835260ff8260081c16151589840152848260101c1688840152808260701c1660608401528160901c1660808301526001600160401b038160b01c1660a083015260f01c60c0820152610120840152511615611b665751163303611b3d57610780338561077a612272565b5162461bcd60e51b815291820152600560248201526410bab9b2b960d91b604482015260649150fd5b50505162461bcd60e51b815291820152600660248201526510b7b93232b960d11b604482015260649150fd5b83346102a1575f3660031901126102a157600f5490516001600160a01b039091168152602090f35b83346102a1575f3660031901126102a1576020906007549051908152f35b83346102a1575f3660031901126102a1575f5490516001600160a01b039091168152602090f35b5050346102a15760203660031901126102a1575f54813591906001600160a01b0316330361028a578115611c9b57600854821015611c67577fb389411734ec38434cf45d5ddb15e04046d44ade0e97b4dd832a6e781f81015a602083858160075551908152a1005b606490602084519162461bcd60e51b8352820152600e60248201526d085b1d17dd1c9a59d9d95c9d1d1b60921b6044820152fd5b606490602084519162461bcd60e51b835282015260086024820152672167745f7a65726f60c01b6044820152fd5b83346102a15760203660031901126102a1576020906001600160a01b03611cee611dca565b165f52600b825260ff815f20541690519015158152f35b83346102a1575f3660031901126102a157335f52600c602052805f20600160ff198254161790557fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47648151915f83525f60208401523392a2005b346102a15760203660031901126102a157610780611d7a611db7565b611d8f60018060a01b03600e54163314612200565b612428565b346102a1575f3660031901126102a1576010546001600160a01b03168152602090f35b6004359063ffffffff821682036102a157565b600435906001600160a01b03821682036102a157565b9060206003198301126102a1576004356001600160401b03928382116102a157806023830112156102a15781600401359384116102a15760248460051b830101116102a1576024019190565b61ffff60c06101e09260018060a01b038082511686526020820151906001600160601b03809216602088015260408301511660408701526001600160501b0360b01b60608301511660608701526080820151151560808701528060a08301511660a08701528083830151168387015260e08201519063ffffffff80921660e088015261010082818501511690880152610120809301519260ff8451169088015260208301511515610140880152604083015116610160870152806060830151166101808701526080820151166101a08601526001600160401b0360a0820151166101c0860152015116910152565b60209060206040818301928281528551809452019301915f5b828110611f41575050505090565b909192938261020082611f576001948951611e2c565b01950193929101611f33565b61014081019081106001600160401b03821117611f7f57604052565b634e487b7160e01b5f52604160045260245ffd5b60e081019081106001600160401b03821117611f7f57604052565b6001600160401b038111611f7f57604052565b90601f801991011681019081106001600160401b03821117611f7f57604052565b61020435906001600160601b03821682036102a157565b61022435906001600160601b03821682036102a157565b6004359081151582036102a157565b610244359061ffff821682036102a157565b906102006003198301126102a1576040519161204c83611f63565b6001600160a01b0390839060043583811681036102a15782526001600160601b039260243584811681036102a157602084015260443590811681036102a15760408301526064356001600160b01b0319811681036102a157606083015260843580151581036102a157608083015260a43583811681036102a15760a083015260c43583811681036102a15760c083015263ffffffff9060e43582811681036102a15760e0840152610104359082821682036102a15760e0916101008501526101231901126102a1576040519261212184611f93565b6101243560ff811681036102a15784526101443580151581036102a15760208501526101643590811681036102a15760408401526101843581811681036102a15760608401526101a43590811681036102a15760808301526101c4356001600160401b03811681036102a15760a08301526101e4359061ffff821682036102a1576101209160c08401520152565b6001600160401b038111611f7f57601f01601f191660200190565b9291926121d6826121af565b916121e46040519384611fc1565b8294818452818301116102a1578281602093845f960137010152565b1561220757565b60405162461bcd60e51b815260206004820152600d60248201526c085d5b985d5d1a1bdc9a5e9959609a1b6044820152606490fd5b1561224357565b60405162461bcd60e51b8152602060048201526007602482015266085c185d5cd95960ca1b6044820152606490fd5b60405190604082018281106001600160401b03821117611f7f576040526007825266313c96bab9b2b960c91b6020830152565b51906001600160a01b03821682036102a157565b63ffffffff8091169081146122ce5760010190565b634e487b7160e01b5f52601160045260245ffd5b91908110156122f25760051b0190565b634e487b7160e01b5f52603260045260245ffd5b3563ffffffff811681036102a15790565b6001600160401b038111611f7f5760051b60200190565b6040519061233b82611f63565b815f81525f60208201525f60408201525f60608201525f60808201525f60a08201525f60c08201525f60e08201525f6101008201526101206040519161238083611f93565b5f83525f60208401525f60408401525f60608401525f60808401525f60a08401525f60c08401520152565b906123b582612317565b6123c26040519182611fc1565b82815280926123d3601f1991612317565b01905f5b8281106123e357505050565b6020906123ee61232e565b828285010152016123d7565b80518210156122f25760209160051b010190565b919082018092116122ce57565b919082039182116122ce57565b63ffffffff80911690815f5260209060018252604090815f209282519461244e86611f63565b84549260018060a01b0393848116885260a01c8388015260ff6001870154858116878a01526001600160501b0360b01b8160101b1660608a015260f01c161515608088015260028601546001600160601b039160a08901918381168352838160601c1660c08b0152818160c01c1660e08b015260e01c6101008a015260038751986124d88a611f93565b01549060ff8216895260ff8260081c161515868a0152838260101c16888a0152808260701c1660608a01528160901c1660808901526001600160401b038160b01c1660a089015260f01c60c088015261012088019687525116156125ad57807fa79f5bd225998a95d4de82c67e16e86bc61191a4faa2dbcb0e1a89886af5a0058360ff6003986001975f9b51168b5260028352612577858a8d206143bd565b5061258185614202565b5061258b856142fe565b505151168751908152a285525282208281558260018201558260028201550155565b505050505050565b9190916001600160601b03808094169116019182116122ce57565b91908251928382525f5b8481106125fa575050825f602080949584010152601f8019910116010190565b6020818301810151848301820152016125da565b905f9263ffffffff9182841693845f526001602052604095865f20918780519361263785611f63565b80549760018060a01b03988981168752602087019060a01c815260018301549460ff858901968c811688526001600160501b0360b01b8160101b1660608b015260f01c16151560808901526002840154906001600160601b03809460a08b0190828516825260c08c0194838160601c1686528c60e0838360c01c1691015260e01c6101008d015260038951986126cc8a611f93565b01549860ff8a16895260ff8a60081c16151560208a0152838a60101c1690890152808960701c1660608901528860901c1660808801526001600160401b0397888160b01c1660a089015260f01c60c08801526101208b01968752511615612910578b936127398a92612428565b5f9760208751015115612834575b50505050505060a0905101511690811515908161282b575b506127ae575b50508392917f7ffce3199c8273ece91032d8f943315778e942eff22f8f7d8a7da972073a58b1946127a99251169680519485941684528060208501528301906125d0565b0390a3565b85600d541690813b1561282757885163078d3b7960e01b81525f60048201526001600160a01b03871660248201526001600160401b039190911660448201529082908290606490829084905af1801561281d5715612765576128108291611fae565b61281a5780612765565b80fd5b88513d84823e3d90fd5b8280fd5b9050155f61275f565b8598509461287e9293949551169384159081612901575b828293612867939b80600d5416975116985116915116906125b5565b90156128fa57868060a088510151165b16906125b5565b90803b156102a1578d5163078d3b7960e01b81526001600160a01b0393841660048201529390921660248401526001600160601b031660448301525f908290606490829084905af180156128f0576128dc575b808087818c94612747565b6128e7919450611fae565b5f9260a06128d1565b8b513d5f823e3d90fd5b865f612877565b84518a168e8b1614915061284b565b5050505050505050505050505050565b63ffffffff90818111612931571690565b60405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608490fd5b519081151582036102a157565b51906001600160601b03821682036102a157565b9291905f946001600160601b0390818316159283801580916131f0575b80156131e3575b6131d3575b8861012097888a0198895160208101511590818015906131c5575b612f7f575b506001600160a01b039a91508a9050612a078c613506565b16808c523303612f73575b6001600160401b0391826009541660a0835101528260a08351015116988c612a3981613603565b99809150151580612f66575b612ef0575b50508590612ee5575b8015612ed8575b612baf575b505050505050612a7e935084604087015116155f14612ba7571661240e565b803410612b7557612a8f903461241b565b9182612a9c575b50505050565b511690804710612b30578280929181925af1612ab66134d7565b5015612ac5575f808080612a96565b60405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608490fd5b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606490fd5b60405162461bcd60e51b815260206004820152600a602482015269216d73672d76616c756560b01b6044820152606490fd5b50508461240e565b87604083949596979a93510151168681612db0575b8d915080612da5575b612d1f575b88612a7e9b8760808194018051159052606087510163ffffffff815116612d17575b505016918215612cc557505090612c2691604084510152600283515260016020845101528360a084510151169061240e565b96612c308b613603565b50945b612c81575b50505063ffffffff8116151580612c72575b612c58575b88818080612a5f565b81612c66612c6b93836141c9565b6141c9565b5f80612c4f565b5063ffffffff82161515612c4a565b612cb294979350908660a09216604082510152600181515260016020825101528b60c082510152510151169061240e565b92612cbc87613603565b505f8080612c38565b61ffff919792509992991680612cdc575b50612c33565b612d0592999196506003845152600160208551015260c0845101528360a084510151169061240e565b96612d0f8b613603565b50945f612cd6565b528f5f612bf4565b6080919a955001511580159081612d97575b8115612d7c575b5015612d47578b93988b612bd2565b60405162461bcd60e51b815260206004820152600d60248201526c085d1c1cdb0b5a5b9d985b1a59609a1b6044820152606490fd5b905080612d8a575b5f612d38565b5086841687891610612d84565b8589168a8a16119150612d31565b50888b161515612bcd565b612e53575b50878a1680612dc5575b86612bc4565b90945060808c01511580159182612e42575b8215612e20575b505015612ded578b935f612dbf565b60405162461bcd60e51b815260206004820152600b60248201526a085cdb0b5a5b9d985b1a5960aa1b6044820152606490fd5b90915081612e31575b505f80612dde565b90508760408451015116105f612e29565b8451604001518a1681109250612dd7565b90945060808c01511580159182612ecb575b8215612eae575b505015612e7b578b935f612db5565b60405162461bcd60e51b815260206004820152600b60248201526a085d1c0b5a5b9d985b1a5960aa1b6044820152606490fd5b90915081612ebf575b505f80612e6c565b9050878916105f612eb7565b809250898b161191612e65565b5061ffff81161515612a5a565b50878a161515612a53565b909192939495508c806010541692511690823b156102a15760445f928360405195869485936356b4b2ad60e01b8552600485015260248401525af18015612f5b57612f42575b908c8e95949392612a4a565b612f50919493929d50611fae565b5f9b9091925f612f36565b6040513d5f823e3d90fd5b508d601054161515612a45565b5f608082510152612a12565b51929350909160ff1660031461316f575b50600e546040808c015160608d0151825163052ec72760e51b81523360048201526001600160a01b0392831660248201526001600160b01b0319918216604482015292949093909291839183916064918391165afa9283156118e4575f93613093575b505050866080820151161561306057810151151560808b015115151461301d579081808c936129ef565b5162461bcd60e51b815260206004820152601760248201527f217265647563652d77726f6e672d646972656374696f6e0000000000000000006044820152606490fd5b815162461bcd60e51b815260206004820152600c60248201526b10b73796b837b9b4ba34b7b760a11b6044820152606490fd5b909180935082813d8311613168575b6130ac8183611fc1565b810103126102a15783519283018381106001600160401b03821117611f7f5784526130d6826122a5565b8352602082015190811681036102a15760208301526130f6838201612985565b83830152613106606082016122a5565b606083015261311760808201612992565b608083015260a081015163ffffffff811681036102a15760a083015261313f60c08201612992565b60c083015261315060e08201612992565b60e083015261010080910151908201525f8080612ff3565b503d6130a2565b613180575f60408a5101525f612f90565b60405162461bcd60e51b815260206004820152601a60248201527f21747261696c696e672d73746f702d6d7573742d7265647563650000000000006044820152606490fd5b50600360ff825116146129ea565b5f60206101208a015101526129cf565b5061ffff871615156129ca565b5083861615156129c3565b5f547f6802d0b735cb1927bc8d580cfb1f7e0307b427272c81609ccce7a80ab5afb070602060ff63ffffffff613235818660a01c166122b9565b958187169563ffffffff60a01b8860a01b169063ffffffff60a01b1916175f55610100810190868252865f5260018552610120600360405f2060018060a01b039485855116956001600160601b0360a01b8a87015160a01b1687178355600183019060408701511681546060880151918b60f01b60808a0151151560f01b16926001600160501b0360a01b9060101c16918c60f81b1617171790556001600160601b0360a086015116906001600160601b0360601b60c087015160601b169163ffffffff60c01b60e088015160c01b169163ffffffff60e01b905160e01b16921717176002820155019101926133a98451918683511684549061ff008a860151151560081b166dffffffffffffffffffffffff0000604087015160101b169163ffffffff60701b606088015160701b169371ffffffffffffffffffffffffffffffffffff1916171717178455608083015116839081549063ffffffff60901b9060901b169063ffffffff60901b1916179055565b60a08101519082549060c061ffff60f01b91015160f01b16916001600160401b0360b01b9060b01b169060018060b01b0316171790555f52600283526133f28560405f2061458a565b508051518216613414576134058561453c565b505b515116604051908152a290565b61341d856144e9565b50613407565b6003548110156122f25760035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01905f90565b6005548110156122f25760055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001905f90565b80548210156122f2575f5260205f2001905f90565b9060418151145f146134ce576134ca91602082015190606060408401519301515f1a9061446e565b9091565b50505f90600290565b3d15613501573d906134e8826121af565b916134f66040519384611fc1565b82523d5f602084013e565b606090565b335f52600b60205260ff60405f20541661351f57503390565b80516001600160a01b039190821615158061359b575b8061358a575b1561354557511690565b60405162461bcd60e51b815260206004820152601a60248201527f66756e64696e672d6163636f756e742d6f726465722d6661696c0000000000006044820152606490fd5b5060ff61012082015151161561353b565b506020610120820151015115613535565b519060ff821682036102a157565b519061ffff821682036102a157565b156135d057565b60405162461bcd60e51b815260206004820152600b60248201526a216d61782d65787069727960a81b6044820152606490fd5b905f91600460ff610120830151511610156141965761012081015160ff81511660018114801561418c575b156141155750506001600160601b036040610120830151015116156140e7575b600d54604082810151905163185c596360e11b81526001600160a01b03918216600482015291169390606081602481885afa8015612f5b575f9061405f575b60200180516affffffffffffffffffffff925082161561402a5760206101208501510151159182159261400d575b505015613fdc576060820151604051637061696b60e01b81526001600160b01b031990911660048201526101c081602481885afa908115612f5b575f91613e9f575b5061ffff60808201511615613e695763ffffffff606061012085015101511680613de0575b5063ffffffff6080610120850151015116613d75575b63ffffffff61374642612920565b1660e08401526001600160601b0360a08401511661ffff60c083015116026001600160601b0381169081036122ce576001600160601b03612710820411613d215761271090046001600160601b031660c0840152610120830151602001515f9586929091156139a55750508160208401525b6001600160401b0360a061012085015101511690811515908161399c575b5061392a575b50506137e7816131fb565b63ffffffff8116610100830152600160a01b60019003825116600160a01b60019003604084015116916001600160501b0360b01b6060850151169060208501516001600160601b031660a08601516001600160601b03166101208701519060408201516001600160601b031660c08901516001600160601b031660808a01511515845160ff16906020860151151592606087015163ffffffff1694608088015163ffffffff169660a08901516001600160401b03169860c0015161ffff16996040519c8d5260208d015260408c015260608b015260808a015260a089015260c088015260e08701526101008601526101208501523361014085015261016084015261018083015263ffffffff16906101a07feb2d670c93b7063878c5ca9fe0ec6eb06b845e9e4250f6cf7e0087680422efb291a4610100015163ffffffff169190565b600d546001600160a01b031690813b156128275760405163e4652f4960e01b81525f60048201523360248201526001600160401b0382166044820152918391839160649183915af1801561399157156137dc576139878291611fae565b61281a57806137dc565b6040513d84823e3d90fd5b9050155f6137d6565b909550610160810151613ce6576001600160601b0360208501511615613cb7576001600160601b0360a08501511690670de0b6b3a76400009180830290838204036122ce576001600160601b03602087015116908115613ca3570490828210613c6e576080015161ffff168281029281159184041417156122ce5711613c3957600f546040840151606085015160a08601516001600160601b0316926001600160a01b03928316926001600160b01b03199092169116803b156102a1575f9260649160405195869485936318512afb60e21b85526004850152602484015260448301525afa8015612f5b57613c26575b50613abb6001600160601b036020850151166001600160601b0360c086015116906125b5565b60408401519095906001600160a01b031680613ba25750505060018060a01b03600d54166001600160401b03906001600160601b03613b058360a0610120880151015116886125b5565b1691613b2960018060a01b036040870151169160a0610120880151015116886125b5565b92823b15613b9e5760405163e4652f4960e01b81526001600160a01b039290921660048301523360248301526001600160601b0393909316604482015291839183916064918391905af1801561399157908291613b8a575b505060016137b8565b613b9390611fae565b61281a57805f613b81565b8480fd5b813b15613c225760405163e4652f4960e01b81526001600160a01b039190911660048201523360248201526001600160601b03871660448201529083908290606490829084905af18015613c1757908391613bff575b50506137b8565b613c0890611fae565b613c1357815f613bf8565b5080fd5b6040513d85823e3d90fd5b8380fd5b613c31919250611fae565b5f905f613a95565b60405162461bcd60e51b815260206004820152600d60248201526c216d61782d6c6576657261676560981b6044820152606490fd5b60405162461bcd60e51b815260206004820152600d60248201526c216d696e2d6c6576657261676560981b6044820152606490fd5b634e487b7160e01b5f52601260045260245ffd5b60405162461bcd60e51b815260206004820152600760248201526610b6b0b933b4b760c91b6044820152606490fd5b60405162461bcd60e51b8152602060048201526013602482015272216d61726b65742d7265647563652d6f6e6c7960681b6044820152606490fd5b60405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201526536206269747360d01b6064820152608490fd5b82516001600160a01b03165f9081526002602090815260408083206101208701516080015163ffffffff1684526001019091529020546137385760405162461bcd60e51b815260206004820152600960248201526821757365722d6f636f60b81b6044820152606490fd5b4211613e3457613dff4263ffffffff606061012087015101511661241b565b6101208401515160ff16613e2157613e1b9060075410156135c9565b5f613722565b613e2f9060085410156135c9565b613e1b565b60405162461bcd60e51b815260206004820152600d60248201526c216578706972792d76616c756560981b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d216d61726b65742d65786973747360901b6044820152606490fd5b90506101c0813d6101c011613fd4575b81613ebd6101c09383611fc1565b810103126102a157604051906101c082018281106001600160401b03821117611f7f576040528051825260208101516001600160a01b0319811681036102a1576020830152613f0e604082016122a5565b604083015260608101516060830152613f29608082016135ba565b6080830152613f3a60a082016135ba565b60a0830152613f4b60c082016135ba565b60c0830152613f5c60e082016135ba565b60e0830152610100613f6f8183016135ba565b90830152613f8061012082016135ac565b610120830152610140613f948183016135ac565b90830152610160613fa6818301612985565b90830152610180613fb88183016135ba565b90830152613fca6101a08092016135ba565b908201525f6136fd565b3d9150613eaf565b60405162461bcd60e51b8152602060048201526009602482015268216d696e2d73697a6560b81b6044820152606490fd5b9091506001600160601b0360a08501511691511611155f806136bb565b60405162461bcd60e51b815260206004820152600d60248201526c2161737365742d65786973747360981b6044820152606490fd5b506060813d6060116140df575b8161407960609383611fc1565b810103126102a157604051606081018181106001600160401b03821117611f7f576040526140a6826135ac565b81526020820151916affffffffffffffffffffff831683036102a15760406140d59160209485850152016122a5565b604082015261368d565b3d915061406c565b60405162461bcd60e51b815260206004820152600660248201526521707269636560d01b6044820152606490fd5b600314614123575b5061364e565b60c0015161ffff16801515908161417f575b5015614141575f61411d565b60405162461bcd60e51b8152602060048201526016602482015275085d1c985a5b1a5b99cb5cdd1bdc0b5a5b9d985b1a5960521b6044820152606490fd5b6107d0915011155f614135565b506002811461362e565b60405162461bcd60e51b815260206004820152600b60248201526a216f726465722d7479706560a81b6044820152606490fd5b63ffffffff165f908152600160205260409020600301805463ffffffff60901b191660909290921b63ffffffff60901b16919091179055565b805f526004908160205260405f20548015155f146142f7575f19908082018181116142e457600354908382019182116142d157808203614288575b50505060035480156142755781019061425582613423565b909182549160031b1b191690556003555f526020525f6040812055600190565b603184634e487b7160e01b5f525260245ffd5b6142bc6142976142a693613423565b90549060031b1c928392613423565b819391549060031b91821b915f19901b19161790565b90555f528360205260405f20555f808061423d565b601186634e487b7160e01b5f525260245ffd5b601185634e487b7160e01b5f525260245ffd5b5050505f90565b5f8181526006602052604090205480156143b7575f19908082018181116122ce57600554908382019182116122ce57808203614383575b505050600554801561436f5781019061434d82613458565b909182549160031b1b191690556005555f5260066020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b6143a16143926142a693613458565b90549060031b1c928392613458565b90555f52600660205260405f20555f8080614335565b50505f90565b906001820191815f528260205260405f2054908115155f14614466575f19918083018181116122ce578254908482019182116122ce57808203614431575b5050508054801561436f57820191614413838361348d565b909182549160031b1b19169055555f526020525f6040812055600190565b6144516144416142a6938661348d565b90549060031b1c9283928661348d565b90555f528460205260405f20555f80806143fb565b505050505f90565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116144de576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15612f5b575f516001600160a01b038116156144d657905f90565b505f90600190565b505050505f90600390565b805f52600660205260405f2054155f1461453757600554600160401b811015611f7f576145206142a6826001859401600555613458565b9055600554905f52600660205260405f2055600190565b505f90565b805f52600460205260405f2054155f1461453757600354600160401b811015611f7f576145736142a6826001859401600355613423565b9055600354905f52600460205260405f2055600190565b5f8281526001820160205260409020546143b757805490600160401b821015611f7f57826145c26142a684600180960185558461348d565b90558054925f520160205260405f205560019056fea264697066735822122073544964512f140197b410bd1dbd91dfadf04ff2d797ff6ecaaec45306031c5164736f6c63430008160033000000000000000000000000b7a842f997cdbd0291d58f0d4ce729436369c839
Deployed Bytecode
0x608060409080825260049081361015610016575f80fd5b60e05f35811c9182626cc35e14611d9457508162d84fd814611d5e5781630b87d88614611d055781630c5644af14611cc9578163101ce0e714611bff57816312d43a5114611bd857816315b0114114611bba578163180a98ed14611b925781631b92468d146119f65781631c4695f41461165a5781631d66bd881461163c57816320f510ca1461149d5781632769c28f146114765781632cc759671461143f578163346e89f114611421578163571fe991146113e5578163612e1488146113a257816363c69f081461120b57816368a3a1771461102157816369cb191414610e05578163791b98bc14610ddd5781637c4283bc14610dc157816380de66b714610da35781638ae7fea214610cdb5781638efd471614610b17578163975057e714610aef5781639a27994a14610a545781639b96eae514610a385781639d8e217714610a16578163a060a4dc146109be578163a8e9a53914610996578163bde4acda14610916578163c2582669146108f0578163cfad57a214610830578163d023fd5c146107a0578163d46a8d3d14610782578163d60c345c146106f5578163d8a26e3a146105c6578163e4223c5b1461052b578163e4fe5719146103c457508063e80225d01461039e578063e8ff863514610312578063eb4bb30d146102c9578063ed33a582146102a55763ef0bcc141461020f575f80fd5b346102a15760203660031901126102a157610228612010565b5f549091906001600160a01b0316330361028a577fbe784d108e8e78fa9f258361956e20859a59b9b3d092da0baa7fe208c181fbe860208460ff8560ff60401b600954911515841b169060ff60401b191617806009558251921c1615158152a1005b60249083519063988d1f0360e01b82523390820152fd5b5f80fd5b82346102a1575f3660031901126102a1576009548151911c60ff1615158152602090f35b82346102a1576102003660031901126102a15760209063ffffffff61030a6102f036612031565b61030560018060a01b03600e54163314612200565b6131fb565b915191168152f35b5090346102a15760203660031901126102a1578135916001600160401b0383168093036102a1575f546001600160a01b03163303610388577fe36ba27db2956162f9f3add30c33e17f0feb383d009583c4cd7867da7dde42ba60208484816001600160401b0319600954161760095551908152a1005b602491519063988d1f0360e01b82523390820152fd5b82346102a1575f3660031901126102a15760209060ff60095460481c1690519015158152f35b8390346102a1576103d436611de0565b916103de836123ab565b925f5b8181106103f9578551806103f58782611f1a565b0390f35b63ffffffff8061040a8385886122e2565b61041390612306565b165f52600190602090828252885f20918980519461043086611f63565b845490600160a01b6001900390818316885260a092831c858901528601549081168388015260109460606001600160501b0360b01b83881b16818a015260ff948c60f09680608096891c161515868d015260028b0154998c8b6001600160601b03948582168b8401528860c09e8f88828c1c16818701521c16818401521c90610100015283519b6104c08d611f93565b6003015499828d8c94851690528360081c161515908d01521c1690890152818660701c16908801528460901c16908601528260b01c6001600160401b0316908501521c9082015261012082015261051782876123fa565b5261052281866123fa565b506001016103e1565b5050346102a157816003193601126102a157610545611dca565b90602435918215158093036102a1575f546001600160a01b039290831633036105af5750916020917ff372b7fc2b1716fcc45bef3ebaad418b80fbda3771a0dbbef64cfa7050093e74931693845f52600b8352805f2060ff1981541660ff841617905551908152a2005b60249085519063988d1f0360e01b82523390820152fd5b8390346102a15760203660031901126102a1576106f3610200926105e8611db7565b926105f161232e565b5063ffffffff8094165f526001602052815f209082519461061186611f63565b60ff835460018060a01b0390818116895260a01c60208901526001850154908116868901526001600160501b0360b01b8160101b16606089015260f01c161515608087015260028301546001600160601b039283821660a0890152838260601c1660c0890152828260c01c16818901521c610100870152600384519361069685611f93565b01549160ff8316845260ff8360081c16151560208501528260101c1684840152808260701c1660608401528160901c1660808301526001600160401b038160b01c1660a083015260f01c60c0820152610120840152518092611e2c565bf35b82346102a15760603660031901126102a15761070f611db7565b602435906001600160401b03928383116102a157366023840112156102a1578201359283116102a15736602484840101116102a157604435916001600160a01b039081841684036102a1576107809461077061077a93601154163314612200565b60243692016121ca565b9061260e565b005b83346102a1575f3660031901126102a157602090600a549051908152f35b5050346102a15760203660031901126102a1576107bb611dca565b5f546001600160a01b039290831633036108195750165f52600c602052805f20600160ff198254161790557fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47648151915f8352600160208401523392a2005b60249084519063988d1f0360e01b82523390820152fd5b8284346102a15760203660031901126102a15761084b611dca565b5f546001600160a01b0380821692338490036108da57169081156108a7576001600160a01b03191681175f55825191825260208201527f53351836099c03ffc3b1727d8abd4b0222afa87d4ed76ae3102d51369ef7f7859250a1005b835162461bcd60e51b8152602081870152600d60248201526c217a65726f206164647265737360981b6044820152606490fd5b845163988d1f0360e01b81523381880152602490fd5b83346102a1575f3660031901126102a15760209063ffffffff5f5460a01c169051908152f35b5050346102a15760203660031901126102a157610931612010565b5f549091906001600160a01b0316330361028a577f6cc75cfd4b33bf26bdf4d64e2fe43562e8749917bf393cc51247676d98dd520360208460ff8560ff60481b60095491151560481b169060ff60481b1916179182600955519160481c1615158152a1005b83346102a1575f3660031901126102a15760115490516001600160a01b039091168152602090f35b8284346102a15760203660031901126102a1575f54823592906001600160a01b03163303610388577f31af123d7934bb99718a85cffb614a7bc155de49132ad4d79a2916ee85f240166020848481600a5551908152a1005b83346102a1575f3660031901126102a15760209051670de0b6b3a76400008152f35b83346102a1575f3660031901126102a157602090516107d08152f35b50506102803660031901126102a157610a6c36612031565b90610a75611fe2565b610a7d611ff9565b90610a8661201f565b92610a9860ff600954881c161561223c565b335f52600c60205260ff865f20541615610abc5750610780945061026435936129a6565b606490602087519162461bcd60e51b8352820152600d60248201526c085b9bdd08185c1c1c9bdd9959609a1b6044820152fd5b83346102a1575f3660031901126102a157600d5490516001600160a01b039091168152602090f35b905082346102a15760209160206003193601126102a157833590600380549463ffffffff9283610b4688612920565b16808611610cd3575b50610b59856123ab565b965f5b868110610b70578751806103f58b82611f1a565b81811015610cc057845f5285817fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154610ba990612920565b165f526001808552885f2084868b5193610bc285611f63565b8c8b8b865492600160a01b60019003948585168a5260a094851c878b0152880154948516818a01526010926060926001600160501b0360b01b87861b16848c015260ff9485918c60f09960809889918c1c1615159101528c60028d0154809c6001600160601b03968783168c85015260c09e8f89828d1c16818701521c16818401521c90610100015284519b610c578d611f93565b015498828a9384168d528360081c161515908c01521c16908801528d8560701c16908701528c8460901c16908601528260b01c6001600160401b0316908501521c90820152610120820152610cac828b6123fa565b52610cb7818a6123fa565b50600101610b5c565b60328a634e487b7160e01b5f525260245ffd5b945088610b4f565b5050346102a15760203660031901126102a1575f54813591906001600160a01b0316330361028a578115610d7657600754821115610d43577ffc0ec2b149e86649398f2116f5e7196cfc456494a2ff5d37c7cedd9c46ca128c602083858160085551908152a1005b606490602084519162461bcd60e51b8352820152600d60248201526c0859dd17db585c9ad95d1d1d1b609a1b6044820152fd5b606490602084519162461bcd60e51b835282015260076024820152662167747a65726f60c81b6044820152fd5b83346102a1575f3660031901126102a1576020906005549051908152f35b83346102a1575f3660031901126102a157602090516127108152f35b83346102a1575f3660031901126102a157600e5490516001600160a01b039091168152602090f35b50506102a03660031901126102a157610e1d36612031565b90610e26611fe2565b610e2e611ff9565b90610e3761201f565b92610284356001600160401b0381116102a157366023820112156102a157610e6890369060248185013591016121ca565b90610e7a60ff600954891c161561223c565b335f52602091600c835260ff885f205416908115610f35575b5015610f06575061078095335f52600c8252805f209081549160ff831615610ec4575b5050505061026435936129a6565b60017fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47649360ff19161790555f815193600185528401523392a25f808080610eb6565b60649187519162461bcd60e51b8352820152600b60248201526a085b9bdd081cda59db995960aa1b6044820152fd5b9050600a54610f4482826134a2565b600581949294101561100e57908a91159384610ffb575b508315610f6c575b5050505f610e93565b5f92935090610fa2610fb0849383519283918a830195630b135d3f60e11b998a88526024850152604484015260648301906125d0565b03601f198101835282611fc1565b5190335afa90610fbe6134d7565b82610fee575b82610fd4575b50505f8881610f63565b90915083818051810103126102a157830151145f80610fca565b9150838251101591610fc4565b6001600160a01b0316331493505f610f5b565b602185634e487b7160e01b5f525260245ffd5b8284346102a157806003193601126102a157813592602493602435938195600590600554926110598861105386612920565b9661240e565b63ffffffff8096168091116111f7575b50611073896123ab565b975f5b8a811061108a578851806103f58c82611f1a565b611094828261240e565b868110156111e5578790865f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001546110cd90612920565b165f5260016020818152888b5f20918b8d8051956110ea87611f63565b855491600160a01b6001900391828416895260a093841c868a0152870154918216818901526010956060916001600160501b0360b01b84891b16838b015260ff9560f09680608096891c161515868d015260028b0154998c8b6001600160601b03948582168b8401528760c09e8f88828d1c16818701521c16818401521c90610100015283519b61117a8d611f93565b6003015499828d8c94851690528360081c161515908d01521c16908901528560701c16908701528d8460901c16908601528260b01c6001600160401b0316908501521c908201526101208201526111d1828c6123fa565b526111dc818b6123fa565b50600101611076565b84603285634e487b7160e01b5f52525ffd5b611204919950889061241b565b9789611069565b83346102a157602090816003193601126102a1576001600160a01b0380611230611dca565b1692835f526002906002815263ffffffff908161124f865f2054612920565b169361125a856123ab565b965f5b868110611271578751806103f58b82611f1a565b815f528584528481895f20906112869161348d565b919054600392831b1c61129890612920565b165f5260018086528b86898c5f20938d8051966112b488611f63565b8654928b8416895260a093841c868a0152870154918b8316818a01528d6010926060936001600160501b0360b01b86821b16858d015260ff918c60f099846080998c1c161515898301528d0154809c6001600160601b03968783168c85015260c09e8f89828d1c16818701521c16818401521c90610100015284519b6113398d611f93565b015498828a9384168d528360081c161515908c01521c16908801528c8560701c16908701528b8460901c16908601528260b01c6001600160401b0316908501521c9082015261012082015261138e828b6123fa565b52611399818a6123fa565b5060010161125d565b83346102a1575f3660031901126102a157517f000000000000000000000000b7a842f997cdbd0291d58f0d4ce729436369c8396001600160a01b03168152602090f35b83346102a15760203660031901126102a1576020906001600160a01b0361140a611dca565b165f52600c825260ff815f20541690519015158152f35b83346102a1575f3660031901126102a1576020906008549051908152f35b83346102a15760203660031901126102a1576020906001600160a01b03611464611dca565b165f5260028252805f20549051908152f35b83346102a1575f3660031901126102a1576020906001600160401b03600954169051908152f35b8390346102a1576114ad36611de0565b60ff92916114c260ff600954871c161561223c565b5f5b63ffffffff908181168481101561078057826114e18287876122e2565b6114ea90612306565b165f526001888760208381528a835f20918451809961150882611f63565b845490600160a01b6001900398898316845260a092831c8585015286015490898216898501526010986060906001600160501b0360b01b848c1b168287015260f09588608095881c1615158582015260028a0154986001600160601b039c828b8f948a019e8f86831690528860c09e8f88828c1c16818701521c16818401521c90610100015283519b61159a8d611f93565b6003015499828d8c94851690528360081c161515908d01521c1690890152818660701c16908801528460901c16908601528260b01c6001600160401b0316908501521c908201526101208701525116151590611603948261162e575b5050611608575b506122b9565b6114c4565b61161e6116196116289287876122e2565b612306565b339061077a612272565b876115fd565b9091505116331489806115f6565b83346102a1575f3660031901126102a1576020906003549051908152f35b8284346102a1575f3660031901126102a1575f546001600160a01b0392908316330361038857827f000000000000000000000000b7a842f997cdbd0291d58f0d4ce729436369c8391682519163bf40fac160e01b938484526020938483820152600560248201526453746f726560d81b60448201528481606481875afa9081156119ec579087915f916119b4575b5016946001600160601b0360a01b948686600d541617600d5582518281528185820152601060248201526f2a3930b234b733ab30b634b230ba37b960811b60448201528181606481895afa9081156118e4579089915f9161197c575b5016918287600f541617600f5583518181528286820152600f60248201526e526566657272616c53746f7261676560881b604482015282816064818a5afa90811561197257908a915f9161193a575b5016948588601054161760105584518281528382820152600860248201526722bc32b1baba37b960c11b604482015283816064818b5afa908115611930578492918c915f916118ee575b5093606491849516998a8c6011541617601155885195869485938452830152600f60248301526e2837b9b4ba34b7b726b0b730b3b2b960891b60448301525afa9081156118e4575f9161187b575b50907ff4aa8c967b4ad917e7d98f8e11fe6ab3e28b2eec7f6423ba8229c8f21375116f9860a098979695949392168096600e541617600e55825196875286015284015260608301526080820152a1005b9796959493929180915088813d83116118dd575b6118998183611fc1565b810103126102a1577ff4aa8c967b4ad917e7d98f8e11fe6ab3e28b2eec7f6423ba8229c8f21375116f986118ce60a0996122a5565b9192939495969798509861182b565b503d61188f565b84513d5f823e3d90fd5b94938092508591503d8311611929575b6119088183611fc1565b810103126102a15760648493928c61192086956122a5565b919550916117dd565b503d6118fe565b86513d5f823e3d90fd5b809250848092503d831161196b575b6119538183611fc1565b810103126102a1576119658a916122a5565b8b611793565b503d611949565b85513d5f823e3d90fd5b809250838092503d83116119ad575b6119958183611fc1565b810103126102a1576119a789916122a5565b8a611744565b503d61198b565b809250868092503d83116119e5575b6119cd8183611fc1565b810103126102a1576119df87916122a5565b886116e8565b503d6119c3565b82513d5f823e3d90fd5b8284346102a157602090816003193601126102a157611a13611db7565b93611a2560ff600954841c161561223c565b63ffffffff908186165f5260018452825f2091835191611a4483611f63565b83549360018060a01b0394858116855260a01c8785015260ff6001820154868116888701526001600160501b0360b01b8160101b16606087015260f01c16151560808501526002810154926001600160601b039360a08601938582168552858260601c1660c0880152828260c01c16818801521c6101008601526003875192611acc84611f93565b01549060ff8216835260ff8260081c16151589840152848260101c1688840152808260701c1660608401528160901c1660808301526001600160401b038160b01c1660a083015260f01c60c0820152610120840152511615611b665751163303611b3d57610780338561077a612272565b5162461bcd60e51b815291820152600560248201526410bab9b2b960d91b604482015260649150fd5b50505162461bcd60e51b815291820152600660248201526510b7b93232b960d11b604482015260649150fd5b83346102a1575f3660031901126102a157600f5490516001600160a01b039091168152602090f35b83346102a1575f3660031901126102a1576020906007549051908152f35b83346102a1575f3660031901126102a1575f5490516001600160a01b039091168152602090f35b5050346102a15760203660031901126102a1575f54813591906001600160a01b0316330361028a578115611c9b57600854821015611c67577fb389411734ec38434cf45d5ddb15e04046d44ade0e97b4dd832a6e781f81015a602083858160075551908152a1005b606490602084519162461bcd60e51b8352820152600e60248201526d085b1d17dd1c9a59d9d95c9d1d1b60921b6044820152fd5b606490602084519162461bcd60e51b835282015260086024820152672167745f7a65726f60c01b6044820152fd5b83346102a15760203660031901126102a1576020906001600160a01b03611cee611dca565b165f52600b825260ff815f20541690519015158152f35b83346102a1575f3660031901126102a157335f52600c602052805f20600160ff198254161790557fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47648151915f83525f60208401523392a2005b346102a15760203660031901126102a157610780611d7a611db7565b611d8f60018060a01b03600e54163314612200565b612428565b346102a1575f3660031901126102a1576010546001600160a01b03168152602090f35b6004359063ffffffff821682036102a157565b600435906001600160a01b03821682036102a157565b9060206003198301126102a1576004356001600160401b03928382116102a157806023830112156102a15781600401359384116102a15760248460051b830101116102a1576024019190565b61ffff60c06101e09260018060a01b038082511686526020820151906001600160601b03809216602088015260408301511660408701526001600160501b0360b01b60608301511660608701526080820151151560808701528060a08301511660a08701528083830151168387015260e08201519063ffffffff80921660e088015261010082818501511690880152610120809301519260ff8451169088015260208301511515610140880152604083015116610160870152806060830151166101808701526080820151166101a08601526001600160401b0360a0820151166101c0860152015116910152565b60209060206040818301928281528551809452019301915f5b828110611f41575050505090565b909192938261020082611f576001948951611e2c565b01950193929101611f33565b61014081019081106001600160401b03821117611f7f57604052565b634e487b7160e01b5f52604160045260245ffd5b60e081019081106001600160401b03821117611f7f57604052565b6001600160401b038111611f7f57604052565b90601f801991011681019081106001600160401b03821117611f7f57604052565b61020435906001600160601b03821682036102a157565b61022435906001600160601b03821682036102a157565b6004359081151582036102a157565b610244359061ffff821682036102a157565b906102006003198301126102a1576040519161204c83611f63565b6001600160a01b0390839060043583811681036102a15782526001600160601b039260243584811681036102a157602084015260443590811681036102a15760408301526064356001600160b01b0319811681036102a157606083015260843580151581036102a157608083015260a43583811681036102a15760a083015260c43583811681036102a15760c083015263ffffffff9060e43582811681036102a15760e0840152610104359082821682036102a15760e0916101008501526101231901126102a1576040519261212184611f93565b6101243560ff811681036102a15784526101443580151581036102a15760208501526101643590811681036102a15760408401526101843581811681036102a15760608401526101a43590811681036102a15760808301526101c4356001600160401b03811681036102a15760a08301526101e4359061ffff821682036102a1576101209160c08401520152565b6001600160401b038111611f7f57601f01601f191660200190565b9291926121d6826121af565b916121e46040519384611fc1565b8294818452818301116102a1578281602093845f960137010152565b1561220757565b60405162461bcd60e51b815260206004820152600d60248201526c085d5b985d5d1a1bdc9a5e9959609a1b6044820152606490fd5b1561224357565b60405162461bcd60e51b8152602060048201526007602482015266085c185d5cd95960ca1b6044820152606490fd5b60405190604082018281106001600160401b03821117611f7f576040526007825266313c96bab9b2b960c91b6020830152565b51906001600160a01b03821682036102a157565b63ffffffff8091169081146122ce5760010190565b634e487b7160e01b5f52601160045260245ffd5b91908110156122f25760051b0190565b634e487b7160e01b5f52603260045260245ffd5b3563ffffffff811681036102a15790565b6001600160401b038111611f7f5760051b60200190565b6040519061233b82611f63565b815f81525f60208201525f60408201525f60608201525f60808201525f60a08201525f60c08201525f60e08201525f6101008201526101206040519161238083611f93565b5f83525f60208401525f60408401525f60608401525f60808401525f60a08401525f60c08401520152565b906123b582612317565b6123c26040519182611fc1565b82815280926123d3601f1991612317565b01905f5b8281106123e357505050565b6020906123ee61232e565b828285010152016123d7565b80518210156122f25760209160051b010190565b919082018092116122ce57565b919082039182116122ce57565b63ffffffff80911690815f5260209060018252604090815f209282519461244e86611f63565b84549260018060a01b0393848116885260a01c8388015260ff6001870154858116878a01526001600160501b0360b01b8160101b1660608a015260f01c161515608088015260028601546001600160601b039160a08901918381168352838160601c1660c08b0152818160c01c1660e08b015260e01c6101008a015260038751986124d88a611f93565b01549060ff8216895260ff8260081c161515868a0152838260101c16888a0152808260701c1660608a01528160901c1660808901526001600160401b038160b01c1660a089015260f01c60c088015261012088019687525116156125ad57807fa79f5bd225998a95d4de82c67e16e86bc61191a4faa2dbcb0e1a89886af5a0058360ff6003986001975f9b51168b5260028352612577858a8d206143bd565b5061258185614202565b5061258b856142fe565b505151168751908152a285525282208281558260018201558260028201550155565b505050505050565b9190916001600160601b03808094169116019182116122ce57565b91908251928382525f5b8481106125fa575050825f602080949584010152601f8019910116010190565b6020818301810151848301820152016125da565b905f9263ffffffff9182841693845f526001602052604095865f20918780519361263785611f63565b80549760018060a01b03988981168752602087019060a01c815260018301549460ff858901968c811688526001600160501b0360b01b8160101b1660608b015260f01c16151560808901526002840154906001600160601b03809460a08b0190828516825260c08c0194838160601c1686528c60e0838360c01c1691015260e01c6101008d015260038951986126cc8a611f93565b01549860ff8a16895260ff8a60081c16151560208a0152838a60101c1690890152808960701c1660608901528860901c1660808801526001600160401b0397888160b01c1660a089015260f01c60c08801526101208b01968752511615612910578b936127398a92612428565b5f9760208751015115612834575b50505050505060a0905101511690811515908161282b575b506127ae575b50508392917f7ffce3199c8273ece91032d8f943315778e942eff22f8f7d8a7da972073a58b1946127a99251169680519485941684528060208501528301906125d0565b0390a3565b85600d541690813b1561282757885163078d3b7960e01b81525f60048201526001600160a01b03871660248201526001600160401b039190911660448201529082908290606490829084905af1801561281d5715612765576128108291611fae565b61281a5780612765565b80fd5b88513d84823e3d90fd5b8280fd5b9050155f61275f565b8598509461287e9293949551169384159081612901575b828293612867939b80600d5416975116985116915116906125b5565b90156128fa57868060a088510151165b16906125b5565b90803b156102a1578d5163078d3b7960e01b81526001600160a01b0393841660048201529390921660248401526001600160601b031660448301525f908290606490829084905af180156128f0576128dc575b808087818c94612747565b6128e7919450611fae565b5f9260a06128d1565b8b513d5f823e3d90fd5b865f612877565b84518a168e8b1614915061284b565b5050505050505050505050505050565b63ffffffff90818111612931571690565b60405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608490fd5b519081151582036102a157565b51906001600160601b03821682036102a157565b9291905f946001600160601b0390818316159283801580916131f0575b80156131e3575b6131d3575b8861012097888a0198895160208101511590818015906131c5575b612f7f575b506001600160a01b039a91508a9050612a078c613506565b16808c523303612f73575b6001600160401b0391826009541660a0835101528260a08351015116988c612a3981613603565b99809150151580612f66575b612ef0575b50508590612ee5575b8015612ed8575b612baf575b505050505050612a7e935084604087015116155f14612ba7571661240e565b803410612b7557612a8f903461241b565b9182612a9c575b50505050565b511690804710612b30578280929181925af1612ab66134d7565b5015612ac5575f808080612a96565b60405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608490fd5b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606490fd5b60405162461bcd60e51b815260206004820152600a602482015269216d73672d76616c756560b01b6044820152606490fd5b50508461240e565b87604083949596979a93510151168681612db0575b8d915080612da5575b612d1f575b88612a7e9b8760808194018051159052606087510163ffffffff815116612d17575b505016918215612cc557505090612c2691604084510152600283515260016020845101528360a084510151169061240e565b96612c308b613603565b50945b612c81575b50505063ffffffff8116151580612c72575b612c58575b88818080612a5f565b81612c66612c6b93836141c9565b6141c9565b5f80612c4f565b5063ffffffff82161515612c4a565b612cb294979350908660a09216604082510152600181515260016020825101528b60c082510152510151169061240e565b92612cbc87613603565b505f8080612c38565b61ffff919792509992991680612cdc575b50612c33565b612d0592999196506003845152600160208551015260c0845101528360a084510151169061240e565b96612d0f8b613603565b50945f612cd6565b528f5f612bf4565b6080919a955001511580159081612d97575b8115612d7c575b5015612d47578b93988b612bd2565b60405162461bcd60e51b815260206004820152600d60248201526c085d1c1cdb0b5a5b9d985b1a59609a1b6044820152606490fd5b905080612d8a575b5f612d38565b5086841687891610612d84565b8589168a8a16119150612d31565b50888b161515612bcd565b612e53575b50878a1680612dc5575b86612bc4565b90945060808c01511580159182612e42575b8215612e20575b505015612ded578b935f612dbf565b60405162461bcd60e51b815260206004820152600b60248201526a085cdb0b5a5b9d985b1a5960aa1b6044820152606490fd5b90915081612e31575b505f80612dde565b90508760408451015116105f612e29565b8451604001518a1681109250612dd7565b90945060808c01511580159182612ecb575b8215612eae575b505015612e7b578b935f612db5565b60405162461bcd60e51b815260206004820152600b60248201526a085d1c0b5a5b9d985b1a5960aa1b6044820152606490fd5b90915081612ebf575b505f80612e6c565b9050878916105f612eb7565b809250898b161191612e65565b5061ffff81161515612a5a565b50878a161515612a53565b909192939495508c806010541692511690823b156102a15760445f928360405195869485936356b4b2ad60e01b8552600485015260248401525af18015612f5b57612f42575b908c8e95949392612a4a565b612f50919493929d50611fae565b5f9b9091925f612f36565b6040513d5f823e3d90fd5b508d601054161515612a45565b5f608082510152612a12565b51929350909160ff1660031461316f575b50600e546040808c015160608d0151825163052ec72760e51b81523360048201526001600160a01b0392831660248201526001600160b01b0319918216604482015292949093909291839183916064918391165afa9283156118e4575f93613093575b505050866080820151161561306057810151151560808b015115151461301d579081808c936129ef565b5162461bcd60e51b815260206004820152601760248201527f217265647563652d77726f6e672d646972656374696f6e0000000000000000006044820152606490fd5b815162461bcd60e51b815260206004820152600c60248201526b10b73796b837b9b4ba34b7b760a11b6044820152606490fd5b909180935082813d8311613168575b6130ac8183611fc1565b810103126102a15783519283018381106001600160401b03821117611f7f5784526130d6826122a5565b8352602082015190811681036102a15760208301526130f6838201612985565b83830152613106606082016122a5565b606083015261311760808201612992565b608083015260a081015163ffffffff811681036102a15760a083015261313f60c08201612992565b60c083015261315060e08201612992565b60e083015261010080910151908201525f8080612ff3565b503d6130a2565b613180575f60408a5101525f612f90565b60405162461bcd60e51b815260206004820152601a60248201527f21747261696c696e672d73746f702d6d7573742d7265647563650000000000006044820152606490fd5b50600360ff825116146129ea565b5f60206101208a015101526129cf565b5061ffff871615156129ca565b5083861615156129c3565b5f547f6802d0b735cb1927bc8d580cfb1f7e0307b427272c81609ccce7a80ab5afb070602060ff63ffffffff613235818660a01c166122b9565b958187169563ffffffff60a01b8860a01b169063ffffffff60a01b1916175f55610100810190868252865f5260018552610120600360405f2060018060a01b039485855116956001600160601b0360a01b8a87015160a01b1687178355600183019060408701511681546060880151918b60f01b60808a0151151560f01b16926001600160501b0360a01b9060101c16918c60f81b1617171790556001600160601b0360a086015116906001600160601b0360601b60c087015160601b169163ffffffff60c01b60e088015160c01b169163ffffffff60e01b905160e01b16921717176002820155019101926133a98451918683511684549061ff008a860151151560081b166dffffffffffffffffffffffff0000604087015160101b169163ffffffff60701b606088015160701b169371ffffffffffffffffffffffffffffffffffff1916171717178455608083015116839081549063ffffffff60901b9060901b169063ffffffff60901b1916179055565b60a08101519082549060c061ffff60f01b91015160f01b16916001600160401b0360b01b9060b01b169060018060b01b0316171790555f52600283526133f28560405f2061458a565b508051518216613414576134058561453c565b505b515116604051908152a290565b61341d856144e9565b50613407565b6003548110156122f25760035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01905f90565b6005548110156122f25760055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001905f90565b80548210156122f2575f5260205f2001905f90565b9060418151145f146134ce576134ca91602082015190606060408401519301515f1a9061446e565b9091565b50505f90600290565b3d15613501573d906134e8826121af565b916134f66040519384611fc1565b82523d5f602084013e565b606090565b335f52600b60205260ff60405f20541661351f57503390565b80516001600160a01b039190821615158061359b575b8061358a575b1561354557511690565b60405162461bcd60e51b815260206004820152601a60248201527f66756e64696e672d6163636f756e742d6f726465722d6661696c0000000000006044820152606490fd5b5060ff61012082015151161561353b565b506020610120820151015115613535565b519060ff821682036102a157565b519061ffff821682036102a157565b156135d057565b60405162461bcd60e51b815260206004820152600b60248201526a216d61782d65787069727960a81b6044820152606490fd5b905f91600460ff610120830151511610156141965761012081015160ff81511660018114801561418c575b156141155750506001600160601b036040610120830151015116156140e7575b600d54604082810151905163185c596360e11b81526001600160a01b03918216600482015291169390606081602481885afa8015612f5b575f9061405f575b60200180516affffffffffffffffffffff925082161561402a5760206101208501510151159182159261400d575b505015613fdc576060820151604051637061696b60e01b81526001600160b01b031990911660048201526101c081602481885afa908115612f5b575f91613e9f575b5061ffff60808201511615613e695763ffffffff606061012085015101511680613de0575b5063ffffffff6080610120850151015116613d75575b63ffffffff61374642612920565b1660e08401526001600160601b0360a08401511661ffff60c083015116026001600160601b0381169081036122ce576001600160601b03612710820411613d215761271090046001600160601b031660c0840152610120830151602001515f9586929091156139a55750508160208401525b6001600160401b0360a061012085015101511690811515908161399c575b5061392a575b50506137e7816131fb565b63ffffffff8116610100830152600160a01b60019003825116600160a01b60019003604084015116916001600160501b0360b01b6060850151169060208501516001600160601b031660a08601516001600160601b03166101208701519060408201516001600160601b031660c08901516001600160601b031660808a01511515845160ff16906020860151151592606087015163ffffffff1694608088015163ffffffff169660a08901516001600160401b03169860c0015161ffff16996040519c8d5260208d015260408c015260608b015260808a015260a089015260c088015260e08701526101008601526101208501523361014085015261016084015261018083015263ffffffff16906101a07feb2d670c93b7063878c5ca9fe0ec6eb06b845e9e4250f6cf7e0087680422efb291a4610100015163ffffffff169190565b600d546001600160a01b031690813b156128275760405163e4652f4960e01b81525f60048201523360248201526001600160401b0382166044820152918391839160649183915af1801561399157156137dc576139878291611fae565b61281a57806137dc565b6040513d84823e3d90fd5b9050155f6137d6565b909550610160810151613ce6576001600160601b0360208501511615613cb7576001600160601b0360a08501511690670de0b6b3a76400009180830290838204036122ce576001600160601b03602087015116908115613ca3570490828210613c6e576080015161ffff168281029281159184041417156122ce5711613c3957600f546040840151606085015160a08601516001600160601b0316926001600160a01b03928316926001600160b01b03199092169116803b156102a1575f9260649160405195869485936318512afb60e21b85526004850152602484015260448301525afa8015612f5b57613c26575b50613abb6001600160601b036020850151166001600160601b0360c086015116906125b5565b60408401519095906001600160a01b031680613ba25750505060018060a01b03600d54166001600160401b03906001600160601b03613b058360a0610120880151015116886125b5565b1691613b2960018060a01b036040870151169160a0610120880151015116886125b5565b92823b15613b9e5760405163e4652f4960e01b81526001600160a01b039290921660048301523360248301526001600160601b0393909316604482015291839183916064918391905af1801561399157908291613b8a575b505060016137b8565b613b9390611fae565b61281a57805f613b81565b8480fd5b813b15613c225760405163e4652f4960e01b81526001600160a01b039190911660048201523360248201526001600160601b03871660448201529083908290606490829084905af18015613c1757908391613bff575b50506137b8565b613c0890611fae565b613c1357815f613bf8565b5080fd5b6040513d85823e3d90fd5b8380fd5b613c31919250611fae565b5f905f613a95565b60405162461bcd60e51b815260206004820152600d60248201526c216d61782d6c6576657261676560981b6044820152606490fd5b60405162461bcd60e51b815260206004820152600d60248201526c216d696e2d6c6576657261676560981b6044820152606490fd5b634e487b7160e01b5f52601260045260245ffd5b60405162461bcd60e51b815260206004820152600760248201526610b6b0b933b4b760c91b6044820152606490fd5b60405162461bcd60e51b8152602060048201526013602482015272216d61726b65742d7265647563652d6f6e6c7960681b6044820152606490fd5b60405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201526536206269747360d01b6064820152608490fd5b82516001600160a01b03165f9081526002602090815260408083206101208701516080015163ffffffff1684526001019091529020546137385760405162461bcd60e51b815260206004820152600960248201526821757365722d6f636f60b81b6044820152606490fd5b4211613e3457613dff4263ffffffff606061012087015101511661241b565b6101208401515160ff16613e2157613e1b9060075410156135c9565b5f613722565b613e2f9060085410156135c9565b613e1b565b60405162461bcd60e51b815260206004820152600d60248201526c216578706972792d76616c756560981b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d216d61726b65742d65786973747360901b6044820152606490fd5b90506101c0813d6101c011613fd4575b81613ebd6101c09383611fc1565b810103126102a157604051906101c082018281106001600160401b03821117611f7f576040528051825260208101516001600160a01b0319811681036102a1576020830152613f0e604082016122a5565b604083015260608101516060830152613f29608082016135ba565b6080830152613f3a60a082016135ba565b60a0830152613f4b60c082016135ba565b60c0830152613f5c60e082016135ba565b60e0830152610100613f6f8183016135ba565b90830152613f8061012082016135ac565b610120830152610140613f948183016135ac565b90830152610160613fa6818301612985565b90830152610180613fb88183016135ba565b90830152613fca6101a08092016135ba565b908201525f6136fd565b3d9150613eaf565b60405162461bcd60e51b8152602060048201526009602482015268216d696e2d73697a6560b81b6044820152606490fd5b9091506001600160601b0360a08501511691511611155f806136bb565b60405162461bcd60e51b815260206004820152600d60248201526c2161737365742d65786973747360981b6044820152606490fd5b506060813d6060116140df575b8161407960609383611fc1565b810103126102a157604051606081018181106001600160401b03821117611f7f576040526140a6826135ac565b81526020820151916affffffffffffffffffffff831683036102a15760406140d59160209485850152016122a5565b604082015261368d565b3d915061406c565b60405162461bcd60e51b815260206004820152600660248201526521707269636560d01b6044820152606490fd5b600314614123575b5061364e565b60c0015161ffff16801515908161417f575b5015614141575f61411d565b60405162461bcd60e51b8152602060048201526016602482015275085d1c985a5b1a5b99cb5cdd1bdc0b5a5b9d985b1a5960521b6044820152606490fd5b6107d0915011155f614135565b506002811461362e565b60405162461bcd60e51b815260206004820152600b60248201526a216f726465722d7479706560a81b6044820152606490fd5b63ffffffff165f908152600160205260409020600301805463ffffffff60901b191660909290921b63ffffffff60901b16919091179055565b805f526004908160205260405f20548015155f146142f7575f19908082018181116142e457600354908382019182116142d157808203614288575b50505060035480156142755781019061425582613423565b909182549160031b1b191690556003555f526020525f6040812055600190565b603184634e487b7160e01b5f525260245ffd5b6142bc6142976142a693613423565b90549060031b1c928392613423565b819391549060031b91821b915f19901b19161790565b90555f528360205260405f20555f808061423d565b601186634e487b7160e01b5f525260245ffd5b601185634e487b7160e01b5f525260245ffd5b5050505f90565b5f8181526006602052604090205480156143b7575f19908082018181116122ce57600554908382019182116122ce57808203614383575b505050600554801561436f5781019061434d82613458565b909182549160031b1b191690556005555f5260066020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b6143a16143926142a693613458565b90549060031b1c928392613458565b90555f52600660205260405f20555f8080614335565b50505f90565b906001820191815f528260205260405f2054908115155f14614466575f19918083018181116122ce578254908482019182116122ce57808203614431575b5050508054801561436f57820191614413838361348d565b909182549160031b1b19169055555f526020525f6040812055600190565b6144516144416142a6938661348d565b90549060031b1c9283928661348d565b90555f528460205260405f20555f80806143fb565b505050505f90565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116144de576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15612f5b575f516001600160a01b038116156144d657905f90565b505f90600190565b505050505f90600390565b805f52600660205260405f2054155f1461453757600554600160401b811015611f7f576145206142a6826001859401600555613458565b9055600554905f52600660205260405f2055600190565b505f90565b805f52600460205260405f2054155f1461453757600354600160401b811015611f7f576145736142a6826001859401600355613423565b9055600354905f52600460205260405f2055600190565b5f8281526001820160205260409020546143b757805490600160401b821015611f7f57826145c26142a684600180960185558461348d565b90558054925f520160205260405f205560019056fea264697066735822122073544964512f140197b410bd1dbd91dfadf04ff2d797ff6ecaaec45306031c5164736f6c63430008160033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b7a842f997cdbd0291d58f0d4ce729436369c839
-----Decoded View---------------
Arg [0] : _addressStorage (address): 0xB7A842f997cDBD0291D58F0d4Ce729436369C839
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000b7a842f997cdbd0291d58f0d4ce729436369c839
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.