Contract 0xb67c152e69217b5acb85a2e19df13423351b0e27 11

 

Contract Overview

Balance:
0 ETH

EtherValue:
$0.00

Token:
Txn Hash Method
Block
From
To
Value
0x495ed04c7b66a660a8ace24f615d3a30cab41840781bf34d42470ee83bb20b7fExecute N Positi...1131179062023-12-06 3:49:4952 secs ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0008233501380.021542143
0x63e29a0c7772402bab98429df85deeda0e475e4031e3a8920904977da75f9bd7Create Open Posi...1131179042023-12-06 3:49:4556 secs ago0xfb076dc52e7b750fb7779811048993541db4e572 IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001341990740.010760427
0x50d6446cdfbb4c2d0650de9ac78e740b7f40bd37c79344fda913b2a0fc2a1f3dExecute N Positi...1131178312023-12-06 3:47:193 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0009763112690.021060272
0xa3942323a5d53656c60e78ab496ab944c0bd6d56c1040955124a7870b3e4ea5dCreate Close Pos...1131178292023-12-06 3:47:153 mins ago0xfb076dc52e7b750fb7779811048993541db4e572 IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001200240980.010797165
0xf67e9c8c4d4b7a22b81de9bfb5a6666910d22c1d2d3d3694757fec1271164147Execute N Positi...1131178082023-12-06 3:46:334 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0007900672770.02178799
0x5f2ceff5ef5889f02b0fb8c54b2fedfb4dac8c40f87591d1a4f31e3025179afeCreate Close Pos...1131178052023-12-06 3:46:274 mins ago0xfb076dc52e7b750fb7779811048993541db4e572 IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001177646180.010273405
0x94588ac397a7f2f366da446aabbd90078e2868fefa5efc9b21adb40473631624Execute N Positi...1131177762023-12-06 3:45:295 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0007899636070.0234293
0x81d838d2bdabb85fa3258e3c6f4826e45fd362078e8af8ddce39b0aed75fb9c9Create Close Pos...1131177742023-12-06 3:45:255 mins ago0x45e6c9a1a7a9c513d23296e293679155d1f8060e IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001164693190.012620523
0x6292c2cf121b34ce902e116a831bf1cc52da6aa6a40e5c076352db88af430917Execute N Positi...1131174122023-12-06 3:33:2117 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.000711627210.021357506
0xdde2c0fb50f50e67b4bef9257318630c5e06d934713ba3152b2f0a4f68b17900Create Open Posi...1131174102023-12-06 3:33:1717 mins ago0xfb076dc52e7b750fb7779811048993541db4e572 IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001315862540.010784121
0x79fa5912a724e453fc22d274e7f9d7472775211cbd36e29e416ee51723567dabExecute N Positi...1131171022023-12-06 3:23:0127 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0007974574220.023494219
0xbb7e3449bc0b87ece71738e626245a5adc3091f7c3db09646d58514a27716914Create Open Posi...1131170992023-12-06 3:22:5527 mins ago0x85893cc28f86c3138e1e3f888b16466e8accafc6 IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001307829940.011338136
0x9fccb7257803bf92d5584000f566d5057bdfc803be88ad80c393549edf8d78bbExecute N Positi...1131164102023-12-06 2:59:5750 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0009288383280.019077976
0xc0c1d63b7c7e66191a5d4b099e8535bdb197335e13e78dec155af7988f962950Create Open Posi...1131164082023-12-06 2:59:5350 mins ago0xfb076dc52e7b750fb7779811048993541db4e572 IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001234716330.007771625
0x196530ffd92da01af3add30d9aa21315ad9274b5c87ecd6e196d00ad5fa40304Execute N Positi...1131162952023-12-06 2:56:0754 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0008146043390.018903305
0xdbeb06e210c2f8424123026a4a43251741aa8ff980f3ad475ab833053eca57e3Create Open Posi...1131162922023-12-06 2:56:0154 mins ago0xeab44011c8c98352017229f6a46e7f2b5ebc7e2b IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001298193440.007385599
0xa454170b3a15fbee6a4be4946f704b2e36daaa9b1a416ed46600d6edb812c417Execute N Positi...1131161852023-12-06 2:52:2758 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0007681181040.019818286
0x5056beddf3d08a49a623e3f140366bb001ccc34b7762e41f4149f5d23c003b96Create Close Pos...1131161822023-12-06 2:52:2158 mins ago0xfb076dc52e7b750fb7779811048993541db4e572 IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001146678590.008434955
0x9cd029c977500e7ba8cac8bb19cb84ede5d180b5a19c303641c1b43f05cb8495Execute N Positi...1131157932023-12-06 2:39:231 hr 11 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0008870043090.019396549
0x81d6658ff90d73409b2f6693f2f0c64e9ae0b890822587e01b2a1f80890b4194Create Open Posi...1131157912023-12-06 2:39:191 hr 11 mins ago0xc834395315ff25140d18cfd1fdf146363217bc60 IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001382678230.0084471
0x5d700d0d24585e4c5380518ca4d011e86c72f67efe98996196f1cfe4513cc2f6Execute N Positi...1131157032023-12-06 2:36:231 hr 14 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0011334240950.018925858
0x5e20e14ed41c2025198d52c9478c34ed1f07984331ef197110a94fd8dacd2a51Create Open Posi...1131157012023-12-06 2:36:191 hr 14 mins ago0xfb076dc52e7b750fb7779811048993541db4e572 IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001515500290.00813067
0x13baf72dc58c58b838d96f36a5d72e289e2daa27e70245bbf6d57281ebabb2d1Execute N Positi...1131155732023-12-06 2:32:031 hr 18 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0011159409020.01912024
0x11304a13aab88ef103c3f3321dda338242b4c4bd2e4e4018e9d7a2dbcbeced89Execute N Positi...1131154832023-12-06 2:29:031 hr 21 mins ago0x7edc26c27a6180618a00ce11e5cfb8e85720b6ae IN  0xb67c152e69217b5acb85a2e19df13423351b0e271 wei0.0008939517310.019375431
0x782a0045693dea2c98a407d9c650add6b6d024ad1e23330dbf51c9ce0bf499b5Create Open Posi...1131154802023-12-06 2:28:571 hr 21 mins ago0xfb076dc52e7b750fb7779811048993541db4e572 IN  0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH0.0001414050710.007521293
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x495ed04c7b66a660a8ace24f615d3a30cab41840781bf34d42470ee83bb20b7f1131179062023-12-06 3:49:4952 secs ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0x495ed04c7b66a660a8ace24f615d3a30cab41840781bf34d42470ee83bb20b7f1131179062023-12-06 3:49:4952 secs ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0x50d6446cdfbb4c2d0650de9ac78e740b7f40bd37c79344fda913b2a0fc2a1f3d1131178312023-12-06 3:47:193 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0x50d6446cdfbb4c2d0650de9ac78e740b7f40bd37c79344fda913b2a0fc2a1f3d1131178312023-12-06 3:47:193 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0xf67e9c8c4d4b7a22b81de9bfb5a6666910d22c1d2d3d3694757fec12711641471131178082023-12-06 3:46:334 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0xf67e9c8c4d4b7a22b81de9bfb5a6666910d22c1d2d3d3694757fec12711641471131178082023-12-06 3:46:334 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0x94588ac397a7f2f366da446aabbd90078e2868fefa5efc9b21adb404736316241131177762023-12-06 3:45:295 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0x94588ac397a7f2f366da446aabbd90078e2868fefa5efc9b21adb404736316241131177762023-12-06 3:45:295 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0x6292c2cf121b34ce902e116a831bf1cc52da6aa6a40e5c076352db88af4309171131174122023-12-06 3:33:2117 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0x6292c2cf121b34ce902e116a831bf1cc52da6aa6a40e5c076352db88af4309171131174122023-12-06 3:33:2117 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0x79fa5912a724e453fc22d274e7f9d7472775211cbd36e29e416ee51723567dab1131171022023-12-06 3:23:0127 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0x79fa5912a724e453fc22d274e7f9d7472775211cbd36e29e416ee51723567dab1131171022023-12-06 3:23:0127 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0x9fccb7257803bf92d5584000f566d5057bdfc803be88ad80c393549edf8d78bb1131164102023-12-06 2:59:5750 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0x9fccb7257803bf92d5584000f566d5057bdfc803be88ad80c393549edf8d78bb1131164102023-12-06 2:59:5750 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0x196530ffd92da01af3add30d9aa21315ad9274b5c87ecd6e196d00ad5fa403041131162952023-12-06 2:56:0754 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0x196530ffd92da01af3add30d9aa21315ad9274b5c87ecd6e196d00ad5fa403041131162952023-12-06 2:56:0754 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0xa454170b3a15fbee6a4be4946f704b2e36daaa9b1a416ed46600d6edb812c4171131161852023-12-06 2:52:2758 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0xa454170b3a15fbee6a4be4946f704b2e36daaa9b1a416ed46600d6edb812c4171131161852023-12-06 2:52:2758 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0x9cd029c977500e7ba8cac8bb19cb84ede5d180b5a19c303641c1b43f05cb84951131157932023-12-06 2:39:231 hr 11 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0x9cd029c977500e7ba8cac8bb19cb84ede5d180b5a19c303641c1b43f05cb84951131157932023-12-06 2:39:231 hr 11 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0x5d700d0d24585e4c5380518ca4d011e86c72f67efe98996196f1cfe4513cc2f61131157032023-12-06 2:36:231 hr 14 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0x5d700d0d24585e4c5380518ca4d011e86c72f67efe98996196f1cfe4513cc2f61131157032023-12-06 2:36:231 hr 14 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0x13baf72dc58c58b838d96f36a5d72e289e2daa27e70245bbf6d57281ebabb2d11131155732023-12-06 2:32:031 hr 18 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e270x7edc26c27a6180618a00ce11e5cfb8e85720b6ae0.00055 ETH
0x13baf72dc58c58b838d96f36a5d72e289e2daa27e70245bbf6d57281ebabb2d11131155732023-12-06 2:32:031 hr 18 mins ago 0xb67c152e69217b5acb85a2e19df13423351b0e27 0x2a3c0592dcb58accd346ccee2bb46e3fb744987a1 wei
0x6765c382ba9c17681612c65be04b01a5cb38b1216d767368153147baeb671b5b1131155712023-12-06 2:31:591 hr 18 mins ago 0xa78cd820b198a943199deb0506e77d655b5078cc 0xb67c152e69217b5acb85a2e19df13423351b0e270.00055 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PositionManager

Compiler Version
v0.8.7+commit.e28d00a7

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 16 : PositionManager.sol
// 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 {}
}

File 2 of 16 : SafeMath.sol
// 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;
        }
    }
}

File 3 of 16 : IERC20.sol
// 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);
}

File 4 of 16 : SafeERC20.sol
// 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");
        }
    }
}

File 5 of 16 : ReentrancyGuard.sol
// 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;
    }
}

File 6 of 16 : IPikaPerp.sol
//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);
}

File 7 of 16 : UniERC20.sol
// 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);
            }
        }
    }
}

File 8 of 16 : PikaPerpV4.sol
// 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");
    }

}

File 9 of 16 : Governable.sol
// 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;
    }
}

File 10 of 16 : IReferralStorage.sol
// 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;
}

File 11 of 16 : Address.sol
// 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);
            }
        }
    }
}

File 12 of 16 : IOracle.sol
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;
}

File 13 of 16 : PerpLib.sol
// 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);
    }
}

File 14 of 16 : IFundingManager.sol
//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);
}

File 15 of 16 : IVaultReward.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IVaultReward {
    function updateReward(address account) external;
}

File 16 of 16 : IFeeCalculator.sol
// 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);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"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"}]

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


Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.