Contract
0xb67c152e69217b5acb85a2e19df13423351b0e27
11
Contract Overview
My Name Tag:
Not Available, login to update
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
Contract Name:
PositionManager
Compiler Version
v0.8.7+commit.e28d00a7
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "./IPikaPerp.sol"; import '../lib/UniERC20.sol'; import "./IPikaPerp.sol"; import "./PikaPerpV4.sol"; import "../access/Governable.sol"; import "../referrals/IReferralStorage.sol"; contract PositionManager is Governable, ReentrancyGuard { using SafeMath for uint256; using SafeERC20 for IERC20; using UniERC20 for IERC20; using Address for address payable; struct OpenPositionRequest { address account; uint256 productId; uint256 margin; uint256 leverage; uint256 tradeFee; bool isLong; uint256 acceptablePrice; uint256 executionFee; uint256 index; uint256 blockNumber; uint256 blockTime; } struct ClosePositionRequest { address account; uint256 productId; uint256 margin; bool isLong; uint256 acceptablePrice; uint256 executionFee; uint256 index; uint256 blockNumber; uint256 blockTime; } address public admin; address public immutable pikaPerp; address public feeCalculator; address public oracle; address public referralStorage; address public immutable collateralToken; uint256 public minExecutionFee; uint256 public minBlockDelayKeeper; uint256 public minTimeExecuteDelayPublic; uint256 public minTimeCancelDelayPublic; uint256 public maxTimeDelay; bool public isUserExecuteEnabled = true; bool public isUserCancelEnabled = true; bool public allowPublicKeeper = false; bool public allowUserCloseOnly = false; bytes32[] public openPositionRequestKeys; bytes32[] public closePositionRequestKeys; uint256 public openPositionRequestKeysStart; uint256 public closePositionRequestKeysStart; uint256 public immutable tokenBase; uint256 public constant BASE = 1e8; mapping (address => bool) public isPositionKeeper; mapping (address => uint256) public openPositionsIndex; mapping (bytes32 => OpenPositionRequest) public openPositionRequests; mapping (address => uint256) public closePositionsIndex; mapping (bytes32 => ClosePositionRequest) public closePositionRequests; mapping (address => bool) public managers; mapping (address => mapping (address => bool)) public approvedManagers; event CreateOpenPosition( address indexed account, uint256 productId, uint256 margin, uint256 leverage, uint256 tradeFee, bool isLong, uint256 acceptablePrice, uint256 executionFee, uint256 index, uint256 blockNumber, uint256 blockTime, uint256 gasPrice ); event ExecuteOpenPosition( address indexed account, uint256 productId, uint256 margin, uint256 leverage, uint256 tradeFee, bool isLong, uint256 acceptablePrice, uint256 executionFee, uint256 index, uint256 timeGap, bytes32 referralCode, address referral ); event CancelOpenPosition( address indexed account, uint256 productId, uint256 margin, uint256 leverage, uint256 tradeFee, bool isLong, uint256 acceptablePrice, uint256 executionFee, uint256 index, uint256 blockGap, uint256 timeGap ); event CreateClosePosition( address indexed account, uint256 productId, uint256 margin, bool isLong, uint256 acceptablePrice, uint256 executionFee, uint256 index, uint256 blockNumber, uint256 blockTime ); event ExecuteClosePosition( address indexed account, uint256 productId, uint256 margin, bool isLong, uint256 acceptablePrice, uint256 executionFee, uint256 index, uint256 blockGap, uint256 timeGap, bytes32 referralCode, address referral ); event CancelClosePosition( address indexed account, uint256 productId, uint256 margin, bool isLong, uint256 acceptablePrice, uint256 executionFee, uint256 index, uint256 blockGap, uint256 timeGap ); event ExecuteOpenPositionError(address indexed account, uint256 index, string executionError); event ExecuteClosePositionError(address indexed account, uint256 index, string executionError); event SetPositionKeeper(address indexed account, bool isActive); event SetMinExecutionFee(uint256 minExecutionFee); event SetIsUserExecuteEnabled(bool isUserExecuteEnabled); event SetIsUserCancelEnabled(bool isUserCancelEnabled); event SetDelayValues(uint256 minBlockDelayKeeper, uint256 minTimeExecuteDelayPublic, uint256 minTimeCancelDelayPublic, uint256 maxTimeDelay); event SetRequestKeysStartValues(uint256 increasePositionRequestKeysStart, uint256 decreasePositionRequestKeysStart); event SetAllowPublicKeeper(bool allowPublicKeeper); event SetAllowUserCloseOnly(bool allowUserCloseOnly); event SetManager(address manager, bool isActive); event SetAccountManager(address account, address manager, bool isActive); event SetReferralStorage(address referralStorage); event SetAdmin(address admin); modifier onlyAdmin() { require(msg.sender == admin, "PositionManager: !admin"); _; } constructor( address _pikaPerp, address _feeCalculator, address _oracle, address _collateralToken, uint256 _minExecutionFee, uint256 _tokenBase ) public { pikaPerp = _pikaPerp; feeCalculator = _feeCalculator; oracle = _oracle; collateralToken = _collateralToken; minExecutionFee = _minExecutionFee; tokenBase = _tokenBase; admin = msg.sender; } function setFeeCalculator(address _feeCalculator) external onlyAdmin { feeCalculator = _feeCalculator; } function setOracle(address _oracle) external onlyAdmin { oracle = _oracle; } function setPositionKeeper(address _account, bool _isActive) external onlyAdmin { isPositionKeeper[_account] = _isActive; emit SetPositionKeeper(_account, _isActive); } function setMinExecutionFee(uint256 _minExecutionFee) external onlyAdmin { minExecutionFee = _minExecutionFee; emit SetMinExecutionFee(_minExecutionFee); } function setIsUserExecuteEnabled(bool _isUserExecuteEnabled) external onlyAdmin { isUserExecuteEnabled = _isUserExecuteEnabled; emit SetIsUserExecuteEnabled(_isUserExecuteEnabled); } function setIsUserCancelEnabled(bool _isUserCancelEnabled) external onlyAdmin { isUserCancelEnabled = _isUserCancelEnabled; emit SetIsUserCancelEnabled(_isUserCancelEnabled); } function setDelayValues( uint256 _minBlockDelayKeeper, uint256 _minTimeExecuteDelayPublic, uint256 _minTimeCancelDelayPublic, uint256 _maxTimeDelay ) external onlyAdmin { minBlockDelayKeeper = _minBlockDelayKeeper; minTimeExecuteDelayPublic = _minTimeExecuteDelayPublic; minTimeCancelDelayPublic = _minTimeCancelDelayPublic; maxTimeDelay = _maxTimeDelay; emit SetDelayValues(_minBlockDelayKeeper, _minTimeExecuteDelayPublic, _minTimeCancelDelayPublic, _maxTimeDelay); } function setRequestKeysStartValues(uint256 _increasePositionRequestKeysStart, uint256 _decreasePositionRequestKeysStart) external onlyAdmin { openPositionRequestKeysStart = _increasePositionRequestKeysStart; closePositionRequestKeysStart = _decreasePositionRequestKeysStart; emit SetRequestKeysStartValues(_increasePositionRequestKeysStart, _decreasePositionRequestKeysStart); } function setAllowPublicKeeper(bool _allowPublicKeeper) external onlyAdmin { allowPublicKeeper = _allowPublicKeeper; emit SetAllowPublicKeeper(_allowPublicKeeper); } function setAllowUserCloseOnly(bool _allowUserCloseOnly) external onlyAdmin { allowUserCloseOnly = _allowUserCloseOnly; emit SetAllowUserCloseOnly(_allowUserCloseOnly); } function setManager(address _manager, bool _isActive) external onlyAdmin { managers[_manager] = _isActive; emit SetManager(_manager, _isActive); } function setAccountManager(address _manager, bool _isActive) external { approvedManagers[msg.sender][_manager] = _isActive; emit SetAccountManager(msg.sender, _manager, _isActive); } function setReferralStorage(address _referralStorage) external onlyAdmin { referralStorage = _referralStorage; emit SetReferralStorage(_referralStorage); } function setAdmin(address _admin) external onlyGov { admin = _admin; emit SetAdmin(_admin); } function executeNPositionsWithPrices( bytes[] calldata _priceUpdateData, uint256 n, address payable _executionFeeReceiver ) external payable { executePositionsWithPrices(_priceUpdateData, openPositionRequestKeysStart + n, closePositionRequestKeysStart + n, _executionFeeReceiver); } function executePositionsWithPrices( bytes[] calldata _priceUpdateData, uint256 _openEndIndex, uint256 _closeEndIndex, address payable _executionFeeReceiver ) public payable { require(isPositionKeeper[msg.sender] || allowPublicKeeper, "PositionManager: !positionKeeper"); IOracle(oracle).setPrices{value: msg.value}(msg.sender, _priceUpdateData); _executePositions(_openEndIndex, _closeEndIndex, _executionFeeReceiver); } function _executeNPositions(uint256 n, address payable _executionFeeReceiver) private { require(canKeeperExecute(), "PositionManager: cannot execute"); _executePositions(openPositionRequestKeysStart + n, closePositionRequestKeysStart + n, _executionFeeReceiver); } function _executePositions(uint256 _openEndIndex, uint256 _closeEndIndex, address payable _executionFeeReceiver) private { _executeOpenPositions(_openEndIndex, _executionFeeReceiver); _executeClosePositions(_closeEndIndex, _executionFeeReceiver); } function _executeOpenPositions(uint256 _endIndex, address payable _executionFeeReceiver) private { uint256 index = openPositionRequestKeysStart; uint256 length = openPositionRequestKeys.length; if (index >= length) { return; } if (_endIndex > length) { _endIndex = length; } while (index < _endIndex) { bytes32 key = openPositionRequestKeys[index]; // if the request was executed then delete the key from the array // if the request was not executed then break from the loop, this can happen if the // minimum number of blocks has not yet passed // an error could be thrown if the request is too old or if the slippage is // higher than what the user specified, or if there is insufficient liquidity for the position // in case an error was thrown, cancel the request try this.executeOpenPosition(key, _executionFeeReceiver) returns (bool _wasExecuted) { if (!_wasExecuted) { break; } } catch Error(string memory executionError) { emit ExecuteOpenPositionError(openPositionRequests[key].account, index, executionError); // wrap this call in a try catch to prevent invalid cancels from blocking the loop try this.cancelOpenPosition(key, _executionFeeReceiver) returns (bool _wasCancelled) { if (!_wasCancelled) { break; } } catch {} } catch (bytes memory /*lowLevelData*/) { // wrap this call in a try catch to prevent invalid cancels from blocking the loop try this.cancelOpenPosition(key, _executionFeeReceiver) returns (bool _wasCancelled) { if (!_wasCancelled) { break; } } catch {} } delete openPositionRequestKeys[index]; index++; } openPositionRequestKeysStart = index; } function _executeClosePositions(uint256 _endIndex, address payable _executionFeeReceiver) private { uint256 index = closePositionRequestKeysStart; uint256 length = closePositionRequestKeys.length; if (index >= length) { return; } if (_endIndex > length) { _endIndex = length; } while (index < _endIndex) { bytes32 key = closePositionRequestKeys[index]; // if the request was executed then delete the key from the array // if the request was not executed then break from the loop, this can happen if the // minimum number of blocks has not yet passed // an error could be thrown if the request is too old // in case an error was thrown, cancel the request try this.executeClosePosition(key, _executionFeeReceiver) returns (bool _wasExecuted) { if (!_wasExecuted) { break; } } catch Error(string memory executionError) { emit ExecuteClosePositionError(closePositionRequests[key].account, index, executionError); // wrap this call in a try catch to prevent invalid cancels from blocking the loop try this.cancelClosePosition(key, _executionFeeReceiver) returns (bool _wasCancelled) { if (!_wasCancelled) { break; } } catch {} } catch (bytes memory /*lowLevelData*/) { // wrap this call in a try catch to prevent invalid cancels from blocking the loop try this.cancelClosePosition(key, _executionFeeReceiver) returns (bool _wasCancelled) { if (!_wasCancelled) { break; } } catch {} } delete closePositionRequestKeys[index]; index++; } closePositionRequestKeysStart = index; } function createOpenPosition( address _account, uint256 _productId, uint256 _margin, uint256 _leverage, bool _isLong, uint256 _acceptablePrice, uint256 _executionFee, bytes32 _referralCode ) external payable nonReentrant { require(_executionFee >= minExecutionFee, "PositionManager: invalid executionFee"); require(msg.sender == _account || _validateManager(_account), "PositionManager: no permission for account"); uint256 tradeFee = _getTradeFee(_margin, _leverage, _productId, _account); if (IERC20(collateralToken).isETH()) { IERC20(collateralToken).uniTransferFromSenderToThis((_executionFee + _margin + tradeFee) * tokenBase / BASE); } else { require(msg.value == _executionFee * 1e18 / BASE, "PositionManager: incorrect execution fee transferred"); IERC20(collateralToken).uniTransferFromSenderToThis((_margin + tradeFee) * tokenBase / BASE); } _setTraderReferralCode(_referralCode); _createOpenPosition( _account, _productId, _margin, tradeFee, _leverage, _isLong, _acceptablePrice, _executionFee ); } function createClosePosition( address _account, uint256 _productId, uint256 _margin, bool _isLong, uint256 _acceptablePrice, uint256 _executionFee ) external payable nonReentrant { require(_executionFee >= minExecutionFee, "PositionManager: invalid executionFee"); require(msg.value == _executionFee * 1e18 / BASE, "PositionManager: invalid msg.value"); require(msg.sender == _account || _validateManager(_account), "PositionManager: no permission for account"); _createClosePosition( _account, _productId, _margin, _isLong, _acceptablePrice, _executionFee ); } function getRequestQueueLengths() external view returns (uint256, uint256, uint256, uint256) { return ( openPositionRequestKeysStart, openPositionRequestKeys.length, closePositionRequestKeysStart, closePositionRequestKeys.length ); } function executeOpenPosition(bytes32 _key, address payable _executionFeeReceiver) public nonReentrant returns (bool) { OpenPositionRequest memory request = openPositionRequests[_key]; // if the request was already executed or cancelled, return true so that the executeOpenPositions loop will continue executing the next request if (request.account == address(0)) { return true; } (address productToken,,,,,,,,) = IPikaPerp(pikaPerp).getProduct(request.productId); uint256 oraclePrice = request.isLong ? IOracle(oracle).getPrice(productToken, true) : IOracle(oracle).getPrice(productToken, false); if (!_validateExecution(request.blockNumber, request.blockTime, request.account, true, request.isLong, request.acceptablePrice, oraclePrice)) { return false; } delete openPositionRequests[_key]; if (IERC20(collateralToken).isETH()) { IPikaPerp(pikaPerp).openPosition{value: (request.margin + request.tradeFee) * tokenBase / BASE }(request.account, request.productId, request.margin, request.isLong, request.leverage, oraclePrice); } else { IERC20(collateralToken).safeApprove(pikaPerp, 0); IERC20(collateralToken).safeApprove(pikaPerp, (request.margin + request.tradeFee) * tokenBase / BASE); IPikaPerp(pikaPerp).openPosition(request.account, request.productId, request.margin, request.isLong, request.leverage, oraclePrice); } _executionFeeReceiver.sendValue(request.executionFee * 1e18 / BASE); bytes32 referralCode; address referrer; if (referralStorage != address(0)) { (referralCode, referrer) = IReferralStorage(referralStorage).getTraderReferralInfo(request.account); } emit ExecuteOpenPosition( request.account, request.productId, request.margin, request.leverage, request.tradeFee, request.isLong, request.acceptablePrice, request.executionFee, request.index, block.timestamp.sub(request.blockTime), referralCode, referrer ); return true; } function cancelOpenPosition(bytes32 _key, address payable _executionFeeReceiver) public nonReentrant returns (bool) { OpenPositionRequest memory request = openPositionRequests[_key]; // if the request was already executed or cancelled, return true so that the executeOpenePositions loop will continue executing the next request if (request.account == address(0)) { return true; } bool shouldCancel = _validateCancellation(request.blockNumber, request.blockTime, request.account); if (!shouldCancel) { return false; } delete openPositionRequests[_key]; if (IERC20(collateralToken).isETH()) { IERC20(collateralToken).uniTransfer(request.account, (request.margin + request.tradeFee) * tokenBase / BASE); IERC20(collateralToken).uniTransfer(_executionFeeReceiver, request.executionFee * tokenBase / BASE); } else { IERC20(collateralToken).uniTransfer(request.account, (request.margin + request.tradeFee) * tokenBase / BASE); payable(_executionFeeReceiver).sendValue(request.executionFee * 1e18 / BASE); } emit CancelOpenPosition( request.account, request.productId, request.margin, request.leverage, request.tradeFee, request.isLong, request.acceptablePrice, request.executionFee, request.index, block.number.sub(request.blockNumber), block.timestamp.sub(request.blockTime) ); return true; } function executeClosePosition(bytes32 _key, address payable _executionFeeReceiver) public nonReentrant returns (bool) { ClosePositionRequest memory request = closePositionRequests[_key]; // if the request was already executed or cancelled, return true so that the executeClosePositions loop will continue executing the next request if (request.account == address(0)) { return true; } (address productToken,,,,,,,,) = IPikaPerp(pikaPerp).getProduct(request.productId); uint256 oraclePrice = !request.isLong ? IOracle(oracle).getPrice(productToken, true) : IOracle(oracle).getPrice(productToken, false); bool shouldExecute = _validateExecution(request.blockNumber, request.blockTime, request.account, false, !request.isLong, request.acceptablePrice, oraclePrice); if (!shouldExecute) { return false; } delete closePositionRequests[_key]; IPikaPerp(pikaPerp).closePosition(request.account, request.productId, request.margin , request.isLong, oraclePrice); _executionFeeReceiver.sendValue(request.executionFee * 1e18 / BASE); bytes32 referralCode; address referrer; if (referralStorage != address(0)) { (referralCode, referrer) = IReferralStorage(referralStorage).getTraderReferralInfo(request.account); } emit ExecuteClosePosition( request.account, request.productId, request.margin, request.isLong, request.acceptablePrice, request.executionFee, request.index, block.number.sub(request.blockNumber), block.timestamp.sub(request.blockTime), referralCode, referrer ); return true; } function cancelClosePosition(bytes32 _key, address payable _executionFeeReceiver) public nonReentrant returns (bool) { ClosePositionRequest memory request = closePositionRequests[_key]; // if the request was already executed or cancelled, return true so that the executeClosePositions loop will continue executing the next request if (request.account == address(0)) { return true; } bool shouldCancel = _validateCancellation(request.blockNumber, request.blockTime, request.account); if (!shouldCancel) { return false; } delete closePositionRequests[_key]; _executionFeeReceiver.sendValue(request.executionFee * 1e18 / BASE); emit CancelClosePosition( request.account, request.productId, request.margin, request.isLong, request.acceptablePrice, request.executionFee, request.index, block.number.sub(request.blockNumber), block.timestamp.sub(request.blockTime) ); return true; } function getRequestKey(address _account, uint256 _index) public pure returns (bytes32) { return keccak256(abi.encodePacked(_account, _index)); } function getOpenPositionRequest(address _account, uint256 _index) public view returns (OpenPositionRequest memory) { return openPositionRequests[getRequestKey(_account, _index)]; } function getClosePositionRequest(address _account, uint256 _index) public view returns (ClosePositionRequest memory) { return closePositionRequests[getRequestKey(_account, _index)]; } function getOpenPositionRequestFromKey(bytes32 _key) public view returns (OpenPositionRequest memory) { return openPositionRequests[_key]; } function getClosePositionRequestFromKey(bytes32 _key) public view returns (ClosePositionRequest memory) { return closePositionRequests[_key]; } function canKeeperExecute() public view returns(bool) { return (openPositionRequestKeysStart < openPositionRequestKeys.length && openPositionRequests[openPositionRequestKeys[openPositionRequestKeysStart]].blockNumber.add(minBlockDelayKeeper) <= block.number) || (closePositionRequestKeysStart < closePositionRequestKeys.length && closePositionRequests[closePositionRequestKeys[closePositionRequestKeysStart]].blockNumber.add(minBlockDelayKeeper) <= block.number); } function _validateExecution(uint256 _positionBlockNumber, uint256 _positionBlockTime, address _account, bool _isOpen, bool _isLong, uint256 _acceptablePrice, uint256 _oraclePrice ) internal view returns (bool) { if (_positionBlockTime.add(maxTimeDelay) <= block.timestamp) { revert("PositionManager: request has expired"); } bool isKeeperCall = msg.sender == address(this); if (!isUserExecuteEnabled && !isKeeperCall) { revert("PositionManager: forbidden"); } if (isKeeperCall) { if (_isLong) { require(_oraclePrice <= _acceptablePrice, "PositionManager: current price too high"); } else { require(_oraclePrice >= _acceptablePrice, "PositionManager: current price too low"); } return _positionBlockNumber.add(minBlockDelayKeeper) <= block.number; } require((!_isOpen || !allowUserCloseOnly) && msg.sender == _account, "PositionManager: forbidden"); require(_positionBlockTime.add(minTimeExecuteDelayPublic) <= block.timestamp, "PositionManager: min delay not yet passed for execution"); return true; } function _validateCancellation(uint256 _positionBlockNumber, uint256 _positionBlockTime, address _account) internal view returns (bool) { bool isKeeperCall = msg.sender == address(this); if (!isUserCancelEnabled && !isKeeperCall) { revert("PositionManager: forbidden"); } if (isKeeperCall) { return _positionBlockNumber.add(minBlockDelayKeeper) <= block.number; } require(msg.sender == _account, "PositionManager: forbidden"); require(_positionBlockTime.add(minTimeCancelDelayPublic) <= block.timestamp, "PositionManager: min delay not yet passed for cancellation"); return true; } function _createOpenPosition( address _account, uint256 _productId, uint256 _margin, uint256 _tradeFee, uint256 _leverage, bool _isLong, uint256 _acceptablePrice, uint256 _executionFee ) internal { uint256 index = openPositionsIndex[_account].add(1); openPositionsIndex[_account] = index; OpenPositionRequest memory request = OpenPositionRequest( _account, _productId, _margin, _leverage, _tradeFee, _isLong, _acceptablePrice, _executionFee, index, block.number, block.timestamp ); bytes32 key = getRequestKey(_account, index); openPositionRequests[key] = request; openPositionRequestKeys.push(key); emit CreateOpenPosition( _account, _productId, _margin, _leverage, _tradeFee, _isLong, _acceptablePrice, _executionFee, index, block.number, block.timestamp, tx.gasprice ); } function _createClosePosition( address _account, uint256 _productId, uint256 _margin, bool _isLong, uint256 _acceptablePrice, uint256 _executionFee ) internal { uint256 index = closePositionsIndex[_account].add(1); closePositionsIndex[_account] = index; ClosePositionRequest memory request = ClosePositionRequest( _account, _productId, _margin, _isLong, _acceptablePrice, _executionFee, index, block.number, block.timestamp ); bytes32 key = getRequestKey(_account, index); closePositionRequests[key] = request; closePositionRequestKeys.push(key); emit CreateClosePosition( _account, _productId, _margin, _isLong, _acceptablePrice, _executionFee, index, block.number, block.timestamp ); } function _validateManager(address _account) private view returns(bool) { return managers[msg.sender] && approvedManagers[_account][msg.sender]; } function _setTraderReferralCode(bytes32 _referralCode) internal { if (_referralCode != bytes32(0) && referralStorage != address(0)) { IReferralStorage(referralStorage).setTraderReferralCode(msg.sender, _referralCode); } } function _getTradeFee(uint256 margin, uint256 leverage, uint256 _productId, address _account) private returns(uint256) { (address productToken,,uint256 fee,,,,,,) = IPikaPerp(pikaPerp).getProduct(_productId); return IFeeCalculator(feeCalculator).getFee(margin, leverage, productToken, fee, _account, msg.sender); } fallback() external payable {} receive() external payable {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol) pragma solidity ^0.8.0; // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler * now has built in overflow checking. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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); /** * @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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.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; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } 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)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } 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"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @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"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IPikaPerp { function getTotalShare() external view returns(uint256); function getShare(address stakeOwner) external view returns(uint256); function distributeProtocolReward() external returns(uint256); function distributePikaReward() external returns(uint256); function distributeVaultReward() external returns(uint256); function getPendingPikaReward() external view returns(uint256); function getPendingProtocolReward() external view returns(uint256); function getPendingVaultReward() external view returns(uint256); function stake(uint256 amount, address user) external payable; function redeem(uint256 shares) external; function openPosition( address user, uint256 productId, uint256 margin, bool isLong, uint256 leverage, uint256 oraclePrice ) external payable; function closePositionWithId( uint256 positionId, uint256 margin, uint256 oraclePrice ) external; function closePosition( address user, uint256 productId, uint256 margin, bool isLong, uint256 oraclePrice ) external; function liquidatePositions(uint256[] calldata positionIds) external; function getProduct(uint256 productId) external view returns ( address,uint256,uint256,bool,uint256,uint256,uint256,uint256,uint256); function getPosition( address account, uint256 productId, bool isLong ) external view returns (uint256,uint256,uint256,uint256,uint256,address,uint256,bool,int256); function getMaxExposure(uint256 productWeight) external view returns(uint256); function getCumulativeFunding(uint256 _productId) external view returns(uint256); function liquidationThreshold() external view returns(uint256); }
// SPDX-License-Identifier: MIT // Originally: https://github.com/CryptoManiacsZone/mooniswap/blob/master/contracts/libraries/UniERC20.sol pragma solidity ^0.8.0; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; library UniERC20 { using SafeMath for uint256; using SafeERC20 for IERC20; function isETH(IERC20 token) internal pure returns (bool) { return (address(token) == address(0)); } function uniBalanceOf(IERC20 token, address account) internal view returns (uint256) { if (isETH(token)) { return account.balance; } else { return token.balanceOf(account); } } function uniTransfer( IERC20 token, address to, uint256 amount ) internal { if (amount > 0) { if (isETH(token)) { (bool success, ) = payable(to).call{value: amount}(""); require(success, "Transfer failed"); } else { token.safeTransfer(to, amount); } } } function uniTransferFromSenderToThis(IERC20 token, uint256 amount) internal { if (amount > 0) { if (isETH(token)) { require(msg.value >= amount, "UniERC20: not enough value"); if (msg.value > amount) { // Return remainder if exist uint256 refundAmount = msg.value.sub(amount); (bool success, ) = msg.sender.call{value: refundAmount}(""); require(success, "Transfer failed"); } } else { token.safeTransferFrom(msg.sender, address(this), amount); } } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import '../oracle/IOracle.sol'; import '../lib/UniERC20.sol'; import '../lib/PerpLib.sol'; import './IPikaPerp.sol'; import './IFundingManager.sol'; import '../staking/IVaultReward.sol'; contract PikaPerpV4 is ReentrancyGuard { using SafeERC20 for IERC20; using UniERC20 for IERC20; // All amounts are stored with 8 decimals // Structs struct Vault { // 32 bytes uint128 cap; // Maximum capacity. 16 bytes uint128 balance; // 16 bytes // 32 bytes uint96 staked; // Total staked by users. 12 bytes uint96 shares; // Total ownership shares. 12 bytes uint64 stakingPeriod; // Time required to lock stake (seconds). 8 bytes } struct Stake { // 32 bytes address owner; // 20 bytes uint96 amount; // 12 bytes // 32 bytes uint128 shares; // 16 bytes uint128 timestamp; // 16 bytes } struct Product { // 32 bytes address productToken; uint72 maxLeverage; uint16 fee; // In bps. 0.5% = 50. bool isActive; // 32 bytes uint64 openInterestLong; uint64 openInterestShort; uint32 minPriceChange; // 1.5%, the minimum oracle price up change for trader to close trade with profit uint32 weight; // share of the max exposure uint64 reserve; // Virtual reserve used to calculate slippage } struct Position { // 32 bytes uint64 productId; uint64 leverage; uint64 price; uint64 oraclePrice; // 32 bytes uint128 margin; int128 funding; // 32 bytes address owner; bool isLong; bool isNextPrice; uint80 timestamp; } // Variables address public owner; address public guardian; address public gov; address private token; address public oracle; address public protocolRewardDistributor; address public pikaRewardDistributor; address public vaultRewardDistributor; address public vaultTokenReward; address public feeCalculator; address public fundingManager; uint256 private tokenBase; uint256 public minMargin; uint256 public protocolRewardRatio = 2000; // 20% uint256 public pikaRewardRatio = 3000; // 30% uint256 public maxShift = 0.003e8; // max shift (shift is used adjust the price to balance the longs and shorts) uint256 public minProfitTime = 12 hours; // the time window where minProfit is effective uint256 public totalWeight; // total exposure weights of all product uint256 public exposureMultiplier = 10000; // exposure multiplier uint256 public utilizationMultiplier = 10000; // exposure multiplier uint256 public maxExposureMultiplier = 3; // total open interest of a product should not exceed maxExposureMultiplier * maxExposure uint256 private liquidationBounty = 5000; // In bps. 5000 = 50% uint256 public liquidationThreshold = 8000; // In bps. 8000 = 80% uint256 private pendingProtocolReward; // protocol reward collected uint256 private pendingPikaReward; // pika reward collected uint256 private pendingVaultReward; // vault reward collected uint256 public totalOpenInterest; uint256 public shiftDivider = 3; bool private canUserStake = true; bool private allowPublicLiquidator = false; bool private isTradeEnabled = true; Vault private vault; uint256 private constant BASE = 10**8; mapping(uint256 => Product) private products; mapping(address => Stake) private stakes; mapping(uint256 => Position) private positions; mapping (address => bool) public liquidators; mapping (address => bool) public nextPriceManagers; mapping (address => bool) public managers; mapping (address => mapping (address => bool)) public approvedManagers; // Events event Staked( address indexed user, uint256 amount, uint256 shares ); event Redeemed( address indexed user, address indexed receiver, uint256 amount, uint256 shares, uint256 shareBalance, bool isFullRedeem ); event NewPosition( uint256 indexed positionId, address indexed user, uint256 indexed productId, bool isLong, uint256 price, uint256 oraclePrice, uint256 margin, uint256 leverage, uint256 fee, bool isNextPrice, int256 funding ); event ModifyMargin( uint256 indexed positionId, address indexed sender, address indexed user, uint256 margin, uint256 newMargin, uint256 newLeverage, bool shouldIncrease ); event ClosePosition( uint256 indexed positionId, address indexed user, uint256 indexed productId, uint256 price, uint256 entryPrice, uint256 margin, uint256 leverage, uint256 fee, int256 pnl, int256 fundingPayment, bool wasLiquidated ); event PositionLiquidated( uint256 indexed positionId, address indexed liquidator, uint256 liquidatorReward, uint256 remainingReward ); event ProtocolRewardDistributed( address to, uint256 amount ); event PikaRewardDistributed( address to, uint256 amount ); event VaultRewardDistributed( address to, uint256 amount ); event VaultUpdated( Vault vault ); event ProductAdded( uint256 productId, Product product ); event ProductUpdated( uint256 productId, Product product ); event AddressesSet( address oracle, address feeCalculator, address fundingManager ); event OwnerUpdated( address newOwner ); event GuardianUpdated( address newGuardian ); event GovUpdated( address newGov ); // Constructor constructor(address _token, uint256 _tokenBase, address _oracle, address _feeCalculator, address _fundingManager) { owner = msg.sender; guardian = msg.sender; gov = msg.sender; token = _token; tokenBase = _tokenBase; oracle = _oracle; feeCalculator = _feeCalculator; fundingManager = _fundingManager; } // Methods function stake(uint256 amount, address user) external payable nonReentrant { require((canUserStake || msg.sender == owner) && (msg.sender == user || _validateManager(user)), "!stake"); IVaultReward(vaultRewardDistributor).updateReward(user); IVaultReward(vaultTokenReward).updateReward(user); IERC20(token).uniTransferFromSenderToThis(amount * tokenBase / BASE); require(uint256(vault.staked) + amount <= uint256(vault.cap), "!cap"); uint256 shares = vault.staked > 0 ? amount * uint256(vault.shares) / uint256(vault.balance) : amount; vault.balance += uint128(amount); vault.staked += uint96(amount); vault.shares += uint96(shares); if (stakes[user].amount == 0) { stakes[user] = Stake({ owner: user, amount: uint96(amount), shares: uint128(shares), timestamp: uint128(block.timestamp) }); } else { stakes[user].amount += uint96(amount); stakes[user].shares += uint128(shares); if (!_validateManager(user)) { stakes[user].timestamp = uint128(block.timestamp); } } emit Staked( user, amount, shares ); } function redeem( address user, uint256 shares, address receiver ) external { require(shares <= uint256(vault.shares) && (user == msg.sender || _validateManager(user)), "!redeem"); IVaultReward(vaultRewardDistributor).updateReward(user); IVaultReward(vaultTokenReward).updateReward(user); Stake storage _stake = stakes[user]; bool isFullRedeem = shares >= uint256(_stake.shares); if (isFullRedeem) { shares = uint256(_stake.shares); } uint256 timeDiff = block.timestamp - uint256(_stake.timestamp); require(timeDiff > uint256(vault.stakingPeriod), "!period"); uint256 shareBalance = shares * uint256(vault.balance) / uint256(vault.shares); uint256 amount = shares * _stake.amount / uint256(_stake.shares); _stake.amount -= uint96(amount); _stake.shares -= uint128(shares); vault.staked -= uint96(amount); vault.shares -= uint96(shares); vault.balance -= uint128(shareBalance); require(totalOpenInterest <= uint256(vault.balance) * utilizationMultiplier / (10**4), "!utilized"); if (isFullRedeem) { delete stakes[user]; } IERC20(token).uniTransfer(receiver, shareBalance * tokenBase / BASE); emit Redeemed( user, receiver, amount, shares, shareBalance, isFullRedeem ); } function openPosition( address user, uint256 productId, uint256 margin, bool isLong, uint256 leverage, uint256 oraclePrice ) public payable nonReentrant { require(_validateManager(user), "!allowed"); require(isTradeEnabled, "!enabled"); // Check params require(margin >= minMargin && margin < type(uint64).max, "!margin"); // Check product Product storage product = products[productId]; require(product.isActive, "!active"); require(leverage >= BASE / 2 && leverage <= uint256(product.maxLeverage), "!lev"); // Transfer margin plus fee uint256 tradeFee = IFeeCalculator(feeCalculator).getFee(margin, leverage, product.productToken, uint256(product.fee), user, msg.sender); IERC20(token).uniTransferFromSenderToThis((margin + tradeFee) * tokenBase / BASE); _updatePendingRewards(tradeFee); uint256 price = _calculatePrice(isLong, product.openInterestLong, product.openInterestShort, uint256(vault.balance) * uint256(product.weight) * exposureMultiplier / uint256(totalWeight) / (10**4), uint256(product.reserve), margin * leverage / BASE, oraclePrice); _updateFundingAndOpenInterest(productId, margin * leverage / BASE, isLong, true); int256 funding = IFundingManager(fundingManager).getFunding(productId); Position storage position = positions[getPositionId(user, productId, isLong)]; if (position.margin > 0) { price = (uint256(position.margin) * position.leverage + margin * leverage) * BASE / (uint256(position.margin) * position.leverage / uint256(position.price) + margin * leverage / price) / BASE; funding = (int256(uint256(position.margin)) * int256(uint256(position.leverage)) * int256(position.funding) + int256(margin * leverage) * funding) / (int256(uint256(position.margin)) * int256(uint256(position.leverage)) + int256(margin * leverage)); leverage = (uint256(position.margin) * uint256(position.leverage) + margin * leverage) / (uint256(position.margin) + margin); margin = uint256(position.margin) + margin; } positions[getPositionId(user, productId, isLong)] = Position({ owner: user, productId: uint64(productId), margin: uint128(margin), leverage: uint64(leverage), price: uint64(price), oraclePrice: uint64(oraclePrice), timestamp: uint80(block.timestamp), isLong: isLong, // if no existing position, isNextPrice depends on if sender is a nextPriceManager, // else it is false if either existing position's isNextPrice is false or the current new position sender is not a nextPriceManager isNextPrice: position.margin == 0 ? nextPriceManagers[msg.sender] : (!position.isNextPrice ? false : nextPriceManagers[msg.sender]), funding: int128(funding) }); emit NewPosition( getPositionId(user, productId, isLong), user, productId, isLong, price, oraclePrice, margin, leverage, tradeFee, position.margin == 0 ? nextPriceManagers[msg.sender] : (!position.isNextPrice ? false : nextPriceManagers[msg.sender]), funding ); } // Add margin to Position with positionId function modifyMargin(uint256 positionId, uint256 margin, bool shouldIncrease) external payable nonReentrant { // Check position Position storage position = positions[positionId]; require(msg.sender == position.owner || _validateManager(position.owner), "!allow"); Product storage product = products[uint256(position.productId)]; uint256 newMargin; if (shouldIncrease) { IERC20(token).uniTransferFromSenderToThis(margin * tokenBase / BASE); newMargin = uint256(position.margin) + margin; } else { int256 fundingPayment = PerpLib._getFundingPayment(fundingManager, position.isLong, position.productId, position.leverage, position.margin, position.funding); int256 pnl = PerpLib._getPnl(position.isLong, position.price, position.leverage, position.margin, IOracle(oracle).getPrice(product.productToken)) - fundingPayment; require (pnl > 0 || uint256(-1 * pnl) < uint256(position.margin) * liquidationThreshold / (10**4), "liquidatable"); newMargin = uint256(position.margin) - margin; require(newMargin >= minMargin, "!margin"); IERC20(token).uniTransfer(msg.sender, margin * tokenBase / BASE); } // New position params uint256 newLeverage = uint256(position.leverage) * uint256(position.margin) / newMargin; require(newLeverage >= BASE / 2 && newLeverage <= uint256(product.maxLeverage), "!lev"); position.margin = uint128(newMargin); position.leverage = uint64(newLeverage); emit ModifyMargin( positionId, msg.sender, position.owner, margin, newMargin, newLeverage, shouldIncrease ); } function closePosition( address user, uint256 productId, uint256 margin, bool isLong, uint256 oraclePrice ) external { return closePositionWithId(getPositionId(user, productId, isLong), margin, oraclePrice); } // Closes position from Position with id = positionId function closePositionWithId( uint256 positionId, uint256 margin, uint256 oraclePrice ) public nonReentrant { // Check position Position storage position = positions[positionId]; require(_validateManager(position.owner), "!close"); // Check product Product storage product = products[uint256(position.productId)]; bool isFullClose; if (margin >= uint256(position.margin)) { margin = uint256(position.margin); isFullClose = true; } uint256 price = _calculatePrice(!position.isLong, product.openInterestLong, product.openInterestShort, getMaxExposure(uint256(product.weight)), uint256(product.reserve), margin * position.leverage / BASE, oraclePrice); _updateFundingAndOpenInterest(uint256(position.productId), margin * uint256(position.leverage) / BASE, position.isLong, false); int256 fundingPayment = PerpLib._getFundingPayment(fundingManager, position.isLong, position.productId, position.leverage, margin, position.funding); int256 pnl = PerpLib._getPnl(position.isLong, uint256(position.price), uint256(position.leverage), margin, price) - fundingPayment; bool isLiquidatable; if (pnl < 0 && uint256(-1 * pnl) >= margin * liquidationThreshold / (10**4)) { margin = uint256(position.margin); pnl = -1 * int256(uint256(position.margin)); isLiquidatable = true; } else { // front running protection: if oracle price up change is smaller than threshold and minProfitTime has not passed // and either open or close order is not using next oracle price, the pnl is be set to 0 if (pnl > 0 && !PerpLib._canTakeProfit(position.isLong, uint256(position.timestamp), uint256(position.oraclePrice), IOracle(oracle).getPrice(product.productToken), product.minPriceChange, minProfitTime) && (!position.isNextPrice || !nextPriceManagers[msg.sender])) { pnl = 0; } } uint256 totalFee = _updateVaultAndGetFee(pnl, position, margin, uint256(product.fee), product.productToken); emit ClosePosition( positionId, position.owner, uint256(position.productId), price, uint256(position.price), margin, uint256(position.leverage), totalFee, pnl, fundingPayment, isLiquidatable ); if (isFullClose) { delete positions[positionId]; } else { position.margin -= uint128(margin); } } function _updateVaultAndGetFee( int256 pnl, Position memory position, uint256 margin, uint256 fee, address productToken ) private returns(uint256) { uint256 totalFee = IFeeCalculator(feeCalculator).getFee(margin, uint256(position.leverage), productToken, fee, position.owner, msg.sender); int256 pnlAfterFee = pnl - int256(totalFee); // Update vault if (pnlAfterFee < 0) { uint256 _pnlAfterFee = uint256(-1 * pnlAfterFee); if (_pnlAfterFee < margin) { IERC20(token).uniTransfer(position.owner, (margin - _pnlAfterFee) * tokenBase / BASE); vault.balance += uint128(_pnlAfterFee); } else { vault.balance += uint128(margin); return totalFee; } } else { uint256 _pnlAfterFee = uint256(pnlAfterFee); // Check vault require(uint256(vault.balance) >= _pnlAfterFee, "!bal"); vault.balance -= uint128(_pnlAfterFee); IERC20(token).uniTransfer(position.owner, (margin + _pnlAfterFee) * tokenBase / BASE); } _updatePendingRewards(totalFee); vault.balance -= uint128(totalFee); return totalFee; } // Liquidate positionIds function liquidatePositions(uint256[] calldata positionIds) external { require(liquidators[msg.sender] || allowPublicLiquidator, "!liquidator"); uint256 totalLiquidatorReward; for (uint256 i = 0; i < positionIds.length; i++) { uint256 positionId = positionIds[i]; uint256 liquidatorReward = liquidatePosition(positionId); totalLiquidatorReward = totalLiquidatorReward + liquidatorReward; } if (totalLiquidatorReward > 0) { IERC20(token).uniTransfer(msg.sender, totalLiquidatorReward * tokenBase / BASE); } } function liquidatePosition( uint256 positionId ) private returns(uint256 liquidatorReward) { Position storage position = positions[positionId]; if (position.productId == 0) { return 0; } Product storage product = products[uint256(position.productId)]; uint256 price = IOracle(oracle).getPrice(product.productToken); // use oracle price for liquidation uint256 remainingReward; _updateFundingAndOpenInterest(uint256(position.productId), uint256(position.margin) * uint256(position.leverage) / BASE, position.isLong, false); int256 fundingPayment = PerpLib._getFundingPayment(fundingManager, position.isLong, position.productId, position.leverage, position.margin, position.funding); int256 pnl = PerpLib._getPnl(position.isLong, position.price, position.leverage, position.margin, price) - fundingPayment; require (pnl < 0 && uint256(-1 * pnl) >= uint256(position.margin) * liquidationThreshold / (10**4)); if (uint256(position.margin) > uint256(-1*pnl)) { uint256 _pnl = uint256(-1*pnl); liquidatorReward = (uint256(position.margin) - _pnl) * liquidationBounty / (10**4); remainingReward = uint256(position.margin) - _pnl - liquidatorReward; _updatePendingRewards(remainingReward); vault.balance += uint128(_pnl); } else { vault.balance += uint128(position.margin); } emit ClosePosition( positionId, position.owner, uint256(position.productId), price, uint256(position.price), uint256(position.margin), uint256(position.leverage), 0, -1*int256(uint256(position.margin)), fundingPayment, true ); delete positions[positionId]; emit PositionLiquidated( positionId, msg.sender, liquidatorReward, remainingReward ); return liquidatorReward; } function _updatePendingRewards(uint256 reward) private { pendingProtocolReward = pendingProtocolReward + (reward * protocolRewardRatio / (10**4)); pendingPikaReward = pendingPikaReward + (reward * pikaRewardRatio / (10**4)); pendingVaultReward = pendingVaultReward + (reward * (10**4 - protocolRewardRatio - pikaRewardRatio) / (10**4)); } function _updateFundingAndOpenInterest(uint256 productId, uint256 amount, bool isLong, bool isIncrease) private { IFundingManager(fundingManager).updateFunding(productId); Product storage product = products[productId]; if (isIncrease) { totalOpenInterest = totalOpenInterest + amount; uint256 maxExposure = getMaxExposure(uint256(product.weight)); require(totalOpenInterest <= uint256(vault.balance) * utilizationMultiplier / 10**4 && uint256(product.openInterestLong) + uint256(product.openInterestShort) + amount < maxExposureMultiplier * maxExposure, "!maxOI"); if (isLong) { product.openInterestLong = product.openInterestLong + uint64(amount); require(uint256(product.openInterestLong) <= uint256(maxExposure) + uint256(product.openInterestShort), "!exposure-long"); } else { product.openInterestShort = product.openInterestShort + uint64(amount); require(uint256(product.openInterestShort) <= uint256(maxExposure) + uint256(product.openInterestLong), "!exposure-short"); } } else { totalOpenInterest = totalOpenInterest - amount; if (isLong) { if (uint256(product.openInterestLong) >= amount) { product.openInterestLong -= uint64(amount); } else { product.openInterestLong = 0; } } else { if (uint256(product.openInterestShort) >= amount) { product.openInterestShort -= uint64(amount); } else { product.openInterestShort = 0; } } } } function _validateManager(address account) private view returns(bool) { return managers[msg.sender] && approvedManagers[account][msg.sender]; } function distributeProtocolReward() external returns(uint256) { require(msg.sender == protocolRewardDistributor, "!dist"); uint256 _pendingProtocolReward = pendingProtocolReward * tokenBase / BASE; if (pendingProtocolReward > 0) { pendingProtocolReward = 0; IERC20(token).uniTransfer(protocolRewardDistributor, _pendingProtocolReward); emit ProtocolRewardDistributed(protocolRewardDistributor, _pendingProtocolReward); } return _pendingProtocolReward; } function distributePikaReward() external returns(uint256) { require(msg.sender == pikaRewardDistributor, "!dist"); uint256 _pendingPikaReward = pendingPikaReward * tokenBase / BASE; if (pendingPikaReward > 0) { pendingPikaReward = 0; IERC20(token).uniTransfer(pikaRewardDistributor, _pendingPikaReward); emit PikaRewardDistributed(pikaRewardDistributor, _pendingPikaReward); } return _pendingPikaReward; } function distributeVaultReward() external returns(uint256) { require(msg.sender == vaultRewardDistributor, "!dist"); uint256 _pendingVaultReward = pendingVaultReward * tokenBase / BASE; if (pendingVaultReward > 0) { pendingVaultReward = 0; IERC20(token).uniTransfer(vaultRewardDistributor, _pendingVaultReward); emit VaultRewardDistributed(vaultRewardDistributor, _pendingVaultReward); } return _pendingVaultReward; } // Getters function getPendingPikaReward() external view returns(uint256) { return pendingPikaReward * tokenBase / BASE; } function getPendingProtocolReward() external view returns(uint256) { return pendingProtocolReward * tokenBase / BASE; } function getPendingVaultReward() external view returns(uint256) { return pendingVaultReward * tokenBase / BASE; } function getVault() external view returns(Vault memory) { return vault; } function getProduct(uint256 productId) external view returns ( address,uint256,uint256,bool,uint256,uint256,uint256,uint256,uint256 ) { Product memory product = products[productId]; return ( product.productToken, uint256(product.maxLeverage), uint256(product.fee), product.isActive, uint256(product.openInterestLong), uint256(product.openInterestShort), uint256(product.minPriceChange), uint256(product.weight), uint256(product.reserve)); } function getPositionId( address account, uint256 productId, bool isLong ) public pure returns (uint256) { return uint256(keccak256(abi.encodePacked(account, productId, isLong))); } function getPosition( address account, uint256 productId, bool isLong ) external view returns ( uint256,uint256,uint256,uint256,uint256,address,uint256,bool,int256 ) { Position memory position = positions[getPositionId(account, productId, isLong)]; return( uint256(position.productId), uint256(position.leverage), uint256(position.price), uint256(position.oraclePrice), uint256(position.margin), position.owner, uint256(position.timestamp), position.isLong, position.funding); } function getPositions(uint256[] calldata positionIds) external view returns(Position[] memory _positions) { uint256 length = positionIds.length; _positions = new Position[](length); for (uint256 i = 0; i < length; i++) { _positions[i] = positions[positionIds[i]]; } } function getMaxExposure(uint256 productWeight) public view returns(uint256) { return uint256(vault.balance) * productWeight * exposureMultiplier / uint256(totalWeight) / (10**4); } function getTotalShare() external view returns(uint256) { return uint256(vault.shares); } function getShare(address stakeOwner) external view returns(uint256) { return uint256(stakes[stakeOwner].shares); } function getStake(address stakeOwner) external view returns(Stake memory) { return stakes[stakeOwner]; } // Private methods function _calculatePrice( bool isLong, uint256 openInterestLong, uint256 openInterestShort, uint256 maxExposure, uint256 reserve, uint256 amount, uint256 oraclePrice ) private view returns(uint256) { int256 shift = (int256(openInterestLong) - int256(openInterestShort)) * int256(maxShift) / int256(maxExposure); if (isLong) { uint256 slippage = (reserve * reserve / (reserve - amount) - reserve) * BASE / amount; slippage = shift >= 0 ? slippage + uint256(shift) : slippage - (uint256(-1 * shift) / shiftDivider); return oraclePrice * slippage / BASE; } else { uint256 slippage = (reserve - (reserve * reserve) / (reserve + amount)) * BASE / amount; slippage = shift >= 0 ? slippage + (uint256(shift) / shiftDivider) : slippage - uint256(-1 * shift); return oraclePrice * slippage / BASE; } } // Owner methods function updateVault(Vault memory _vault) external { onlyOwner(); require(_vault.cap > 0 && _vault.stakingPeriod > 0 && _vault.stakingPeriod < 30 days, "!allowed"); vault.cap = _vault.cap; vault.stakingPeriod = _vault.stakingPeriod; emit VaultUpdated(vault); } function addProduct(uint256 productId, Product memory _product) external { onlyOwner(); require(productId > 0); Product memory product = products[productId]; require(product.maxLeverage == 0 && _product.maxLeverage > 1 * BASE && _product.productToken != address(0)); products[productId] = Product({ productToken: _product.productToken, maxLeverage: _product.maxLeverage, fee: _product.fee, isActive: true, openInterestLong: 0, openInterestShort: 0, minPriceChange: _product.minPriceChange, weight: _product.weight, reserve: _product.reserve }); totalWeight = totalWeight + _product.weight; emit ProductAdded(productId, products[productId]); } function updateProduct(uint256 productId, Product memory _product) external { onlyOwner(); require(productId > 0); Product storage product = products[productId]; require(product.maxLeverage > 0 && _product.maxLeverage >= 1 * BASE && _product.productToken != address(0)); product.productToken = _product.productToken; product.maxLeverage = _product.maxLeverage; product.fee = _product.fee; product.isActive = _product.isActive; product.minPriceChange = _product.minPriceChange; totalWeight = totalWeight - product.weight + _product.weight; product.weight = _product.weight; product.reserve = _product.reserve; emit ProductUpdated(productId, product); } function setDistributors( address _protocolRewardDistributor, address _pikaRewardDistributor, address _vaultRewardDistributor, address _vaultTokenReward ) external { onlyOwner(); protocolRewardDistributor = _protocolRewardDistributor; pikaRewardDistributor = _pikaRewardDistributor; vaultRewardDistributor = _vaultRewardDistributor; vaultTokenReward = _vaultTokenReward; } function setManager(address _manager, bool _isActive) external { onlyOwner(); managers[_manager] = _isActive; } function setAccountManager(address _manager, bool _isActive) external { approvedManagers[msg.sender][_manager] = _isActive; } function setRewardRatio(uint256 _protocolRewardRatio, uint256 _pikaRewardRatio) external { onlyOwner(); require(_protocolRewardRatio + _pikaRewardRatio <= 10000); protocolRewardRatio = _protocolRewardRatio; pikaRewardRatio = _pikaRewardRatio; } function setMinMargin(uint256 _minMargin) external { onlyOwner(); minMargin = _minMargin; } function setTradeEnabled(bool _isTradeEnabled) external { require(msg.sender == owner || managers[msg.sender]); isTradeEnabled = _isTradeEnabled; } function setParameters( uint256 _maxShift, uint256 _minProfitTime, bool _canUserStake, bool _allowPublicLiquidator, uint256 _exposureMultiplier, uint256 _utilizationMultiplier, uint256 _maxExposureMultiplier, uint256 _liquidationBounty, uint256 _liquidationThreshold, uint256 _shiftDivider ) external { onlyOwner(); require(_maxShift <= 0.01e8 && _minProfitTime <= 24 hours && _shiftDivider > 0 && _liquidationThreshold > 5000 && _maxExposureMultiplier > 0); maxShift = _maxShift; minProfitTime = _minProfitTime; canUserStake = _canUserStake; allowPublicLiquidator = _allowPublicLiquidator; exposureMultiplier = _exposureMultiplier; utilizationMultiplier = _utilizationMultiplier; maxExposureMultiplier = _maxExposureMultiplier; liquidationBounty = _liquidationBounty; liquidationThreshold = _liquidationThreshold; shiftDivider = _shiftDivider; } function setAddresses(address _oracle, address _feeCalculator, address _fundingManager) external { onlyOwner(); oracle = _oracle; feeCalculator = _feeCalculator; fundingManager = _fundingManager; emit AddressesSet(_oracle, _feeCalculator, _fundingManager); } function setLiquidator(address _liquidator, bool _isActive) external { onlyOwner(); liquidators[_liquidator] = _isActive; } function setNextPriceManager(address _nextPriceManager, bool _isActive) external { onlyOwner(); nextPriceManagers[_nextPriceManager] = _isActive; } function setOwner(address _owner) external { onlyGov(); owner = _owner; emit OwnerUpdated(_owner); } function setGuardian(address _guardian) external { onlyGov(); guardian = _guardian; emit GuardianUpdated(_guardian); } function setGov(address _gov) external { onlyGov(); gov = _gov; emit GovUpdated(_gov); } function pauseTrading() external { require(msg.sender == guardian); isTradeEnabled = false; canUserStake = false; } function onlyOwner() private { require(msg.sender == owner, "!owner"); } function onlyGov() private { require(msg.sender == gov, "!gov"); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Governable { address public gov; constructor() public { gov = msg.sender; } modifier onlyGov() { require(msg.sender == gov, "Governable: forbidden"); _; } function setGov(address _gov) external onlyGov { gov = _gov; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; 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: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
pragma solidity ^0.8.0; interface IOracle { function getPrice(address feed) external view returns (uint256); function getPrice(address token, bool isMax) external view returns (uint256); function getLastNPrices(address token, uint256 n) external view returns(uint256[] memory); function setPrices(address[] memory tokens, uint256[] memory prices) external; function setPrices(address sender, bytes[] calldata priceUpdateData) external payable; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../oracle/IOracle.sol"; import '../perp/IFeeCalculator.sol'; import '../perp/IFundingManager.sol'; library PerpLib { uint256 private constant BASE = 10**8; uint256 private constant FUNDING_BASE = 10**12; function _canTakeProfit( bool isLong, uint256 positionTimestamp, uint256 positionOraclePrice, uint256 oraclePrice, uint256 minPriceChange, uint256 minProfitTime ) internal view returns(bool) { if (block.timestamp > positionTimestamp + minProfitTime) { return true; } else if (isLong && oraclePrice > positionOraclePrice * (10**4 + minPriceChange) / (10**4)) { return true; } else if (!isLong && oraclePrice < positionOraclePrice * (10**4 - minPriceChange) / (10**4)) { return true; } return false; } function _getPnl( bool isLong, uint256 positionPrice, uint256 positionLeverage, uint256 margin, uint256 price ) internal view returns(int256 _pnl) { bool pnlIsNegative; uint256 pnl; if (isLong) { if (price >= positionPrice) { pnl = margin * positionLeverage * (price - positionPrice) / positionPrice / BASE; } else { pnl = margin * positionLeverage * (positionPrice - price) / positionPrice / BASE; pnlIsNegative = true; } } else { if (price > positionPrice) { pnl = margin * positionLeverage * (price - positionPrice) / positionPrice / BASE; pnlIsNegative = true; } else { pnl = margin * positionLeverage * (positionPrice - price) / positionPrice / BASE; } } if (pnlIsNegative) { _pnl = -1 * int256(pnl); } else { _pnl = int256(pnl); } return _pnl; } function _getFundingPayment( address fundingManager, bool isLong, uint256 productId, uint256 positionLeverage, uint256 margin, int256 funding ) internal view returns(int256) { return isLong ? int256(margin * positionLeverage) * (IFundingManager(fundingManager).getFunding(productId) - funding) / int256(BASE * FUNDING_BASE) : int256(margin * positionLeverage) * (funding - IFundingManager(fundingManager).getFunding(productId)) / int256(BASE * FUNDING_BASE); } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFundingManager { function updateFunding(uint256) external; function getFunding(uint256) external view returns(int256); function getFundingRate(uint256) external view returns(int256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IVaultReward { function updateReward(address account) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFeeCalculator { function getFee(uint256 margin, uint256 leverage, address token, uint256 productFee, address account, address sender) external view returns (uint256); function getFeeRate(address token, uint256 productFee, address account, address sender) external view returns (uint256); }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_pikaPerp","type":"address"},{"internalType":"address","name":"_feeCalculator","type":"address"},{"internalType":"address","name":"_oracle","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"uint256","name":"_minExecutionFee","type":"uint256"},{"internalType":"uint256","name":"_tokenBase","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"productId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"margin","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockGap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeGap","type":"uint256"}],"name":"CancelClosePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"productId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"margin","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"leverage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tradeFee","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockGap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeGap","type":"uint256"}],"name":"CancelOpenPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"productId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"margin","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockNumber","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockTime","type":"uint256"}],"name":"CreateClosePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"productId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"margin","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"leverage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tradeFee","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockNumber","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasPrice","type":"uint256"}],"name":"CreateOpenPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"productId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"margin","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockGap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeGap","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"referralCode","type":"bytes32"},{"indexed":false,"internalType":"address","name":"referral","type":"address"}],"name":"ExecuteClosePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"string","name":"executionError","type":"string"}],"name":"ExecuteClosePositionError","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"productId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"margin","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"leverage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tradeFee","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeGap","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"referralCode","type":"bytes32"},{"indexed":false,"internalType":"address","name":"referral","type":"address"}],"name":"ExecuteOpenPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"string","name":"executionError","type":"string"}],"name":"ExecuteOpenPositionError","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"manager","type":"address"},{"indexed":false,"internalType":"bool","name":"isActive","type":"bool"}],"name":"SetAccountManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"SetAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"allowPublicKeeper","type":"bool"}],"name":"SetAllowPublicKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"allowUserCloseOnly","type":"bool"}],"name":"SetAllowUserCloseOnly","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minBlockDelayKeeper","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minTimeExecuteDelayPublic","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minTimeCancelDelayPublic","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxTimeDelay","type":"uint256"}],"name":"SetDelayValues","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isUserCancelEnabled","type":"bool"}],"name":"SetIsUserCancelEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isUserExecuteEnabled","type":"bool"}],"name":"SetIsUserExecuteEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"manager","type":"address"},{"indexed":false,"internalType":"bool","name":"isActive","type":"bool"}],"name":"SetManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minExecutionFee","type":"uint256"}],"name":"SetMinExecutionFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"isActive","type":"bool"}],"name":"SetPositionKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"referralStorage","type":"address"}],"name":"SetReferralStorage","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"increasePositionRequestKeysStart","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"decreasePositionRequestKeysStart","type":"uint256"}],"name":"SetRequestKeysStartValues","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowPublicKeeper","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowUserCloseOnly","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"approvedManagers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canKeeperExecute","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"cancelClosePosition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"cancelOpenPosition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"closePositionRequestKeys","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"closePositionRequestKeysStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"closePositionRequests","outputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"productId","type":"uint256"},{"internalType":"uint256","name":"margin","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"blockTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"closePositionsIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"uint256","name":"_margin","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"_executionFee","type":"uint256"}],"name":"createClosePosition","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"uint256","name":"_margin","type":"uint256"},{"internalType":"uint256","name":"_leverage","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"_executionFee","type":"uint256"},{"internalType":"bytes32","name":"_referralCode","type":"bytes32"}],"name":"createOpenPosition","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"executeClosePosition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"_priceUpdateData","type":"bytes[]"},{"internalType":"uint256","name":"n","type":"uint256"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"executeNPositionsWithPrices","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"executeOpenPosition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"_priceUpdateData","type":"bytes[]"},{"internalType":"uint256","name":"_openEndIndex","type":"uint256"},{"internalType":"uint256","name":"_closeEndIndex","type":"uint256"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"executePositionsWithPrices","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"feeCalculator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getClosePositionRequest","outputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"productId","type":"uint256"},{"internalType":"uint256","name":"margin","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"blockTime","type":"uint256"}],"internalType":"struct PositionManager.ClosePositionRequest","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"}],"name":"getClosePositionRequestFromKey","outputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"productId","type":"uint256"},{"internalType":"uint256","name":"margin","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"blockTime","type":"uint256"}],"internalType":"struct PositionManager.ClosePositionRequest","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getOpenPositionRequest","outputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"productId","type":"uint256"},{"internalType":"uint256","name":"margin","type":"uint256"},{"internalType":"uint256","name":"leverage","type":"uint256"},{"internalType":"uint256","name":"tradeFee","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"blockTime","type":"uint256"}],"internalType":"struct PositionManager.OpenPositionRequest","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"}],"name":"getOpenPositionRequestFromKey","outputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"productId","type":"uint256"},{"internalType":"uint256","name":"margin","type":"uint256"},{"internalType":"uint256","name":"leverage","type":"uint256"},{"internalType":"uint256","name":"tradeFee","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"blockTime","type":"uint256"}],"internalType":"struct PositionManager.OpenPositionRequest","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getRequestKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getRequestQueueLengths","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isPositionKeeper","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isUserCancelEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isUserExecuteEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"managers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTimeDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minBlockDelayKeeper","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minExecutionFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minTimeCancelDelayPublic","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minTimeExecuteDelayPublic","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"openPositionRequestKeys","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openPositionRequestKeysStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"openPositionRequests","outputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"productId","type":"uint256"},{"internalType":"uint256","name":"margin","type":"uint256"},{"internalType":"uint256","name":"leverage","type":"uint256"},{"internalType":"uint256","name":"tradeFee","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"blockTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"openPositionsIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pikaPerp","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referralStorage","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"},{"internalType":"bool","name":"_isActive","type":"bool"}],"name":"setAccountManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_allowPublicKeeper","type":"bool"}],"name":"setAllowPublicKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_allowUserCloseOnly","type":"bool"}],"name":"setAllowUserCloseOnly","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minBlockDelayKeeper","type":"uint256"},{"internalType":"uint256","name":"_minTimeExecuteDelayPublic","type":"uint256"},{"internalType":"uint256","name":"_minTimeCancelDelayPublic","type":"uint256"},{"internalType":"uint256","name":"_maxTimeDelay","type":"uint256"}],"name":"setDelayValues","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeCalculator","type":"address"}],"name":"setFeeCalculator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gov","type":"address"}],"name":"setGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isUserCancelEnabled","type":"bool"}],"name":"setIsUserCancelEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isUserExecuteEnabled","type":"bool"}],"name":"setIsUserExecuteEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"},{"internalType":"bool","name":"_isActive","type":"bool"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minExecutionFee","type":"uint256"}],"name":"setMinExecutionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oracle","type":"address"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_isActive","type":"bool"}],"name":"setPositionKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_referralStorage","type":"address"}],"name":"setReferralStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_increasePositionRequestKeysStart","type":"uint256"},{"internalType":"uint256","name":"_decreasePositionRequestKeysStart","type":"uint256"}],"name":"setRequestKeysStartValues","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenBase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60e0604052600b805463ffffffff19166101011790553480156200002257600080fd5b50604051620050f0380380620050f08339810160408190526200004591620000dd565b60008054336001600160a01b0319918216811790925560018055606097881b6001600160601b03199081166080526003805483166001600160a01b03998a1617905560048054831697909816969096179096559290951b90921660a05260069190915560c0929092526002805490911690911790556200014c565b80516001600160a01b0381168114620000d857600080fd5b919050565b60008060008060008060c08789031215620000f757600080fd5b6200010287620000c0565b95506200011260208801620000c0565b94506200012260408801620000c0565b93506200013260608801620000c0565b92506080870151915060a087015190509295509295509295565b60805160601c60a05160601c60c051614ed46200021c60003960008181610b9101528181610f1501528181610f9801528181610fda01528181611b7a01528181611c8d01528181612223015261237f015260008181610b5d01528181610ee201528181610f6601528181611b4c01528181611bcc015281816121c90152818161230c01526123d0015260008181610442015281816112740152818161150a01528181611f75015281816121ef0152818161232e015281816123590152818161244f01526134e00152614ed46000f3fe60806040526004361061035f5760003560e01c80637adbf973116101c4578063ab1903a8116100f6578063cb0269c91161009a578063f2cea6a51161006c578063f2cea6a514610c51578063f851a44014610c8a578063fc2cee6214610caa578063fdff9b4d14610cca57005b8063cb0269c914610be9578063cfad57a214610bff578063e6d6735a14610c1f578063ec342ad014610c3957005b8063b2016bd4116100d3578063b2016bd414610b4b578063b61daaee14610b7f578063c516909014610bb3578063c64c0c4b14610bd357005b8063ab1903a814610aeb578063ae4d7f9a14610b0b578063b00eb9fe14610b2b57005b80638c66d04f11610168578063a07554041161013a578063a0755404146109c8578063a3aad82e146109e9578063a5e90eee14610aab578063aaebd7d014610acb57005b80638c66d04f146109605780638e153bc4146109805780638f303b76146109935780639d4512f8146109a857005b80638052248c116101a15780638052248c146108d3578063819e893f146108f35780638af2bf57146109135780638bdfd88b1461094057005b80637adbf973146108735780637dc0d1d0146108935780637f47a102146108b357005b806353cc32701161029d57806362f8a3fe11610241578063680df9c411610213578063680df9c414610817578063704b6c021461082d5780637284432a1461084d578063737a61201461086057005b806362f8a3fe1461079e578063634e89e8146107be57806363ae2103146107eb578063672e92d81461080157005b80635841fcaa1161027a5780635841fcaa146106695780635ac4f79e1461067f57806361d497941461069f57806362b2ad001461077e57005b806353cc3270146105a457806355804143146105b75780635662791b1461064957005b80632b5b9dac116103045780633422ead1116102e15780633422ead11461051e57806336eba48a1461053e5780633934eb5c1461056e578063496e85581461058457005b80632b5b9dac146104bf578063308aa81f146104de57806334127d04146104fe57005b806312d43a511161033d57806312d43a51146104105780631980cd35146104305780632511e72814610464578063299c9d091461048457005b80626cc35e146103685780630883ed02146103a55780630cf7d564146103c557005b3661036657005b005b34801561037457600080fd5b50600554610388906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156103b157600080fd5b506103666103c03660046148a2565b610cfa565b3480156103d157600080fd5b506104006103e03660046145fb565b601660209081526000928352604080842090915290825290205460ff1681565b604051901515815260200161039c565b34801561041c57600080fd5b50600054610388906001600160a01b031681565b34801561043c57600080fd5b506103887f000000000000000000000000000000000000000000000000000000000000000081565b34801561047057600080fd5b5061040061047f36600461491a565b610d75565b34801561049057600080fd5b506104b161049f3660046145de565b60136020526000908152604090205481565b60405190815260200161039c565b3480156104cb57600080fd5b50600b5461040090610100900460ff1681565b3480156104ea57600080fd5b506103666104f9366004614958565b611127565b34801561050a57600080fd5b5061040061051936600461491a565b611199565b34801561052a57600080fd5b50610366610539366004614634565b611718565b34801561054a57600080fd5b506104006105593660046145de565b60106020526000908152604090205460ff1681565b34801561057a57600080fd5b506104b1600f5481565b34801561059057600080fd5b506104b161059f3660046148dc565b6117a1565b6103666105b23660046147d8565b6117c2565b3480156105c357600080fd5b506105d76105d2366004614662565b6117ef565b60405161039c919081516001600160a01b0316815260208083015190820152604080830151908201526060808301511515908201526080808301519082015260a0828101519082015260c0808301519082015260e0808301519082015261010091820151918101919091526101200190565b34801561065557600080fd5b506103666106643660046148a2565b61188e565b34801561067557600080fd5b506104b160075481565b34801561068b57600080fd5b506104b161069a3660046148dc565b611905565b3480156106ab57600080fd5b506107216106ba3660046148dc565b601260205260009081526040902080546001820154600283015460038401546004850154600586015460068701546007880154600889015460098a0154600a909a01546001600160a01b0390991699979896979596949560ff90941694929391929091908b565b604080516001600160a01b03909c168c5260208c019a909a52988a01979097526060890195909552608088019390935290151560a087015260c086015260e08501526101008401526101208301526101408201526101600161039c565b34801561078a57600080fd5b50600b546104009062010000900460ff1681565b3480156107aa57600080fd5b506104b16107b9366004614662565b611915565b3480156107ca57600080fd5b506107de6107d9366004614662565b61195c565b60405161039c9190614c12565b3480156107f757600080fd5b506104b160065481565b34801561080d57600080fd5b506104b160095481565b34801561082357600080fd5b506104b160085481565b34801561083957600080fd5b506103666108483660046145de565b611a11565b61036661085b366004614769565b611ab1565b61036661086e366004614837565b611cde565b34801561087f57600080fd5b5061036661088e3660046145de565b611dc9565b34801561089f57600080fd5b50600454610388906001600160a01b031681565b3480156108bf57600080fd5b506103666108ce366004614634565b611e15565b3480156108df57600080fd5b506104006108ee36600461491a565b611e84565b3480156108ff57600080fd5b506105d761090e3660046148dc565b612661565b34801561091f57600080fd5b506104b161092e3660046145de565b60116020526000908152604090205481565b34801561094c57600080fd5b5061036661095b3660046148a2565b6126f0565b34801561096c57600080fd5b5061036661097b3660046145de565b612763565b61036661098e36600461468e565b6127af565b34801561099f57600080fd5b506104006128c4565b3480156109b457600080fd5b506103666109c33660046148a2565b61298a565b3480156109d457600080fd5b50600b54610400906301000000900460ff1681565b3480156109f557600080fd5b50610a5d610a043660046148dc565b6014602052600090815260409020805460018201546002830154600384015460048501546005860154600687015460078801546008909801546001600160a01b03909716979596949560ff909416949293919290919089565b604080516001600160a01b03909a168a5260208a0198909852968801959095529215156060870152608086019190915260a085015260c084015260e08301526101008201526101200161039c565b348015610ab757600080fd5b50610366610ac6366004614634565b6129ff565b348015610ad757600080fd5b506107de610ae63660046148dc565b612a85565b348015610af757600080fd5b50610400610b0636600461491a565b612b2a565b348015610b1757600080fd5b50610366610b263660046145de565b612d3f565b348015610b3757600080fd5b50600354610388906001600160a01b031681565b348015610b5757600080fd5b506103887f000000000000000000000000000000000000000000000000000000000000000081565b348015610b8b57600080fd5b506104b17f000000000000000000000000000000000000000000000000000000000000000081565b348015610bbf57600080fd5b50610366610bce36600461497a565b612db7565b348015610bdf57600080fd5b506104b1600e5481565b348015610bf557600080fd5b506104b1600a5481565b348015610c0b57600080fd5b50610366610c1a3660046145de565b612e43565b348015610c2b57600080fd5b50600b546104009060ff1681565b348015610c4557600080fd5b506104b16305f5e10081565b348015610c5d57600080fd5b50600e54600c54600f54600d5460408051948552602085019390935291830152606082015260800161039c565b348015610c9657600080fd5b50600254610388906001600160a01b031681565b348015610cb657600080fd5b50610366610cc53660046148dc565b612eb7565b348015610cd657600080fd5b50610400610ce53660046145de565b60156020526000908152604090205460ff1681565b6002546001600160a01b03163314610d2d5760405162461bcd60e51b8152600401610d2490614b28565b60405180910390fd5b600b805460ff19168215159081179091556040519081527f58331dcec30d19299d46fa051395268fc68ee71ed1771a9e7e4f6cdc358a2bf9906020015b60405180910390a150565b600060026001541415610d9a5760405162461bcd60e51b8152600401610d2490614bdb565b6002600181815560008581526012602090815260409182902082516101608101845281546001600160a01b0316808252948201549281019290925293840154918101919091526003830154606082015260048301546080820152600583015460ff16151560a0820152600683015460c0820152600783015460e082015260088301546101008201526009830154610120820152600a90920154610140830152610e4757600191505061111d565b6000610e628261012001518361014001518460000151612f16565b905080610e745760009250505061111d565b600085815260126020526040812080546001600160a01b03191681556001810182905560028101829055600381018290556004810182905560058101805460ff1916905560068101829055600781018290556008810182905560098101829055600a01556001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016610fcb57610f8d82600001516305f5e1007f000000000000000000000000000000000000000000000000000000000000000085608001518660400151610f489190614cb7565b610f529190614cf1565b610f5c9190614ccf565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169190613035565b610fc6846305f5e1007f00000000000000000000000000000000000000000000000000000000000000008560e00151610f529190614cf1565b611047565b61100d82600001516305f5e1007f000000000000000000000000000000000000000000000000000000000000000085608001518660400151610f489190614cb7565b6110476305f5e1008360e00151670de0b6b3a764000061102d9190614cf1565b6110379190614ccf565b6001600160a01b038616906130f7565b81600001516001600160a01b03167f8a7811729d375743691a44aa980ceddc3e670397b5047ed835683390094398ae83602001518460400151856060015186608001518760a001518860c001518960e001518a61010001516110b78c61012001514361321090919063ffffffff16565b6101408d01516110c8904290613210565b604080519a8b5260208b0199909952978901969096526060880194909452911515608087015260a086015260c085015260e0840152610100830152610120820152610140015b60405180910390a26001925050505b6001805592915050565b6002546001600160a01b031633146111515760405162461bcd60e51b8152600401610d2490614b28565b600e829055600f81905560408051838152602081018390527febb0f666150f4be5b60c45df8f3e49992510b0128027fe58eea6110f296493bc91015b60405180910390a15050565b6000600260015414156111be5760405162461bcd60e51b8152600401610d2490614bdb565b6002600181815560008581526014602090815260409182902082516101208101845281546001600160a01b031680825294820154928101929092529384015491810191909152600383015460ff161515606082015260048301546080820152600583015460a0820152600683015460c0820152600783015460e082015260089092015461010083015261125557600191505061111d565b6020810151604051632e76c56d60e21b81526000916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163b9db15b4916112ab9160040190815260200190565b6101206040518083038186803b1580156112c457600080fd5b505afa1580156112d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112fc91906146ea565b50505050505050509050600082606001511561139d57600480546040516303b6b4bb60e51b81526001600160a01b0385811693820193909352600060248201529116906376d697609060440160206040518083038186803b15801561136057600080fd5b505afa158015611374573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611398919061493f565b611423565b600480546040516303b6b4bb60e51b81526001600160a01b0385811693820193909352600160248201529116906376d697609060440160206040518083038186803b1580156113eb57600080fd5b505afa1580156113ff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611423919061493f565b9050600061144d8460e001518561010001518660000151600088606001511589608001518861321c565b90508061146157600094505050505061111d565b600087815260146020908152604080832080546001600160a01b0319168155600181018490556002810184905560038101805460ff191690556004808201859055600582018590556006820185905560078201859055600890910193909355865191870151878201516060890151925163514b903960e11b81526001600160a01b03948516958101959095526024850191909152604484015215156064830152608482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a29720729060a401600060405180830381600087803b15801561154e57600080fd5b505af1158015611562573d6000803e3d6000fd5b505050506115a06305f5e1008560a00151670de0b6b3a76400006115869190614cf1565b6115909190614ccf565b6001600160a01b038816906130f7565b60055460009081906001600160a01b03161561163957600554865160405163534ef88360e01b81526001600160a01b03918216600482015291169063534ef88390602401604080518083038186803b1580156115fb57600080fd5b505afa15801561160f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061163391906148f5565b90925090505b85600001516001600160a01b03167f457aedf645dce385d76432f8b8ec6605ad77a013f9fc62228e3ea7b3bf2e21ad8760200151886040015189606001518a608001518b60a001518c60c0015161169d8e60e001514361321090919063ffffffff16565b6101008f01516116ae904290613210565b604080519889526020890197909752941515878701526060870193909352608086019190915260a085015260c084015260e083015261010082018690526001600160a01b03851661012083015251908190036101400190a250506001808055979650505050505050565b6002546001600160a01b031633146117425760405162461bcd60e51b8152600401610d2490614b28565b6001600160a01b038216600081815260106020908152604091829020805460ff191685151590811790915591519182527ffbabc02389290a451c6e600d05bf9887b99bfad39d8e1237e4e3df042e4941fe910160405180910390a25050565b600d81815481106117b157600080fd5b600091825260209091200154905081565b6117e9848484600e546117d59190614cb7565b85600f546117e39190614cb7565b85611cde565b50505050565b6117f76144d6565b601460006118058585611915565b8152602080820192909252604090810160002081516101208101835281546001600160a01b03168152600182015493810193909352600281015491830191909152600381015460ff161515606083015260048101546080830152600581015460a0830152600681015460c0830152600781015460e0830152600801546101008201529392505050565b6002546001600160a01b031633146118b85760405162461bcd60e51b8152600401610d2490614b28565b600b805482151563010000000263ff000000199091161790556040517f217940ceb1b67fc0fb0a06287af08fc84ced756bd618d9b5edb6e31e7f401dbc90610d6a90831515815260200190565b600c81815481106117b157600080fd5b6040516bffffffffffffffffffffffff19606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b61196461452d565b601260006119728585611915565b8152602080820192909252604090810160002081516101608101835281546001600160a01b031681526001820154938101939093526002810154918301919091526003810154606083015260048101546080830152600581015460ff16151560a0830152600681015460c0830152600781015460e083015260088101546101008301526009810154610120830152600a01546101408201529392505050565b6000546001600160a01b03163314611a635760405162461bcd60e51b815260206004820152601560248201527423b7bb32b93730b136329d103337b93134b23232b760591b6044820152606401610d24565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a190602001610d6a565b60026001541415611ad45760405162461bcd60e51b8152600401610d2490614bdb565b6002600155600654821015611afb5760405162461bcd60e51b8152600401610d2490614b5f565b336001600160a01b0389161480611b165750611b1688613491565b611b325760405162461bcd60e51b8152600401610d2490614ade565b6000611b4087878a8c6134d9565b90506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016611bf757611bf26305f5e1007f000000000000000000000000000000000000000000000000000000000000000083611ba48b88614cb7565b611bae9190614cb7565b611bb89190614cf1565b611bc29190614ccf565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690613645565b611cb6565b6305f5e100611c0e84670de0b6b3a7640000614cf1565b611c189190614ccf565b3414611c835760405162461bcd60e51b815260206004820152603460248201527f506f736974696f6e4d616e616765723a20696e636f72726563742065786563756044820152731d1a5bdb88199959481d1c985b9cd9995c9c995960621b6064820152608401610d24565b611cb66305f5e1007f0000000000000000000000000000000000000000000000000000000000000000611bae848b614cb7565b611cbf8261371b565b611ccf898989848a8a8a8a61379c565b50506001805550505050505050565b3360009081526010602052604090205460ff1680611d045750600b5462010000900460ff165b611d505760405162461bcd60e51b815260206004820181905260248201527f506f736974696f6e4d616e616765723a2021706f736974696f6e4b65657065726044820152606401610d24565b6004805460405163c267818160e01b81526001600160a01b039091169163c2678181913491611d859133918b918b9101614a1d565b6000604051808303818588803b158015611d9e57600080fd5b505af1158015611db2573d6000803e3d6000fd5b5050505050611dc28383836139c3565b5050505050565b6002546001600160a01b03163314611df35760405162461bcd60e51b8152600401610d2490614b28565b600480546001600160a01b0319166001600160a01b0392909216919091179055565b3360008181526016602090815260408083206001600160a01b03871680855290835292819020805460ff1916861515908117909155815194855291840192909252908201527ffa4ec1eddf88dd5f08e4bfca8bfd618541cab8f998bd15946e64b3c771becb949060600161118d565b600060026001541415611ea95760405162461bcd60e51b8152600401610d2490614bdb565b6002600181815560008581526012602090815260409182902082516101608101845281546001600160a01b0316808252948201549281019290925293840154918101919091526003830154606082015260048301546080820152600583015460ff16151560a0820152600683015460c0820152600783015460e082015260088301546101008201526009830154610120820152600a90920154610140830152611f5657600191505061111d565b6020810151604051632e76c56d60e21b81526000916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163b9db15b491611fac9160040190815260200190565b6101206040518083038186803b158015611fc557600080fd5b505afa158015611fd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ffd91906146ea565b5050505050505050905060008260a0015161209d57600480546040516303b6b4bb60e51b81526001600160a01b0385811693820193909352600060248201529116906376d697609060440160206040518083038186803b15801561206057600080fd5b505afa158015612074573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612098919061493f565b612123565b600480546040516303b6b4bb60e51b81526001600160a01b0385811693820193909352600160248201529116906376d697609060440160206040518083038186803b1580156120eb57600080fd5b505afa1580156120ff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612123919061493f565b905061214b836101200151846101400151856000015160018760a001518860c001518761321c565b61215b576000935050505061111d565b600086815260126020526040812080546001600160a01b03191681556001810182905560028101829055600381018290556004810182905560058101805460ff1916905560068101829055600781018290556008810182905560098101829055600a01556001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166122ff577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632345057d6305f5e1007f0000000000000000000000000000000000000000000000000000000000000000866080015187604001516122569190614cb7565b6122609190614cf1565b61226a9190614ccf565b8551602087015160408089015160a08a015160608b0151925160e088901b6001600160e01b03191681526001600160a01b039095166004860152602485019390935260448401529015156064830152608482015260a4810185905260c4016000604051808303818588803b1580156122e157600080fd5b505af11580156122f5573d6000803e3d6000fd5b50505050506124ae565b6123546001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000060006139d7565b6123f77f00000000000000000000000000000000000000000000000000000000000000006305f5e1007f0000000000000000000000000000000000000000000000000000000000000000866080015187604001516123b29190614cb7565b6123bc9190614cf1565b6123c69190614ccf565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691906139d7565b8251602084015160408086015160a087015160608801519251632345057d60e01b81526001600160a01b0395861660048201526024810194909452604484019190915215156064830152608482015260a481018390527f000000000000000000000000000000000000000000000000000000000000000090911690632345057d9060c401600060405180830381600087803b15801561249557600080fd5b505af11580156124a9573d6000803e3d6000fd5b505050505b6124e86305f5e1008460e00151670de0b6b3a76400006124ce9190614cf1565b6124d89190614ccf565b6001600160a01b038716906130f7565b60055460009081906001600160a01b03161561258157600554855160405163534ef88360e01b81526001600160a01b03918216600482015291169063534ef88390602401604080518083038186803b15801561254357600080fd5b505afa158015612557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257b91906148f5565b90925090505b84600001516001600160a01b03167f67ec5e1575c3b7ad145ab62669f7b0cf9a5bf84a55162a098b2b3e2f1c1e1e9d86602001518760400151886060015189608001518a60a001518b60c001518c60e001518d61010001516125f18f61014001514261321090919063ffffffff16565b60408051998a5260208a0198909852968801959095526060870193909352901515608086015260a085015260c084015260e083015261010082015261012081018590526001600160a01b0384166101408201526101600160405180910390a2505060018080559695505050505050565b6126696144d6565b5060009081526014602090815260409182902082516101208101845281546001600160a01b03168152600182015492810192909252600281015492820192909252600382015460ff161515606082015260048201546080820152600582015460a0820152600682015460c0820152600782015460e082015260089091015461010082015290565b6002546001600160a01b0316331461271a5760405162461bcd60e51b8152600401610d2490614b28565b600b80548215156101000261ff00199091161790556040517faf6eef0b9d8e5b1d685fec937b1a6b2bc987f262d24ed1dcf7b327cd3b4c337690610d6a90831515815260200190565b6002546001600160a01b0316331461278d5760405162461bcd60e51b8152600401610d2490614b28565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b600260015414156127d25760405162461bcd60e51b8152600401610d2490614bdb565b60026001556006548110156127f95760405162461bcd60e51b8152600401610d2490614b5f565b6305f5e10061281082670de0b6b3a7640000614cf1565b61281a9190614ccf565b34146128735760405162461bcd60e51b815260206004820152602260248201527f506f736974696f6e4d616e616765723a20696e76616c6964206d73672e76616c604482015261756560f01b6064820152608401610d24565b336001600160a01b038716148061288e575061288e86613491565b6128aa5760405162461bcd60e51b8152600401610d2490614ade565b6128b8868686868686613b2e565b50506001805550505050565b600c54600e5460009111801561292357504361292060075460126000600c600e54815481106128f5576128f5614dbf565b9060005260206000200154815260200190815260200160002060090154613d0f90919063ffffffff16565b11155b806129855750600d54600f5410801561298557504361298260075460146000600d600f548154811061295757612957614dbf565b9060005260206000200154815260200190815260200160002060070154613d0f90919063ffffffff16565b11155b905090565b6002546001600160a01b031633146129b45760405162461bcd60e51b8152600401610d2490614b28565b600b8054821515620100000262ff0000199091161790556040517f478b69cbec0f3fcbb7c9f224df297d3385a199faf2dd4d917287d5dfc9daac2090610d6a90831515815260200190565b6002546001600160a01b03163314612a295760405162461bcd60e51b8152600401610d2490614b28565b6001600160a01b038216600081815260156020908152604091829020805460ff19168515159081179091558251938452908301527fbe9474bb3e78da7e315cdffa5cfa30b767fcc95bbf44a6197da60228eea10286910161118d565b612a8d61452d565b5060009081526012602090815260409182902082516101608101845281546001600160a01b031681526001820154928101929092526002810154928201929092526003820154606082015260048201546080820152600582015460ff16151560a0820152600682015460c0820152600782015460e082015260088201546101008201526009820154610120820152600a9091015461014082015290565b600060026001541415612b4f5760405162461bcd60e51b8152600401610d2490614bdb565b6002600181815560008581526014602090815260409182902082516101208101845281546001600160a01b031680825294820154928101929092529384015491810191909152600383015460ff161515606082015260048301546080820152600583015460a0820152600683015460c0820152600783015460e0820152600890920154610100830152612be657600191505061111d565b6000612c008260e001518361010001518460000151612f16565b905080612c125760009250505061111d565b600085815260146020526040812080546001600160a01b0319168155600181018290556002810182905560038101805460ff19169055600481018290556005810182905560068101829055600781018290556008015560a0820151612c89906305f5e1009061102d90670de0b6b3a7640000614cf1565b81600001516001600160a01b03167fb7a6f1dcb51698329a59a7df64eb230274b899bb051fa6e29bcd0d85cc5e048283602001518460400151856060015186608001518760a001518860c00151612ced8a60e001514361321090919063ffffffff16565b6101008b0151612cfe904290613210565b604080519889526020890197909752941515958701959095526060860192909252608085015260a084015260c083019190915260e08201526101000161110e565b6002546001600160a01b03163314612d695760405162461bcd60e51b8152600401610d2490614b28565b600580546001600160a01b0319166001600160a01b0383169081179091556040519081527f828abcccea18192c21d645e575652c49e20b986dab777906fc473d056b01b6a890602001610d6a565b6002546001600160a01b03163314612de15760405162461bcd60e51b8152600401610d2490614b28565b600784905560088390556009829055600a8190556040805185815260208101859052908101839052606081018290527fa51804b935245b47cb5ae3ba68b49e1e2473a09b9eb03f383895b3ebd1d6e5599060800160405180910390a150505050565b6000546001600160a01b03163314612e955760405162461bcd60e51b815260206004820152601560248201527423b7bb32b93730b136329d103337b93134b23232b760591b6044820152606401610d24565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6002546001600160a01b03163314612ee15760405162461bcd60e51b8152600401610d2490614b28565b60068190556040518181527f52a8358457e20bbb36e4086b83fb0749599f1893fe4c35a876c46dc4886d12db90602001610d6a565b600b5460009030331490610100900460ff16158015612f33575080155b15612f505760405162461bcd60e51b8152600401610d2490614ba4565b8015612f765743612f6c60075487613d0f90919063ffffffff16565b111591505061302e565b336001600160a01b03841614612f9e5760405162461bcd60e51b8152600401610d2490614ba4565b42612fb460095486613d0f90919063ffffffff16565b11156130285760405162461bcd60e51b815260206004820152603a60248201527f506f736974696f6e4d616e616765723a206d696e2064656c6179206e6f74207960448201527f65742070617373656420666f722063616e63656c6c6174696f6e0000000000006064820152608401610d24565b60019150505b9392505050565b80156130f2576001600160a01b0383166130de576000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613096576040519150601f19603f3d011682016040523d82523d6000602084013e61309b565b606091505b50509050806117e95760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b6044820152606401610d24565b6130f26001600160a01b0384168383613d1b565b505050565b804710156131475760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610d24565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613194576040519150601f19603f3d011682016040523d82523d6000602084013e613199565b606091505b50509050806130f25760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610d24565b600061302e8284614d10565b600042613234600a5489613d0f90919063ffffffff16565b1161328d5760405162461bcd60e51b8152602060048201526024808201527f506f736974696f6e4d616e616765723a207265717565737420686173206578706044820152631a5c995960e21b6064820152608401610d24565b600b543330149060ff161580156132a2575080155b156132bf5760405162461bcd60e51b8152600401610d2490614ba4565b80156133af578415613330578383111561332b5760405162461bcd60e51b815260206004820152602760248201527f506f736974696f6e4d616e616765723a2063757272656e7420707269636520746044820152660dede40d0d2ced60cb1b6064820152608401610d24565b61338f565b8383101561338f5760405162461bcd60e51b815260206004820152602660248201527f506f736974696f6e4d616e616765723a2063757272656e7420707269636520746044820152656f6f206c6f7760d01b6064820152608401610d24565b436133a56007548b613d0f90919063ffffffff16565b1115915050613486565b8515806133c65750600b546301000000900460ff16155b80156133da5750336001600160a01b038816145b6133f65760405162461bcd60e51b8152600401610d2490614ba4565b4261340c6008548a613d0f90919063ffffffff16565b11156134805760405162461bcd60e51b815260206004820152603760248201527f506f736974696f6e4d616e616765723a206d696e2064656c6179206e6f74207960448201527f65742070617373656420666f7220657865637574696f6e0000000000000000006064820152608401610d24565b60019150505b979650505050505050565b3360009081526015602052604081205460ff1680156134d357506001600160a01b038216600090815260166020908152604080832033845290915290205460ff165b92915050565b60008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b9db15b4866040518263ffffffff1660e01b815260040161352c91815260200190565b6101206040518083038186803b15801561354557600080fd5b505afa158015613559573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061357d91906146ea565b505060035460405163181e471b60e21b8152979950949750506001600160a01b03909316946360791c6c94506135f593508c92508b9150879087908b90339060040195865260208601949094526001600160a01b0392831660408601526060850191909152811660808401521660a082015260c00190565b60206040518083038186803b15801561360d57600080fd5b505afa158015613621573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613486919061493f565b8015613702576001600160a01b03821661370657803410156136a95760405162461bcd60e51b815260206004820152601a60248201527f556e6945524332303a206e6f7420656e6f7567682076616c75650000000000006044820152606401610d24565b803411156137025760006136bd3483613210565b604051909150600090339083908381818185875af1925050503d8060008114613096576040519150601f19603f3d011682016040523d82523d6000602084013e61309b565b5050565b6137026001600160a01b038316333084613d4b565b801580159061373457506005546001600160a01b031615155b15613799576005546040516356b4b2ad60e01b8152336004820152602481018390526001600160a01b03909116906356b4b2ad90604401600060405180830381600087803b15801561378557600080fd5b505af1158015611dc2573d6000803e3d6000fd5b50565b6001600160a01b0388166000908152601160205260408120546137c0906001613d0f565b6001600160a01b038a1660008181526011602090815260408083208590558051610160810182529384529083018c905282018a9052606082018890526080820189905286151560a083015260c0820186905260e08201859052610100820183905243610120830152426101408301529192509061383d8b84611915565b6000818152601260209081526040808320865181546001600160a01b039182166001600160a01b031990911617825592870151600180830191909155828801516002830155606088015160038301556080880151600483015560a088015160058301805491151560ff1990921691909117905560c0880151600683015560e0880151600783015561010088015160088301556101208801516009830155610140880151600a90920191909155600c805491820181559093527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c790920183905590519192508c16907f0b78f1597afadf8fca2bae58d2b1fdf85ffd5a62a70b7c9e4212e02dd294ea50906139ae908d908d908c908e908d908d908d908d90439042903a909a8b5260208b019990995260408a01979097526060890195909552921515608088015260a087019190915260c086015260e08501526101008401526101208301526101408201526101600190565b60405180910390a25050505050505050505050565b6139cd8382613d83565b6130f2828261400b565b801580613a605750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b158015613a2657600080fd5b505afa158015613a3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a5e919061493f565b155b613acb5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610d24565b6040516001600160a01b0383166024820152604481018290526130f290849063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614293565b6001600160a01b038616600090815260136020526040812054613b52906001613d0f565b6001600160a01b03881660008181526013602090815260408083208590558051610120810182529384529083018a9052820188905286151560608301526080820186905260a0820185905260c082018390524360e08301524261010083015291925090613bbf8984611915565b6000818152601460209081526040808320865181546001600160a01b039182166001600160a01b031990911617825592870151600180830191909155828801516002830155606088015160038301805491151560ff199092169190911790556080880151600483015560a0880151600583015560c0880151600683015560e08801516007830155610100880151600890920191909155600d805491820181559093527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb590920183905590519192508a16907fec732ce54a7d63ca49bd10a7367c4477263625779b35852b0f0cf87dd2aacd7090613cfc908b908b908b908b908b908b9043904290978852602088019690965293151560408701526060860192909252608085015260a084015260c083015260e08201526101000190565b60405180910390a2505050505050505050565b600061302e8284614cb7565b6040516001600160a01b0383166024820152604481018290526130f290849063a9059cbb60e01b90606401613af7565b6040516001600160a01b03808516602483015283166044820152606481018290526117e99085906323b872dd60e01b90608401613af7565b600e54600c54808210613d965750505050565b80841115613da2578093505b83821015614003576000600c8381548110613dbf57613dbf614dbf565b600091825260209091200154604051632014892360e21b8152600481018290526001600160a01b03861660248201529091503090638052248c90604401602060405180830381600087803b158015613e1657600080fd5b505af1925050508015613e46575060408051601f3d908101601f19168201909252613e43918101906148bf565b60015b613fc457613e52614dd5565b806308c379a01415613f5f5750613e67614df1565b80613e725750613f61565b600082815260126020526040908190205490516001600160a01b03909116907fcbcbe4acd0386acabd867b6d4bcf646526b9d0931e4dd69b059a54d9c282bb1590613ec09087908590614c9e565b60405180910390a26040516304a23ce560e31b8152600481018390526001600160a01b03861660248201523090632511e728906044015b602060405180830381600087803b158015613f1157600080fd5b505af1925050508015613f41575060408051601f3d908101601f19168201909252613f3e918101906148bf565b60015b613f4a57613f59565b80613f5757505050614003565b505b50613fd2565b505b3d808015613f8b576040519150601f19603f3d011682016040523d82523d6000602084013e613f90565b606091505b506040516304a23ce560e31b8152600481018390526001600160a01b03861660248201523090632511e72890604401613ef7565b80613fd0575050614003565b505b600c8381548110613fe557613fe5614dbf565b600091825260208220015582613ffa81614d8e565b93505050613da2565b50600e555050565b600f54600d5480821061401e5750505050565b8084111561402a578093505b8382101561428b576000600d838154811061404757614047614dbf565b600091825260209091200154604051630d049f4160e21b8152600481018290526001600160a01b038616602482015290915030906334127d0490604401602060405180830381600087803b15801561409e57600080fd5b505af19250505080156140ce575060408051601f3d908101601f191682019092526140cb918101906148bf565b60015b61424c576140da614dd5565b806308c379a014156141e757506140ef614df1565b806140fa57506141e9565b600082815260146020526040908190205490516001600160a01b03909116907f4d9305d9d9a1ba4872e8662ffc2378824f77df4194681e60ff1946f1533a153f906141489087908590614c9e565b60405180910390a2604051631563207560e31b8152600481018390526001600160a01b0386166024820152309063ab1903a8906044015b602060405180830381600087803b15801561419957600080fd5b505af19250505080156141c9575060408051601f3d908101601f191682019092526141c6918101906148bf565b60015b6141d2576141e1565b806141df5750505061428b565b505b5061425a565b505b3d808015614213576040519150601f19603f3d011682016040523d82523d6000602084013e614218565b606091505b50604051631563207560e31b8152600481018390526001600160a01b0386166024820152309063ab1903a89060440161417f565b8061425857505061428b565b505b600d838154811061426d5761426d614dbf565b60009182526020822001558261428281614d8e565b9350505061402a565b50600f555050565b60006142e8826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166143659092919063ffffffff16565b8051909150156130f2578080602001905181019061430691906148bf565b6130f25760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610d24565b6060614374848460008561437c565b949350505050565b6060824710156143dd5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610d24565b6001600160a01b0385163b6144345760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d24565b600080866001600160a01b031685876040516144509190614a01565b60006040518083038185875af1925050503d806000811461448d576040519150601f19603f3d011682016040523d82523d6000602084013e614492565b606091505b5091509150613486828286606083156144ac57508161302e565b8251156144bc5782518084602001fd5b8160405162461bcd60e51b8152600401610d249190614acb565b60405180610120016040528060006001600160a01b03168152602001600081526020016000815260200160001515815260200160008152602001600081526020016000815260200160008152602001600081525090565b60405180610160016040528060006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081526020016000815260200160008152602001600081525090565b60008083601f8401126145a457600080fd5b50813567ffffffffffffffff8111156145bc57600080fd5b6020830191508360208260051b85010111156145d757600080fd5b9250929050565b6000602082840312156145f057600080fd5b813561302e81614e7b565b6000806040838503121561460e57600080fd5b823561461981614e7b565b9150602083013561462981614e7b565b809150509250929050565b6000806040838503121561464757600080fd5b823561465281614e7b565b9150602083013561462981614e90565b6000806040838503121561467557600080fd5b823561468081614e7b565b946020939093013593505050565b60008060008060008060c087890312156146a757600080fd5b86356146b281614e7b565b9550602087013594506040870135935060608701356146d081614e90565b9598949750929560808101359460a0909101359350915050565b60008060008060008060008060006101208a8c03121561470957600080fd5b895161471481614e7b565b8099505060208a0151975060408a0151965060608a015161473481614e90565b8096505060808a0151945060a08a0151935060c08a0151925060e08a015191506101008a015190509295985092959850929598565b600080600080600080600080610100898b03121561478657600080fd5b883561479181614e7b565b975060208901359650604089013595506060890135945060808901356147b681614e90565b979a969950949793969560a0850135955060c08501359460e001359350915050565b600080600080606085870312156147ee57600080fd5b843567ffffffffffffffff81111561480557600080fd5b61481187828801614592565b90955093505060208501359150604085013561482c81614e7b565b939692955090935050565b60008060008060006080868803121561484f57600080fd5b853567ffffffffffffffff81111561486657600080fd5b61487288828901614592565b9096509450506020860135925060408601359150606086013561489481614e7b565b809150509295509295909350565b6000602082840312156148b457600080fd5b813561302e81614e90565b6000602082840312156148d157600080fd5b815161302e81614e90565b6000602082840312156148ee57600080fd5b5035919050565b6000806040838503121561490857600080fd5b82519150602083015161462981614e7b565b6000806040838503121561492d57600080fd5b82359150602083013561462981614e7b565b60006020828403121561495157600080fd5b5051919050565b6000806040838503121561496b57600080fd5b50508035926020909101359150565b6000806000806080858703121561499057600080fd5b5050823594602084013594506040840135936060013592509050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b600081518084526149ed816020860160208601614d27565b601f01601f19169290920160200192915050565b60008251614a13818460208701614d27565b9190910192915050565b6001600160a01b0384168152604060208083018290529082018390526000906060600585901b840181019190840186845b87811015614abd57868503605f190183528135368a9003601e19018112614a7457600080fd5b8901803567ffffffffffffffff811115614a8d57600080fd5b8036038b1315614a9c57600080fd5b614aa987828885016149ac565b965050509183019190830190600101614a4e565b509298975050505050505050565b60208152600061302e60208301846149d5565b6020808252602a908201527f506f736974696f6e4d616e616765723a206e6f207065726d697373696f6e20666040820152691bdc881858d8dbdd5b9d60b21b606082015260800190565b60208082526017908201527f506f736974696f6e4d616e616765723a202161646d696e000000000000000000604082015260600190565b60208082526025908201527f506f736974696f6e4d616e616765723a20696e76616c696420657865637574696040820152646f6e46656560d81b606082015260800190565b6020808252601a908201527f506f736974696f6e4d616e616765723a20666f7262696464656e000000000000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b81516001600160a01b0316815261016081016020830151602083015260408301516040830152606083015160608301526080830151608083015260a0830151614c5f60a084018215159052565b5060c083015160c083015260e083015160e083015261010080840151818401525061012080840151818401525061014080840151818401525092915050565b82815260406020820152600061437460408301846149d5565b60008219821115614cca57614cca614da9565b500190565b600082614cec57634e487b7160e01b600052601260045260246000fd5b500490565b6000816000190483118215151615614d0b57614d0b614da9565b500290565b600082821015614d2257614d22614da9565b500390565b60005b83811015614d42578181015183820152602001614d2a565b838111156117e95750506000910152565b601f8201601f1916810167ffffffffffffffff81118282101715614d8757634e487b7160e01b600052604160045260246000fd5b6040525050565b6000600019821415614da257614da2614da9565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600060033d1115614dee5760046000803e5060005160e01c5b90565b600060443d1015614dff5790565b6040516003193d81016004833e81513d67ffffffffffffffff8160248401118184111715614e2f57505050505090565b8285019150815181811115614e475750505050505090565b843d8701016020828501011115614e615750505050505090565b614e7060208286010187614d53565b509095945050505050565b6001600160a01b038116811461379957600080fd5b801515811461379957600080fdfea264697066735822122046ef7ef3e05761bf5e6ab2a4ac52a0b3baebddf3eb34dc3ece239e3b43f58e6e64736f6c634300080700330000000000000000000000009b86b2be8edb2958089e522fe0eb7dd5935975ab000000000000000000000000e3451b170806aab3e24b5cd03a331c1ccdb4d7c10000000000000000000000002a3c0592dcb58accd346ccee2bb46e3fb744987a0000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c316070000000000000000000000000000000000000000000000000000000000004e2000000000000000000000000000000000000000000000000000000000000f4240
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000009b86b2be8edb2958089e522fe0eb7dd5935975ab000000000000000000000000e3451b170806aab3e24b5cd03a331c1ccdb4d7c10000000000000000000000002a3c0592dcb58accd346ccee2bb46e3fb744987a0000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c316070000000000000000000000000000000000000000000000000000000000004e2000000000000000000000000000000000000000000000000000000000000f4240
-----Decoded View---------------
Arg [0] : _pikaPerp (address): 0x9b86B2Be8eDB2958089E522Fe0eB7dD5935975AB
Arg [1] : _feeCalculator (address): 0xe3451b170806Aab3e24b5Cd03a331C1CCdb4d7C1
Arg [2] : _oracle (address): 0x2A3c0592dCb58accD346cCEE2bB46e3fB744987a
Arg [3] : _collateralToken (address): 0x7F5c764cBc14f9669B88837ca1490cCa17c31607
Arg [4] : _minExecutionFee (uint256): 20000
Arg [5] : _tokenBase (uint256): 1000000
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000009b86b2be8edb2958089e522fe0eb7dd5935975ab
Arg [1] : 000000000000000000000000e3451b170806aab3e24b5cd03a331c1ccdb4d7c1
Arg [2] : 0000000000000000000000002a3c0592dcb58accd346ccee2bb46e3fb744987a
Arg [3] : 0000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c31607
Arg [4] : 0000000000000000000000000000000000000000000000000000000000004e20
Arg [5] : 00000000000000000000000000000000000000000000000000000000000f4240
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.