Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 44043303 | 1152 days ago | 0 ETH | ||||
| 44043286 | 1152 days ago | 0 ETH | ||||
| 44043271 | 1152 days ago | 0 ETH | ||||
| 44043255 | 1152 days ago | 0 ETH | ||||
| 44043238 | 1152 days ago | 0 ETH | ||||
| 44043218 | 1152 days ago | 0 ETH | ||||
| 44043201 | 1152 days ago | 0 ETH | ||||
| 44043189 | 1152 days ago | 0 ETH | ||||
| 44043175 | 1152 days ago | 0 ETH | ||||
| 44043154 | 1152 days ago | 0 ETH | ||||
| 44043138 | 1152 days ago | 0 ETH | ||||
| 44043118 | 1152 days ago | 0 ETH | ||||
| 44043104 | 1152 days ago | 0 ETH | ||||
| 44043087 | 1152 days ago | 0 ETH | ||||
| 44043075 | 1152 days ago | 0 ETH | ||||
| 44043058 | 1152 days ago | 0 ETH | ||||
| 44043040 | 1152 days ago | 0 ETH | ||||
| 44043024 | 1152 days ago | 0 ETH | ||||
| 44043010 | 1152 days ago | 0 ETH | ||||
| 44042991 | 1152 days ago | 0 ETH | ||||
| 44042975 | 1152 days ago | 0 ETH | ||||
| 44042961 | 1152 days ago | 0 ETH | ||||
| 44042946 | 1152 days ago | 0 ETH | ||||
| 44042927 | 1152 days ago | 0 ETH | ||||
| 44042911 | 1152 days ago | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
LiquidityPoolShutdownModule
Compiler Version
v0.7.4+commit.3f05b770
Optimization Enabled:
Yes with 1000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/SafeCastUpgradeable.sol";
import "../interface/IAccessControl.sol";
import "../interface/IGovernor.sol";
import "../interface/IPoolCreatorFull.sol";
import "../interface/ISymbolService.sol";
import "../libraries/SafeMathExt.sol";
import "../libraries/OrderData.sol";
import "../libraries/Utils.sol";
import "./AMMModule.sol";
import "./CollateralModule.sol";
import "./MarginAccountModule.sol";
import "./PerpetualModule.sol";
import "../Type.sol";
library LiquidityPoolShutdownModule {
using SafeCastUpgradeable for uint256;
using SafeCastUpgradeable for int256;
using SafeMathExt for int256;
using SafeMathExt for uint256;
using SafeMathUpgradeable for uint256;
using SignedSafeMathUpgradeable for int256;
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
using OrderData for uint32;
using AMMModule for LiquidityPoolStorage;
using CollateralModule for LiquidityPoolStorage;
using MarginAccountModule for PerpetualStorage;
using PerpetualModule for PerpetualStorage;
uint256 public constant OPERATOR_CHECK_IN_TIMEOUT = 10 days;
uint256 public constant MAX_PERPETUAL_COUNT = 48;
event AddLiquidity(
address indexed trader,
int256 addedCash,
int256 mintedShare,
int256 addedPoolMargin
);
event RemoveLiquidity(
address indexed trader,
int256 returnedCash,
int256 burnedShare,
int256 removedPoolMargin
);
event UpdatePoolMargin(int256 poolMargin);
event TransferOperatorTo(address indexed newOperator);
event ClaimOperator(address indexed newOperator);
event RevokeOperator();
event SetLiquidityPoolParameter(int256[4] value);
event CreatePerpetual(
uint256 perpetualIndex,
address governor,
address shareToken,
address operator,
address oracle,
address collateral,
int256[9] baseParams,
int256[9] riskParams
);
event RunLiquidityPool();
event OperatorCheckIn(address indexed operator);
event DonateInsuranceFund(int256 amount);
event TransferExcessInsuranceFundToLP(int256 amount);
event SetTargetLeverage(uint256 perpetualIndex, address indexed trader, int256 targetLeverage);
event AddAMMKeeper(uint256 perpetualIndex, address indexed keeper);
event RemoveAMMKeeper(uint256 perpetualIndex, address indexed keeper);
event AddTraderKeeper(uint256 perpetualIndex, address indexed keeper);
event RemoveTraderKeeper(uint256 perpetualIndex, address indexed keeper);
/**
* @dev Get the vault's address of the liquidity pool
*
* @param liquidityPool The reference of liquidity pool storage.
* @return vault The vault's address of the liquidity pool
*/
function getVault(LiquidityPoolStorage storage liquidityPool)
public
view
returns (address vault)
{
vault = IPoolCreatorFull(liquidityPool.creator).getVault();
}
function getShareTransferDelay(LiquidityPoolStorage storage liquidityPool)
public
view
returns (uint256 delay)
{
delay = liquidityPool.shareTransferDelay.max(1);
}
function getOperator(LiquidityPoolStorage storage liquidityPool)
internal
view
returns (address)
{
return
block.timestamp <= liquidityPool.operatorExpiration
? liquidityPool.operator
: address(0);
}
function getTransferringOperator(LiquidityPoolStorage storage liquidityPool)
internal
view
returns (address)
{
return
block.timestamp <= liquidityPool.operatorExpiration
? liquidityPool.transferringOperator
: address(0);
}
/**
* @dev Get the vault fee rate of the liquidity pool
*
* @param liquidityPool The reference of liquidity pool storage.
* @return vaultFeeRate The vault fee rate.
*/
function getVaultFeeRate(LiquidityPoolStorage storage liquidityPool)
public
view
returns (int256 vaultFeeRate)
{
vaultFeeRate = IPoolCreatorFull(liquidityPool.creator).getVaultFeeRate();
}
/**
* @dev Get the available pool cash(collateral) of the liquidity pool excluding the specific perpetual. Available cash
* in a perpetual means: margin - initial margin
*
* @param liquidityPool The reference of liquidity pool storage.
* @param exclusiveIndex The index of perpetual in the liquidity pool to exclude,
* set to liquidityPool.perpetualCount to skip excluding.
* @return availablePoolCash The available pool cash(collateral) of the liquidity pool excluding the specific perpetual
*/
function getAvailablePoolCash(
LiquidityPoolStorage storage liquidityPool,
uint256 exclusiveIndex
) public view returns (int256 availablePoolCash) {
uint256 length = liquidityPool.perpetualCount;
for (uint256 i = 0; i < length; i++) {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
if (i == exclusiveIndex || perpetual.state != PerpetualState.NORMAL) {
continue;
}
int256 markPrice = perpetual.getMarkPrice();
availablePoolCash = availablePoolCash.add(
perpetual.getMargin(address(this), markPrice).sub(
perpetual.getInitialMargin(address(this), markPrice)
)
);
}
return availablePoolCash.add(liquidityPool.poolCash);
}
/**
* @dev Get the available pool cash(collateral) of the liquidity pool.
* Sum of available cash of AMM in every perpetual in the liquidity pool, and add the pool cash.
*
* @param liquidityPool The reference of liquidity pool storage.
* @return availablePoolCash The available pool cash(collateral) of the liquidity pool
*/
function getAvailablePoolCash(LiquidityPoolStorage storage liquidityPool)
public
view
returns (int256 availablePoolCash)
{
return getAvailablePoolCash(liquidityPool, liquidityPool.perpetualCount);
}
/**
* @dev Check if Trader is maintenance margin safe in the perpetual.
*
* @param liquidityPool The reference of liquidity pool storage.
* @param perpetualIndex The index of the perpetual in the liquidity pool.
* @param trader The address of the trader
* @param tradeAmount The amount of positions actually traded in the transaction
* @return isSafe True if Trader is maintenance margin safe in the perpetual.
*/
function isTraderMarginSafe(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
address trader,
int256 tradeAmount
) public view returns (bool isSafe) {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
bool hasOpened = Utils.hasOpenedPosition(perpetual.getPosition(trader), tradeAmount);
int256 markPrice = perpetual.getMarkPrice();
return
hasOpened
? perpetual.isInitialMarginSafe(trader, markPrice)
: perpetual.isMarginSafe(trader, markPrice);
}
/**
* @dev Initialize the liquidity pool and set up its configuration.
*
* @param liquidityPool The reference of liquidity pool storage.
* @param collateral The collateral's address of the liquidity pool.
* @param collateralDecimals The collateral's decimals of the liquidity pool.
* @param operator The operator's address of the liquidity pool.
* @param governor The governor's address of the liquidity pool.
* @param initData The byte array contains data to initialize new created liquidity pool.
*/
function initialize(
LiquidityPoolStorage storage liquidityPool,
address creator,
address collateral,
uint256 collateralDecimals,
address operator,
address governor,
bytes memory initData
) public {
require(collateral != address(0), "collateral is invalid");
require(governor != address(0), "governor is invalid");
(
bool isFastCreationEnabled,
int256 insuranceFundCap,
uint256 liquidityCap,
uint256 shareTransferDelay
) = abi.decode(initData, (bool, int256, uint256, uint256));
require(liquidityCap >= 0, "liquidity cap should be greater than 0");
require(shareTransferDelay >= 1, "share transfer delay should be at lease 1");
liquidityPool.initializeCollateral(collateral, collateralDecimals);
liquidityPool.creator = creator;
liquidityPool.accessController = IPoolCreatorFull(creator).getAccessController();
liquidityPool.operator = operator;
liquidityPool.operatorExpiration = block.timestamp.add(OPERATOR_CHECK_IN_TIMEOUT);
liquidityPool.governor = governor;
liquidityPool.shareToken = governor;
liquidityPool.isFastCreationEnabled = isFastCreationEnabled;
liquidityPool.insuranceFundCap = insuranceFundCap;
liquidityPool.liquidityCap = liquidityCap;
liquidityPool.shareTransferDelay = shareTransferDelay;
}
/**
* @dev Create and initialize new perpetual in the liquidity pool. Can only called by the operator
* if the liquidity pool is running or isFastCreationEnabled is set to true.
* Otherwise can only called by the governor
* @param liquidityPool The reference of liquidity pool storage.
* @param oracle The oracle's address of the perpetual
* @param baseParams The base parameters of the perpetual
* @param riskParams The risk parameters of the perpetual, must between minimum value and maximum value
* @param minRiskParamValues The risk parameters' minimum values of the perpetual
* @param maxRiskParamValues The risk parameters' maximum values of the perpetual
*/
function createPerpetual(
LiquidityPoolStorage storage liquidityPool,
address oracle,
int256[9] calldata baseParams,
int256[9] calldata riskParams,
int256[9] calldata minRiskParamValues,
int256[9] calldata maxRiskParamValues
) public {
require(
liquidityPool.perpetualCount < MAX_PERPETUAL_COUNT,
"perpetual count exceeds limit"
);
uint256 perpetualIndex = liquidityPool.perpetualCount;
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
perpetual.initialize(
perpetualIndex,
oracle,
baseParams,
riskParams,
minRiskParamValues,
maxRiskParamValues
);
ISymbolService service = ISymbolService(
IPoolCreatorFull(liquidityPool.creator).getSymbolService()
);
service.allocateSymbol(address(this), perpetualIndex);
if (liquidityPool.isRunning) {
perpetual.setNormalState();
}
liquidityPool.perpetualCount++;
emit CreatePerpetual(
perpetualIndex,
liquidityPool.governor,
liquidityPool.shareToken,
getOperator(liquidityPool),
oracle,
liquidityPool.collateralToken,
baseParams,
riskParams
);
}
/**
* @dev Run the liquidity pool. Can only called by the operator. The operator can create new perpetual before running
* or after running if isFastCreationEnabled is set to true
*
* @param liquidityPool The reference of liquidity pool storage.
*/
function runLiquidityPool(LiquidityPoolStorage storage liquidityPool) public {
uint256 length = liquidityPool.perpetualCount;
require(length > 0, "there should be at least 1 perpetual to run");
for (uint256 i = 0; i < length; i++) {
liquidityPool.perpetuals[i].setNormalState();
}
liquidityPool.isRunning = true;
emit RunLiquidityPool();
}
/**
* @dev Set the parameter of the liquidity pool. Can only called by the governor.
*
* @param liquidityPool The reference of liquidity pool storage.
* @param params The new value of the parameter
*/
function setLiquidityPoolParameter(
LiquidityPoolStorage storage liquidityPool,
int256[4] memory params
) public {
validateLiquidityPoolParameter(params);
liquidityPool.isFastCreationEnabled = (params[0] != 0);
liquidityPool.insuranceFundCap = params[1];
liquidityPool.liquidityCap = uint256(params[2]);
liquidityPool.shareTransferDelay = uint256(params[3]);
emit SetLiquidityPoolParameter(params);
}
/**
* @dev Validate the liquidity pool parameter:
* 1. insurance fund cap >= 0
* @param liquidityPoolParams The parameters of the liquidity pool.
*/
function validateLiquidityPoolParameter(int256[4] memory liquidityPoolParams) public pure {
require(liquidityPoolParams[1] >= 0, "insuranceFundCap < 0");
require(liquidityPoolParams[2] >= 0, "liquidityCap < 0");
require(liquidityPoolParams[3] >= 1, "shareTransferDelay < 1");
}
/**
* @dev Add an account to the whitelist, accounts in the whitelist is allowed to call `liquidateByAMM`.
* If never called, the whitelist in poolCreator will be used instead.
* Once called, the local whitelist will be used and the the whitelist in poolCreator will be ignored.
*
* @param keeper The account of keeper.
* @param perpetualIndex The index of perpetual in the liquidity pool
*/
function addAMMKeeper(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
address keeper
) public {
require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
EnumerableSetUpgradeable.AddressSet storage whitelist = liquidityPool
.perpetuals[perpetualIndex]
.ammKeepers;
require(!whitelist.contains(keeper), "keeper is already added");
bool success = whitelist.add(keeper);
require(success, "fail to add keeper to whitelist");
emit AddAMMKeeper(perpetualIndex, keeper);
}
/**
* @dev Remove an account from the `liquidateByAMM` whitelist.
*
* @param keeper The account of keeper.
* @param perpetualIndex The index of perpetual in the liquidity pool
*/
function removeAMMKeeper(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
address keeper
) public {
require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
EnumerableSetUpgradeable.AddressSet storage whitelist = liquidityPool
.perpetuals[perpetualIndex]
.ammKeepers;
require(whitelist.contains(keeper), "keeper is not added");
bool success = whitelist.remove(keeper);
require(success, "fail to remove keeper from whitelist");
emit RemoveAMMKeeper(perpetualIndex, keeper);
}
function setPerpetualOracle(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
address newOracle
) public {
require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
perpetual.setOracle(newOracle);
}
/**
* @dev Set the base parameter of the perpetual. Can only called by the governor
* @param liquidityPool The reference of liquidity pool storage.
* @param perpetualIndex The index of perpetual in the liquidity pool
* @param baseParams The new value of the base parameter
*/
function setPerpetualBaseParameter(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
int256[9] memory baseParams
) public {
require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
perpetual.setBaseParameter(baseParams);
}
/**
* @dev Set the risk parameter of the perpetual, including minimum value and maximum value.
* Can only called by the governor
* @param liquidityPool The reference of liquidity pool storage.
* @param perpetualIndex The index of perpetual in the liquidity pool
* @param riskParams The new value of the risk parameter, must between minimum value and maximum value
* @param minRiskParamValues The minimum value of the risk parameter
* @param maxRiskParamValues The maximum value of the risk parameter
*/
function setPerpetualRiskParameter(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
int256[9] memory riskParams,
int256[9] memory minRiskParamValues,
int256[9] memory maxRiskParamValues
) public {
require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
perpetual.setRiskParameter(riskParams, minRiskParamValues, maxRiskParamValues);
}
/**
* @dev Set the risk parameter of the perpetual. Can only called by the governor
* @param liquidityPool The reference of liquidity pool storage.
* @param perpetualIndex The index of perpetual in the liquidity pool
* @param riskParams The new value of the risk parameter, must between minimum value and maximum value
*/
function updatePerpetualRiskParameter(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
int256[9] memory riskParams
) public {
require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
perpetual.updateRiskParameter(riskParams);
}
/**
* @dev Set the state of the perpetual to "EMERGENCY". Must rebalance first.
* After that the perpetual is not allowed to trade, deposit and withdraw.
* The price of the perpetual is freezed to the settlement price
* @param liquidityPool The reference of liquidity pool storage.
* @param perpetualIndex The index of the perpetual in the liquidity pool
*/
function setEmergencyState(LiquidityPoolStorage storage liquidityPool, uint256 perpetualIndex)
public
{
require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
rebalance(liquidityPool, perpetualIndex);
liquidityPool.perpetuals[perpetualIndex].setEmergencyState();
if (!isAnyPerpetualIn(liquidityPool, PerpetualState.NORMAL)) {
refundDonatedInsuranceFund(liquidityPool);
}
}
/**
* @dev Check if all the perpetuals in the liquidity pool are not in a state.
*/
function isAnyPerpetualIn(LiquidityPoolStorage storage liquidityPool, PerpetualState state)
internal
view
returns (bool)
{
uint256 length = liquidityPool.perpetualCount;
for (uint256 i = 0; i < length; i++) {
if (liquidityPool.perpetuals[i].state == state) {
return true;
}
}
return false;
}
/**
* @dev Check if all the perpetuals in the liquidity pool are not in normal state.
*/
function isAllPerpetualIn(LiquidityPoolStorage storage liquidityPool, PerpetualState state)
internal
view
returns (bool)
{
uint256 length = liquidityPool.perpetualCount;
for (uint256 i = 0; i < length; i++) {
if (liquidityPool.perpetuals[i].state != state) {
return false;
}
}
return true;
}
/**
* @dev Refund donated insurance fund to current operator.
* - If current operator address is non-zero, all the donated funds will be forward to the operator address;
* - If no operator, the donated funds will be dispatched to the LPs according to the ratio of owned shares.
*/
function refundDonatedInsuranceFund(LiquidityPoolStorage storage liquidityPool) internal {
address operator = getOperator(liquidityPool);
if (
(liquidityPool.donatedInsuranceFund > 0 || liquidityPool.insuranceFund > 0) &&
operator != address(0)
) {
int256 toRefund = liquidityPool.donatedInsuranceFund.add(liquidityPool.insuranceFund);
liquidityPool.donatedInsuranceFund = 0;
liquidityPool.insuranceFund = 0;
liquidityPool.transferToUser(operator, toRefund);
}
}
/**
* @dev Set the state of all the perpetuals to "EMERGENCY". Use special type of rebalance.
* After rebalance, pool cash >= 0 and margin / initialMargin is the same in all perpetuals.
* Can only called when AMM is not maintenance margin safe in all perpetuals.
* After that all the perpetuals are not allowed to trade, deposit and withdraw.
* The price of every perpetual is freezed to the settlement price
* @param liquidityPool The reference of liquidity pool storage.
*/
function setAllPerpetualsToEmergencyState(LiquidityPoolStorage storage liquidityPool) public {
require(liquidityPool.perpetualCount > 0, "no perpetual to settle");
int256 margin;
int256 maintenanceMargin;
int256 initialMargin;
uint256 length = liquidityPool.perpetualCount;
for (uint256 i = 0; i < length; i++) {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
if (perpetual.state != PerpetualState.NORMAL) {
continue;
}
int256 markPrice = perpetual.getMarkPrice();
maintenanceMargin = maintenanceMargin.add(
perpetual.getMaintenanceMargin(address(this), markPrice)
);
initialMargin = initialMargin.add(perpetual.getInitialMargin(address(this), markPrice));
margin = margin.add(perpetual.getMargin(address(this), markPrice));
}
margin = margin.add(liquidityPool.poolCash);
// shutdown require(
// shutdown margin < maintenanceMargin ||
// shutdown IPoolCreatorFull(liquidityPool.creator).isUniverseSettled(),
// shutdown "AMM's margin >= maintenance margin or not universe settled"
// shutdown );
// rebalance for settle all perps
// Floor to make sure poolCash >= 0
int256 rate = initialMargin != 0 ? margin.wdiv(initialMargin, Round.FLOOR) : 0;
for (uint256 i = 0; i < length; i++) {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
if (perpetual.state != PerpetualState.NORMAL) {
continue;
}
int256 markPrice = perpetual.getMarkPrice();
// Floor to make sure poolCash >= 0
int256 newMargin = perpetual.getInitialMargin(address(this), markPrice).wmul(
rate,
Round.FLOOR
);
margin = perpetual.getMargin(address(this), markPrice);
int256 deltaMargin = newMargin.sub(margin);
if (deltaMargin > 0) {
// from pool to perp
perpetual.updateCash(address(this), deltaMargin);
transferFromPoolToPerpetual(liquidityPool, i, deltaMargin);
} else if (deltaMargin < 0) {
// from perp to pool
perpetual.updateCash(address(this), deltaMargin);
transferFromPerpetualToPool(liquidityPool, i, deltaMargin.neg());
}
liquidityPool.perpetuals[i].setEmergencyState();
}
require(liquidityPool.poolCash >= 0, "negative poolCash after settle all");
refundDonatedInsuranceFund(liquidityPool);
}
/**
* @dev Set the state of the perpetual to "CLEARED". Add the collateral of AMM in the perpetual to the pool cash.
* Can only called when all the active accounts in the perpetual are cleared
* @param liquidityPool The reference of liquidity pool storage.
* @param perpetualIndex The index of the perpetual in the liquidity pool
*/
function setClearedState(LiquidityPoolStorage storage liquidityPool, uint256 perpetualIndex)
public
{
require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
perpetual.countMargin(address(this));
perpetual.setClearedState();
int256 marginToReturn = perpetual.settle(address(this));
transferFromPerpetualToPool(liquidityPool, perpetualIndex, marginToReturn);
}
/**
* @dev Specify a new address to be operator. See transferOperator in Governance.sol.
* @param liquidityPool The liquidity pool storage.
* @param newOperator The address of new operator to transfer to
*/
function transferOperator(LiquidityPoolStorage storage liquidityPool, address newOperator)
public
{
require(newOperator != address(0), "new operator is invalid");
require(newOperator != getOperator(liquidityPool), "cannot transfer to current operator");
liquidityPool.transferringOperator = newOperator;
liquidityPool.operatorExpiration = block.timestamp.add(OPERATOR_CHECK_IN_TIMEOUT);
emit TransferOperatorTo(newOperator);
}
/**
* @dev A lease mechanism to check if the operator is alive as the pool manager.
* When called the operatorExpiration will be extended according to OPERATOR_CHECK_IN_TIMEOUT.
* After OPERATOR_CHECK_IN_TIMEOUT, the operator will no longer be the operator.
* New operator will only be raised by voting.
* Transfer operator to another account will renew the expiration.
*
* @param liquidityPool The liquidity pool storage.
*/
function checkIn(LiquidityPoolStorage storage liquidityPool) public {
liquidityPool.operatorExpiration = block.timestamp.add(OPERATOR_CHECK_IN_TIMEOUT);
emit OperatorCheckIn(getOperator(liquidityPool));
}
/**
* @dev Claim the ownership of the liquidity pool to claimer. See `transferOperator` in Governance.sol.
* @param liquidityPool The liquidity pool storage.
* @param claimer The address of claimer
*/
function claimOperator(LiquidityPoolStorage storage liquidityPool, address claimer) public {
require(claimer == getTransferringOperator(liquidityPool), "caller is not qualified");
liquidityPool.operator = claimer;
liquidityPool.operatorExpiration = block.timestamp.add(OPERATOR_CHECK_IN_TIMEOUT);
liquidityPool.transferringOperator = address(0);
IPoolCreatorFull(liquidityPool.creator).registerOperatorOfLiquidityPool(
address(this),
claimer
);
emit ClaimOperator(claimer);
}
/**
* @dev Revoke operator of the liquidity pool.
* @param liquidityPool The liquidity pool object
*/
function revokeOperator(LiquidityPoolStorage storage liquidityPool) public {
liquidityPool.operator = address(0);
IPoolCreatorFull(liquidityPool.creator).registerOperatorOfLiquidityPool(
address(this),
address(0)
);
emit RevokeOperator();
}
/**
* @dev Update the funding state of each perpetual of the liquidity pool. Funding payment of every account in the
* liquidity pool is updated
* @param liquidityPool The reference of liquidity pool storage.
* @param currentTime The current timestamp
*/
function updateFundingState(LiquidityPoolStorage storage liquidityPool, uint256 currentTime)
public
{
if (liquidityPool.fundingTime >= currentTime) {
// invalid time
return;
}
int256 timeElapsed = currentTime.sub(liquidityPool.fundingTime).toInt256();
uint256 length = liquidityPool.perpetualCount;
for (uint256 i = 0; i < length; i++) {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
if (perpetual.state != PerpetualState.NORMAL) {
continue;
}
perpetual.updateFundingState(timeElapsed);
}
liquidityPool.fundingTime = currentTime;
}
/**
* @dev Update the funding rate of each perpetual of the liquidity pool
* @param liquidityPool The reference of liquidity pool storage.
*/
function updateFundingRate(LiquidityPoolStorage storage liquidityPool) public {
(int256 poolMargin, bool isAMMSafe) = liquidityPool.getPoolMargin();
emit UpdatePoolMargin(poolMargin);
if (!isAMMSafe) {
poolMargin = 0;
}
uint256 length = liquidityPool.perpetualCount;
for (uint256 i = 0; i < length; i++) {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
if (perpetual.state != PerpetualState.NORMAL) {
continue;
}
perpetual.updateFundingRate(poolMargin);
}
}
/**
* @dev Update the oracle price of each perpetual of the liquidity pool.
* If oracle is terminated, set market to EMERGENCY.
*
* @param liquidityPool The liquidity pool object
* @param ignoreTerminated Ignore terminated oracle if set to True.
*/
function updatePrice(LiquidityPoolStorage storage liquidityPool, bool ignoreTerminated) public {
uint256 length = liquidityPool.perpetualCount;
for (uint256 i = 0; i < length; i++) {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
if (perpetual.state != PerpetualState.NORMAL) {
continue;
}
perpetual.updatePrice();
if (IOracle(perpetual.oracle).isTerminated() && !ignoreTerminated) {
setEmergencyState(liquidityPool, perpetual.id);
}
}
}
/**
* @dev Donate collateral to the insurance fund of the liquidity pool to make the liquidity pool safe.
*
* @param liquidityPool The liquidity pool object
* @param amount The amount of collateral to donate
*/
function donateInsuranceFund(
LiquidityPoolStorage storage liquidityPool,
address donator,
int256 amount
) public {
require(amount > 0, "invalid amount");
liquidityPool.transferFromUser(donator, amount);
liquidityPool.donatedInsuranceFund = liquidityPool.donatedInsuranceFund.add(amount);
emit DonateInsuranceFund(amount);
}
/**
* @dev Update the collateral of the insurance fund in the liquidity pool.
* If the collateral of the insurance fund exceeds the cap, the extra part of collateral belongs to LP.
* If the collateral of the insurance fund < 0, the donated insurance fund will cover it.
*
* @param liquidityPool The liquidity pool object
* @param deltaFund The update collateral amount of the insurance fund in the perpetual
* @return penaltyToLP The extra part of collateral if the collateral of the insurance fund exceeds the cap
*/
function updateInsuranceFund(LiquidityPoolStorage storage liquidityPool, int256 deltaFund)
public
returns (int256 penaltyToLP)
{
if (deltaFund != 0) {
int256 newInsuranceFund = liquidityPool.insuranceFund.add(deltaFund);
if (deltaFund > 0) {
if (newInsuranceFund > liquidityPool.insuranceFundCap) {
penaltyToLP = newInsuranceFund.sub(liquidityPool.insuranceFundCap);
newInsuranceFund = liquidityPool.insuranceFundCap;
emit TransferExcessInsuranceFundToLP(penaltyToLP);
}
} else {
if (newInsuranceFund < 0) {
liquidityPool.donatedInsuranceFund = liquidityPool.donatedInsuranceFund.add(
newInsuranceFund
);
require(
liquidityPool.donatedInsuranceFund >= 0,
"negative donated insurance fund"
);
newInsuranceFund = 0;
}
}
liquidityPool.insuranceFund = newInsuranceFund;
}
}
/**
* @dev Deposit collateral to the trader's account of the perpetual. The trader's cash will increase.
* Activate the perpetual for the trader if the account in the perpetual is empty before depositing.
* Empty means cash and position are zero.
*
* @param liquidityPool The liquidity pool object
* @param perpetualIndex The index of the perpetual in the liquidity pool
* @param trader The address of the trader
* @param amount The amount of collateral to deposit
*/
function deposit(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
address trader,
int256 amount
) public {
require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
transferFromUserToPerpetual(liquidityPool, perpetualIndex, trader, amount);
if (liquidityPool.perpetuals[perpetualIndex].deposit(trader, amount)) {
IPoolCreatorFull(liquidityPool.creator).activatePerpetualFor(trader, perpetualIndex);
}
}
/**
* @dev Withdraw collateral from the trader's account of the perpetual. The trader's cash will decrease.
* Trader must be initial margin safe in the perpetual after withdrawing.
* Deactivate the perpetual for the trader if the account in the perpetual is empty after withdrawing.
* Empty means cash and position are zero.
*
* @param liquidityPool The liquidity pool object
* @param perpetualIndex The index of the perpetual in the liquidity pool
* @param trader The address of the trader
* @param amount The amount of collateral to withdraw
*/
function withdraw(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
address trader,
int256 amount
) public {
require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
rebalance(liquidityPool, perpetualIndex);
if (perpetual.withdraw(trader, amount)) {
IPoolCreatorFull(liquidityPool.creator).deactivatePerpetualFor(trader, perpetualIndex);
}
transferFromPerpetualToUser(liquidityPool, perpetualIndex, trader, amount);
}
/**
* @dev If the state of the perpetual is "CLEARED", anyone authorized withdraw privilege by trader can settle
* trader's account in the perpetual. Which means to calculate how much the collateral should be returned
* to the trader, return it to trader's wallet and clear the trader's cash and position in the perpetual.
*
* @param liquidityPool The reference of liquidity pool storage.
* @param perpetualIndex The index of the perpetual in the liquidity pool.
* @param trader The address of the trader.
*/
function settle(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
address trader
) public {
require(trader != address(0), "invalid trader");
int256 marginToReturn = liquidityPool.perpetuals[perpetualIndex].settle(trader);
require(marginToReturn > 0, "no margin to settle");
transferFromPerpetualToUser(liquidityPool, perpetualIndex, trader, marginToReturn);
}
/**
* @dev Clear the next active account of the perpetual which state is "EMERGENCY" and send gas reward of collateral
* to sender. If all active accounts are cleared, the clear progress is done and the perpetual's state will
* change to "CLEARED". Active means the trader's account is not empty in the perpetual.
* Empty means cash and position are zero.
*
* @param liquidityPool The reference of liquidity pool storage.
* @param perpetualIndex The index of the perpetual in the liquidity pool
*/
function clear(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
address trader
) public {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
if (
perpetual.keeperGasReward > 0 && perpetual.totalCollateral >= perpetual.keeperGasReward
) {
transferFromPerpetualToUser(
liquidityPool,
perpetualIndex,
trader,
perpetual.keeperGasReward
);
}
if (
perpetual.activeAccounts.length() == 0 ||
perpetual.clear(perpetual.getNextActiveAccount())
) {
setClearedState(liquidityPool, perpetualIndex);
}
}
// shutdown/**
// shutdown * @dev Add collateral to the liquidity pool and get the minted share tokens.
// shutdown * The share token is the credential and use to get the collateral back when removing liquidity.
// shutdown * Can only called when at least 1 perpetual is in NORMAL state.
// shutdown *
// shutdown * @param liquidityPool The reference of liquidity pool storage.
// shutdown * @param trader The address of the trader that adding liquidity
// shutdown * @param cashToAdd The cash(collateral) to add
// shutdown */
// shutdownfunction addLiquidity(
// shutdown LiquidityPoolStorage storage liquidityPool,
// shutdown address trader,
// shutdown int256 cashToAdd
// shutdown) public {
// shutdown require(cashToAdd > 0, "cash amount must be positive");
// shutdown uint256 length = liquidityPool.perpetualCount;
// shutdown bool allowAdd;
// shutdown for (uint256 i = 0; i < length; i++) {
// shutdown if (liquidityPool.perpetuals[i].state == PerpetualState.NORMAL) {
// shutdown allowAdd = true;
// shutdown break;
// shutdown }
// shutdown }
// shutdown require(allowAdd, "all perpetuals are NOT in NORMAL state");
// shutdown liquidityPool.transferFromUser(trader, cashToAdd);
// shutdown
// shutdown IGovernor shareToken = IGovernor(liquidityPool.shareToken);
// shutdown int256 shareTotalSupply = shareToken.totalSupply().toInt256();
// shutdown
// shutdown (int256 shareToMint, int256 addedPoolMargin) = liquidityPool.getShareToMint(
// shutdown shareTotalSupply,
// shutdown cashToAdd
// shutdown );
// shutdown require(shareToMint > 0, "received share must be positive");
// shutdown // pool cash cannot be added before calculation, DO NOT use transferFromUserToPool
// shutdown
// shutdown increasePoolCash(liquidityPool, cashToAdd);
// shutdown shareToken.mint(trader, shareToMint.toUint256());
// shutdown
// shutdown emit AddLiquidity(trader, cashToAdd, shareToMint, addedPoolMargin);
// shutdown}
/**
* @dev Remove collateral from the liquidity pool and redeem the share tokens when the liquidity pool is running.
* Only one of shareToRemove or cashToReturn may be non-zero.
*
* @param liquidityPool The reference of liquidity pool storage.
* @param trader The address of the trader that removing liquidity.
* @param shareToRemove The amount of the share token to redeem.
* @param cashToReturn The amount of cash(collateral) to return.
*/
function removeLiquidity(
LiquidityPoolStorage storage liquidityPool,
address trader,
int256 shareToRemove,
int256 cashToReturn
) public {
IGovernor shareToken = IGovernor(liquidityPool.shareToken);
int256 shareTotalSupply = shareToken.totalSupply().toInt256();
int256 removedInsuranceFund;
int256 removedDonatedInsuranceFund;
int256 removedPoolMargin;
if (cashToReturn == 0 && shareToRemove > 0) {
(
cashToReturn,
removedInsuranceFund,
removedDonatedInsuranceFund,
removedPoolMargin
) = liquidityPool.getCashToReturn(shareTotalSupply, shareToRemove);
require(cashToReturn > 0, "cash to return must be positive");
} else if (cashToReturn > 0 && shareToRemove == 0) {
(
shareToRemove,
removedInsuranceFund,
removedDonatedInsuranceFund,
removedPoolMargin
) = liquidityPool.getShareToRemove(shareTotalSupply, cashToReturn);
require(shareToRemove > 0, "share to remove must be positive");
} else {
revert("invalid parameter");
}
require(
shareToRemove.toUint256() <= shareToken.balanceOf(trader),
"insufficient share balance"
);
int256 removedCashFromPool = cashToReturn.sub(removedInsuranceFund).sub(
removedDonatedInsuranceFund
);
require(
removedCashFromPool <= getAvailablePoolCash(liquidityPool),
"insufficient pool cash"
);
shareToken.burn(trader, shareToRemove.toUint256());
liquidityPool.transferToUser(trader, cashToReturn);
liquidityPool.insuranceFund = liquidityPool.insuranceFund.sub(removedInsuranceFund);
liquidityPool.donatedInsuranceFund = liquidityPool.donatedInsuranceFund.sub(
removedDonatedInsuranceFund
);
decreasePoolCash(liquidityPool, removedCashFromPool);
emit RemoveLiquidity(trader, cashToReturn, shareToRemove, removedPoolMargin);
}
/**
* @dev Add collateral to the liquidity pool without getting share tokens.
*
* @param liquidityPool The reference of liquidity pool storage.
* @param trader The address of the trader that adding liquidity
* @param cashToAdd The cash(collateral) to add
*/
function donateLiquidity(
LiquidityPoolStorage storage liquidityPool,
address trader,
int256 cashToAdd
) public {
require(cashToAdd > 0, "cash amount must be positive");
(, int256 addedPoolMargin) = liquidityPool.getShareToMint(0, cashToAdd);
liquidityPool.transferFromUser(trader, cashToAdd);
// pool cash cannot be added before calculation, DO NOT use transferFromUserToPool
increasePoolCash(liquidityPool, cashToAdd);
emit AddLiquidity(trader, cashToAdd, 0, addedPoolMargin);
}
/**
* @dev To keep the AMM's margin equal to initial margin in the perpetual as possible.
* Transfer collateral between the perpetual and the liquidity pool's cash, then
* update the AMM's cash in perpetual. The liquidity pool's cash can be negative,
* but the available cash can't. If AMM need to transfer and the available cash
* is not enough, transfer all the rest available cash of collateral.
*
* @param liquidityPool The reference of liquidity pool storage.
* @param perpetualIndex The index of the perpetual in the liquidity pool
* @return The amount of rebalanced margin. A positive amount indicates the collaterals
* are moved from perpetual to pool, and a negative amount indicates the opposite.
* 0 means no rebalance happened.
*/
function rebalance(LiquidityPoolStorage storage liquidityPool, uint256 perpetualIndex)
public
returns (int256)
{
require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
if (perpetual.state != PerpetualState.NORMAL) {
return 0;
}
int256 rebalanceMargin = perpetual.getRebalanceMargin();
if (rebalanceMargin == 0) {
// nothing to rebalance
return 0;
} else if (rebalanceMargin > 0) {
// from perp to pool
rebalanceMargin = rebalanceMargin.min(perpetual.totalCollateral);
perpetual.updateCash(address(this), rebalanceMargin.neg());
transferFromPerpetualToPool(liquidityPool, perpetualIndex, rebalanceMargin);
} else {
// from pool to perp
int256 availablePoolCash = getAvailablePoolCash(liquidityPool, perpetualIndex);
if (availablePoolCash <= 0) {
// pool has no more collateral, nothing to rebalance
return 0;
}
rebalanceMargin = rebalanceMargin.abs().min(availablePoolCash);
perpetual.updateCash(address(this), rebalanceMargin);
transferFromPoolToPerpetual(liquidityPool, perpetualIndex, rebalanceMargin);
}
return rebalanceMargin;
}
/**
* @dev Increase the liquidity pool's cash(collateral).
* @param liquidityPool The reference of liquidity pool storage.
* @param amount The amount of cash(collateral) to increase.
*/
function increasePoolCash(LiquidityPoolStorage storage liquidityPool, int256 amount) internal {
require(amount >= 0, "increase negative pool cash");
liquidityPool.poolCash = liquidityPool.poolCash.add(amount);
}
/**
* @dev Decrease the liquidity pool's cash(collateral).
* @param liquidityPool The reference of liquidity pool storage.
* @param amount The amount of cash(collateral) to decrease.
*/
function decreasePoolCash(LiquidityPoolStorage storage liquidityPool, int256 amount) internal {
require(amount >= 0, "decrease negative pool cash");
liquidityPool.poolCash = liquidityPool.poolCash.sub(amount);
}
// user <=> pool (addLiquidity/removeLiquidity)
function transferFromUserToPool(
LiquidityPoolStorage storage liquidityPool,
address account,
int256 amount
) public {
liquidityPool.transferFromUser(account, amount);
increasePoolCash(liquidityPool, amount);
}
function transferFromPoolToUser(
LiquidityPoolStorage storage liquidityPool,
address account,
int256 amount
) public {
if (amount == 0) {
return;
}
liquidityPool.transferToUser(account, amount);
decreasePoolCash(liquidityPool, amount);
}
// user <=> perpetual (deposit/withdraw)
function transferFromUserToPerpetual(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
address account,
int256 amount
) public {
liquidityPool.transferFromUser(account, amount);
liquidityPool.perpetuals[perpetualIndex].increaseTotalCollateral(amount);
}
function transferFromPerpetualToUser(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
address account,
int256 amount
) public {
if (amount == 0) {
return;
}
liquidityPool.transferToUser(account, amount);
liquidityPool.perpetuals[perpetualIndex].decreaseTotalCollateral(amount);
}
// pool <=> perpetual (fee/rebalance)
function transferFromPerpetualToPool(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
int256 amount
) public {
if (amount == 0) {
return;
}
liquidityPool.perpetuals[perpetualIndex].decreaseTotalCollateral(amount);
increasePoolCash(liquidityPool, amount);
}
function transferFromPoolToPerpetual(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
int256 amount
) public {
if (amount == 0) {
return;
}
liquidityPool.perpetuals[perpetualIndex].increaseTotalCollateral(amount);
decreasePoolCash(liquidityPool, amount);
}
/**
* @dev Check if the trader is authorized the privilege by the grantee. Any trader is authorized by himself
* @param liquidityPool The reference of liquidity pool storage.
* @param trader The address of the trader
* @param grantee The address of the grantee
* @param privilege The privilege
* @return isGranted True if the trader is authorized
*/
function isAuthorized(
LiquidityPoolStorage storage liquidityPool,
address trader,
address grantee,
uint256 privilege
) public view returns (bool isGranted) {
isGranted =
trader == grantee ||
IAccessControl(liquidityPool.accessController).isGranted(trader, grantee, privilege);
}
/**
* @dev Deposit or withdraw to let effective leverage == target leverage
*
* @param liquidityPool The reference of liquidity pool storage.
* @param perpetualIndex The index of the perpetual in the liquidity pool.
* @param trader The address of the trader.
* @param deltaPosition The update position of the trader's account in the perpetual.
* @param deltaCash The update cash(collateral) of the trader's account in the perpetual.
* @param totalFee The total fee collected from the trader after the trade.
* @param flags The flags of the trade.
*/
function adjustMarginLeverage(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
address trader,
int256 deltaPosition,
int256 deltaCash,
int256 totalFee,
uint32 flags
) public {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
// read perp
int256 position = perpetual.getPosition(trader);
int256 adjustCollateral;
(int256 closePosition, int256 openPosition) = Utils.splitAmount(
position.sub(deltaPosition),
deltaPosition
);
if (closePosition != 0 && openPosition == 0) {
// close only
adjustCollateral = adjustClosedMargin(
perpetual,
trader,
closePosition,
deltaCash,
totalFee
);
} else {
// open only or close + open
adjustCollateral = adjustOpenedMargin(
perpetual,
trader,
deltaPosition,
deltaCash,
closePosition,
openPosition,
totalFee,
flags
);
}
// real deposit/withdraw
if (adjustCollateral > 0) {
deposit(liquidityPool, perpetualIndex, trader, adjustCollateral);
} else if (adjustCollateral < 0) {
withdraw(liquidityPool, perpetualIndex, trader, adjustCollateral.neg());
}
}
function adjustClosedMargin(
PerpetualStorage storage perpetual,
address trader,
int256 closePosition,
int256 deltaCash,
int256 totalFee
) public view returns (int256 adjustCollateral) {
int256 markPrice = perpetual.getMarkPrice();
int256 position2 = perpetual.getPosition(trader);
if (position2 == 0) {
// close all, withdraw all
return perpetual.getAvailableCash(trader).neg().min(0);
}
// when close, keep the margin ratio
// -withdraw == (availableCash2 * close - (deltaCash - fee) * position2 + reservedValue) / position1
// reservedValue = 0 if position2 == 0 else keeperGasReward * (-deltaPos)
adjustCollateral = perpetual.getAvailableCash(trader).wmul(closePosition);
adjustCollateral = adjustCollateral.sub(deltaCash.sub(totalFee).wmul(position2));
if (position2 != 0) {
adjustCollateral = adjustCollateral.sub(perpetual.keeperGasReward.wmul(closePosition));
}
adjustCollateral = adjustCollateral.wdiv(position2.sub(closePosition));
// withdraw only when IM is satisfied
adjustCollateral = adjustCollateral.max(
perpetual.getAvailableMargin(trader, markPrice).neg()
);
// never deposit when close positions
adjustCollateral = adjustCollateral.min(0);
}
// open only or close + open
function adjustOpenedMargin(
PerpetualStorage storage perpetual,
address trader,
int256 deltaPosition,
int256 deltaCash,
int256 closePosition,
int256 openPosition,
int256 totalFee,
uint32 flags
) public view returns (int256 adjustCollateral) {
int256 markPrice = perpetual.getMarkPrice();
int256 oldMargin = perpetual.getMargin(trader, markPrice);
{
int256 leverage = perpetual.getTargetLeverageWithFlags(trader, flags);
require(leverage > 0, "target leverage = 0");
// openPositionMargin
adjustCollateral = openPosition.abs().wfrac(markPrice, leverage);
}
if (perpetual.getPosition(trader).sub(deltaPosition) != 0 && closePosition == 0) {
// open from non-zero position
// adjustCollateral = openPositionMargin + fee - pnl
adjustCollateral = adjustCollateral
.add(totalFee)
.sub(markPrice.wmul(deltaPosition))
.sub(deltaCash);
} else {
// open from 0 or close + open
adjustCollateral = adjustCollateral.add(perpetual.keeperGasReward).sub(oldMargin);
}
// make sure after adjust: trader is initial margin safe
adjustCollateral = adjustCollateral.max(
perpetual.getAvailableMargin(trader, markPrice).neg()
);
}
// shutdown // deprecated
// shutdown function setTargetLeverage(
// shutdown LiquidityPoolStorage storage liquidityPool,
// shutdown uint256 perpetualIndex,
// shutdown address trader,
// shutdown int256 targetLeverage
// shutdown ) public {
// shutdown PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
// shutdown require(perpetual.initialMarginRate != 0, "initialMarginRate is not set");
// shutdown require(
// shutdown targetLeverage != perpetual.marginAccounts[trader].targetLeverage,
// shutdown "targetLeverage is already set"
// shutdown );
// shutdown int256 maxLeverage = Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate);
// shutdown require(targetLeverage <= maxLeverage, "targetLeverage exceeds maxLeverage");
// shutdown perpetual.setTargetLeverage(trader, targetLeverage);
// shutdown emit SetTargetLeverage(perpetualIndex, trader, targetLeverage);
// shutdown }
// A readonly version of adjustMarginLeverage. This function was written post-audit. So there's a lot of repeated logic here.
function readonlyAdjustMarginLeverage(
PerpetualStorage storage perpetual,
MarginAccount memory trader,
int256 deltaPosition,
int256 deltaCash,
int256 totalFee,
uint32 flags
) public view returns (int256 adjustCollateral) {
// read perp
(int256 closePosition, int256 openPosition) = Utils.splitAmount(
trader.position.sub(deltaPosition),
deltaPosition
);
if (closePosition != 0 && openPosition == 0) {
// close only
adjustCollateral = readonlyAdjustClosedMargin(
perpetual,
trader,
closePosition,
deltaCash,
totalFee
);
} else {
// open only or close + open
adjustCollateral = readonlyAdjustOpenedMargin(
perpetual,
trader,
deltaPosition,
deltaCash,
closePosition,
openPosition,
totalFee,
flags
);
}
}
// A readonly version of adjustClosedMargin. This function was written post-audit. So there's a lot of repeated logic here.
function readonlyAdjustClosedMargin(
PerpetualStorage storage perpetual,
MarginAccount memory trader,
int256 closePosition,
int256 deltaCash,
int256 totalFee
) public view returns (int256 adjustCollateral) {
int256 markPrice = perpetual.getMarkPrice();
int256 position2 = trader.position;
// was perpetual.getAvailableCash(trader)
adjustCollateral = trader.cash.sub(position2.wmul(perpetual.unitAccumulativeFunding));
if (position2 == 0) {
// close all, withdraw all
return adjustCollateral.neg().min(0);
}
// was adjustClosedMargin
adjustCollateral = adjustCollateral.wmul(closePosition);
adjustCollateral = adjustCollateral.sub(deltaCash.sub(totalFee).wmul(position2));
if (position2 != 0) {
adjustCollateral = adjustCollateral.sub(perpetual.keeperGasReward.wmul(closePosition));
}
adjustCollateral = adjustCollateral.wdiv(position2.sub(closePosition));
// withdraw only when IM is satisfied
adjustCollateral = adjustCollateral.max(
readonlyGetAvailableMargin(perpetual, trader, markPrice).neg()
);
// never deposit when close positions
adjustCollateral = adjustCollateral.min(0);
}
// A readonly version of adjustOpenedMargin. This function was written post-audit. So there's a lot of repeated logic here.
function readonlyAdjustOpenedMargin(
PerpetualStorage storage perpetual,
MarginAccount memory trader,
int256 deltaPosition,
int256 deltaCash,
int256 closePosition,
int256 openPosition,
int256 totalFee,
uint32 flags
) public view returns (int256 adjustCollateral) {
int256 markPrice = perpetual.getMarkPrice();
int256 oldMargin = readonlyGetMargin(perpetual, trader, markPrice);
// was perpetual.getTargetLeverageWithFlags
int256 leverage;
{
bool _oldUseTargetLeverage = flags.oldUseTargetLeverage();
bool _newUseTargetLeverage = flags.newUseTargetLeverage();
require(!(_oldUseTargetLeverage && _newUseTargetLeverage), "invalid flags");
if (_oldUseTargetLeverage) {
leverage = trader.targetLeverage;
} else {
leverage = flags.getTargetLeverageByFlags();
}
require(perpetual.initialMarginRate != 0, "initialMarginRate is not set");
int256 maxLeverage = Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate);
leverage = leverage == 0 ? perpetual.defaultTargetLeverage.value : leverage;
leverage = leverage.min(maxLeverage);
}
require(leverage > 0, "target leverage = 0");
// openPositionMargin
adjustCollateral = openPosition.abs().wfrac(markPrice, leverage);
if (trader.position.sub(deltaPosition) != 0 && closePosition == 0) {
// open from non-zero position
// adjustCollateral = openPositionMargin + fee - pnl
adjustCollateral = adjustCollateral
.add(totalFee)
.sub(markPrice.wmul(deltaPosition))
.sub(deltaCash);
} else {
// open from 0 or close + open
adjustCollateral = adjustCollateral.add(perpetual.keeperGasReward).sub(oldMargin);
}
// make sure after adjust: trader is initial margin safe
adjustCollateral = adjustCollateral.max(
readonlyGetAvailableMargin(perpetual, trader, markPrice).neg()
);
}
// A readonly version of getMargin. This function was written post-audit. So there's a lot of repeated logic here.
function readonlyGetMargin(
PerpetualStorage storage perpetual,
MarginAccount memory account,
int256 price
) public view returns (int256 margin) {
margin = account.position.wmul(price).add(
account.cash.sub(account.position.wmul(perpetual.unitAccumulativeFunding))
);
}
// A readonly version of getAvailableMargin. This function was written post-audit. So there's a lot of repeated logic here.
function readonlyGetAvailableMargin(
PerpetualStorage storage perpetual,
MarginAccount memory account,
int256 price
) public view returns (int256 availableMargin) {
int256 threshold = account.position == 0
? 0 // was getInitialMargin
: account.position.wmul(price).wmul(perpetual.initialMarginRate).abs().add(
perpetual.keeperGasReward
);
// was getAvailableMargin
availableMargin = readonlyGetMargin(perpetual, account, price).sub(threshold);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @title SignedSafeMath
* @dev Signed math operations with safety checks that revert on error.
*/
library SignedSafeMathUpgradeable {
int256 constant private _INT256_MIN = -2**255;
/**
* @dev Returns the multiplication of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(int256 a, int256 b) internal pure returns (int256) {
// 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 0;
}
require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow");
int256 c = a * b;
require(c / a == b, "SignedSafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two signed integers. Reverts 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(int256 a, int256 b) internal pure returns (int256) {
require(b != 0, "SignedSafeMath: division by zero");
require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow");
int256 c = a / b;
return c;
}
/**
* @dev Returns the subtraction of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(int256 a, int256 b) internal pure returns (int256) {
int256 c = a - b;
require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");
return c;
}
/**
* @dev Returns the addition of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(int256 a, int256 b) internal pure returns (int256) {
int256 c = a + b;
require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");
return c;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCastUpgradeable {
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128) {
require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\'t fit in 128 bits");
return int128(value);
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64) {
require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\'t fit in 64 bits");
return int64(value);
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32) {
require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\'t fit in 32 bits");
return int32(value);
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16) {
require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\'t fit in 16 bits");
return int16(value);
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8) {
require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\'t fit in 8 bits");
return int8(value);
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
require(value < 2**255, "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
interface IAccessControl {
function grantPrivilege(address trader, uint256 privilege) external;
function revokePrivilege(address trader, uint256 privilege) external;
function isGranted(
address owner,
address trader,
uint256 privilege
) external view returns (bool);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IGovernor {
function initialize(
string memory name,
string memory symbol,
address minter,
address target_,
address rewardToken,
address poolCreator
) external;
function totalSupply() external view returns (uint256);
function getTarget() external view returns (address);
function mint(address account, uint256 amount) external;
function burn(address account, uint256 amount) external;
function balanceOf(address account) external view returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
import "./IAccessControl.sol";
import "./IPoolCreator.sol";
import "./ITracer.sol";
import "./IVersionControl.sol";
import "./IVariables.sol";
import "./IKeeperWhitelist.sol";
interface IPoolCreatorFull is
IPoolCreator,
IVersionControl,
ITracer,
IVariables,
IAccessControl,
IKeeperWhitelist
{
/**
* @notice Owner of version control.
*/
function owner() external view override(IVersionControl, IVariables) returns (address);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
interface ISymbolService {
function isWhitelistedFactory(address factory) external view returns (bool);
function addWhitelistedFactory(address factory) external;
function removeWhitelistedFactory(address factory) external;
function getPerpetualUID(uint256 symbol)
external
view
returns (address liquidityPool, uint256 perpetualIndex);
function getSymbols(address liquidityPool, uint256 perpetualIndex)
external
view
returns (uint256[] memory symbols);
function allocateSymbol(address liquidityPool, uint256 perpetualIndex) external;
function assignReservedSymbol(
address liquidityPool,
uint256 perpetualIndex,
uint256 symbol
) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import "./Constant.sol";
import "./Utils.sol";
enum Round {
CEIL,
FLOOR
}
library SafeMathExt {
using SafeMathUpgradeable for uint256;
using SignedSafeMathUpgradeable for int256;
/*
* @dev Always half up for uint256
*/
function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x.mul(y).add(Constant.UNSIGNED_ONE / 2) / Constant.UNSIGNED_ONE;
}
/*
* @dev Always half up for uint256
*/
function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x.mul(Constant.UNSIGNED_ONE).add(y / 2).div(y);
}
/*
* @dev Always half up for uint256
*/
function wfrac(
uint256 x,
uint256 y,
uint256 z
) internal pure returns (uint256 r) {
r = x.mul(y).add(z / 2).div(z);
}
/*
* @dev Always half up if no rounding parameter
*/
function wmul(int256 x, int256 y) internal pure returns (int256 z) {
z = roundHalfUp(x.mul(y), Constant.SIGNED_ONE) / Constant.SIGNED_ONE;
}
/*
* @dev Always half up if no rounding parameter
*/
function wdiv(int256 x, int256 y) internal pure returns (int256 z) {
if (y < 0) {
y = neg(y);
x = neg(x);
}
z = roundHalfUp(x.mul(Constant.SIGNED_ONE), y).div(y);
}
/*
* @dev Always half up if no rounding parameter
*/
function wfrac(
int256 x,
int256 y,
int256 z
) internal pure returns (int256 r) {
int256 t = x.mul(y);
if (z < 0) {
z = neg(z);
t = neg(t);
}
r = roundHalfUp(t, z).div(z);
}
function wmul(
int256 x,
int256 y,
Round round
) internal pure returns (int256 z) {
z = div(x.mul(y), Constant.SIGNED_ONE, round);
}
function wdiv(
int256 x,
int256 y,
Round round
) internal pure returns (int256 z) {
z = div(x.mul(Constant.SIGNED_ONE), y, round);
}
function wfrac(
int256 x,
int256 y,
int256 z,
Round round
) internal pure returns (int256 r) {
int256 t = x.mul(y);
r = div(t, z, round);
}
function abs(int256 x) internal pure returns (int256) {
return x >= 0 ? x : neg(x);
}
function neg(int256 a) internal pure returns (int256) {
return SignedSafeMathUpgradeable.sub(int256(0), a);
}
/*
* @dev ROUND_HALF_UP rule helper.
* You have to call roundHalfUp(x, y) / y to finish the rounding operation.
* 0.5 ≈ 1, 0.4 ≈ 0, -0.5 ≈ -1, -0.4 ≈ 0
*/
function roundHalfUp(int256 x, int256 y) internal pure returns (int256) {
require(y > 0, "roundHalfUp only supports y > 0");
if (x >= 0) {
return x.add(y / 2);
}
return x.sub(y / 2);
}
/*
* @dev Division, rounding ceil or rounding floor
*/
function div(
int256 x,
int256 y,
Round round
) internal pure returns (int256 divResult) {
require(y != 0, "division by zero");
divResult = x.div(y);
if (x % y == 0) {
return divResult;
}
bool isSameSign = Utils.hasTheSameSign(x, y);
if (round == Round.CEIL && isSameSign) {
divResult = divResult.add(1);
}
if (round == Round.FLOOR && !isSameSign) {
divResult = divResult.sub(1);
}
}
function max(int256 a, int256 b) internal pure returns (int256) {
return a >= b ? a : b;
}
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
import "../libraries/Utils.sol";
import "../Type.sol";
library OrderData {
uint32 internal constant MASK_CLOSE_ONLY = 0x80000000;
uint32 internal constant MASK_MARKET_ORDER = 0x40000000;
uint32 internal constant MASK_STOP_LOSS_ORDER = 0x20000000;
uint32 internal constant MASK_TAKE_PROFIT_ORDER = 0x10000000;
uint32 internal constant MASK_USE_TARGET_LEVERAGE = 0x08000000;
// old domain, will be removed in future
string internal constant DOMAIN_NAME = "Mai Protocol v3";
bytes32 internal constant EIP712_DOMAIN_TYPEHASH =
keccak256(abi.encodePacked("EIP712Domain(string name)"));
bytes32 internal constant DOMAIN_SEPARATOR =
keccak256(abi.encodePacked(EIP712_DOMAIN_TYPEHASH, keccak256(bytes(DOMAIN_NAME))));
bytes32 internal constant EIP712_ORDER_TYPE =
keccak256(
abi.encodePacked(
"Order(address trader,address broker,address relayer,address referrer,address liquidityPool,",
"int256 minTradeAmount,int256 amount,int256 limitPrice,int256 triggerPrice,uint256 chainID,",
"uint64 expiredAt,uint32 perpetualIndex,uint32 brokerFeeLimit,uint32 flags,uint32 salt)"
)
);
/*
* @dev Check if the order is close-only order. Close-only order means the order can only close position
* of the trader
* @param order The order object
* @return bool True if the order is close-only order
*/
function isCloseOnly(Order memory order) internal pure returns (bool) {
return (order.flags & MASK_CLOSE_ONLY) > 0;
}
/*
* @dev Check if the order is market order. Market order means the order which has no limit price, should be
* executed immediately
* @param order The order object
* @return bool True if the order is market order
*/
function isMarketOrder(Order memory order) internal pure returns (bool) {
return (order.flags & MASK_MARKET_ORDER) > 0;
}
/*
* @dev Check if the order is stop-loss order. Stop-loss order means the order will trigger when the
* price is worst than the trigger price
* @param order The order object
* @return bool True if the order is stop-loss order
*/
function isStopLossOrder(Order memory order) internal pure returns (bool) {
return (order.flags & MASK_STOP_LOSS_ORDER) > 0;
}
/*
* @dev Check if the order is take-profit order. Take-profit order means the order will trigger when
* the price is better than the trigger price
* @param order The order object
* @return bool True if the order is take-profit order
*/
function isTakeProfitOrder(Order memory order) internal pure returns (bool) {
return (order.flags & MASK_TAKE_PROFIT_ORDER) > 0;
}
/*
* @dev Check if the flags contain close-only flag
* @param flags The flags
* @return bool True if the flags contain close-only flag
*/
function isCloseOnly(uint32 flags) internal pure returns (bool) {
return (flags & MASK_CLOSE_ONLY) > 0;
}
/*
* @dev Check if the flags contain market flag
* @param flags The flags
* @return bool True if the flags contain market flag
*/
function isMarketOrder(uint32 flags) internal pure returns (bool) {
return (flags & MASK_MARKET_ORDER) > 0;
}
/*
* @dev Check if the flags contain stop-loss flag
* @param flags The flags
* @return bool True if the flags contain stop-loss flag
*/
function isStopLossOrder(uint32 flags) internal pure returns (bool) {
return (flags & MASK_STOP_LOSS_ORDER) > 0;
}
/*
* @dev Check if the flags contain take-profit flag
* @param flags The flags
* @return bool True if the flags contain take-profit flag
*/
function isTakeProfitOrder(uint32 flags) internal pure returns (bool) {
return (flags & MASK_TAKE_PROFIT_ORDER) > 0;
}
function oldUseTargetLeverage(uint32 flags) internal pure returns (bool) {
return (flags & MASK_USE_TARGET_LEVERAGE) > 0;
}
function newUseTargetLeverage(uint32 flags) internal pure returns (bool) {
return getTargetLeverageByFlags(flags) > 0;
}
function getTargetLeverageByFlags(uint32 flags) internal pure returns (int256) {
return int256((flags >> 7) & 0xfffff) * 10**16;
}
function useTargetLeverage(uint32 flags) internal pure returns (bool) {
bool _oldUseTargetLeverage = oldUseTargetLeverage(flags);
bool _newUseTargetLeverage = newUseTargetLeverage(flags);
require(!(_oldUseTargetLeverage && _newUseTargetLeverage), "invalid flags");
return _oldUseTargetLeverage || _newUseTargetLeverage;
}
/*
* @dev Get the hash of the order
* @param order The order object
* @return bytes32 The hash of the order
*/
function getOrderHash(Order memory order) internal pure returns (bytes32) {
bytes32 result = keccak256(abi.encode(EIP712_ORDER_TYPE, order));
return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, result));
}
/*
* @dev Decode the signature from the data
* @param data The data object to decode
* @return signature The signature
*/
function decodeSignature(bytes memory data) internal pure returns (bytes memory signature) {
require(data.length >= 350, "broken data");
bytes32 r;
bytes32 s;
uint8 v;
uint8 signType;
assembly {
r := mload(add(data, 318))
s := mload(add(data, 350))
v := byte(24, mload(add(data, 292)))
signType := byte(25, mload(add(data, 292)))
}
signature = abi.encodePacked(r, s, v, signType);
}
/*
* @dev Decode the order from the data
* @param data The data object to decode
* @return order The order
*/
function decodeOrderData(bytes memory data) internal pure returns (Order memory order) {
require(data.length >= 256, "broken data");
bytes32 tmp;
assembly {
// trader / 20
mstore(add(order, 0), mload(add(data, 20)))
// broker / 20
mstore(add(order, 32), mload(add(data, 40)))
// relayer / 20
mstore(add(order, 64), mload(add(data, 60)))
// referrer / 20
mstore(add(order, 96), mload(add(data, 80)))
// liquidityPool / 20
mstore(add(order, 128), mload(add(data, 100)))
// minTradeAmount / 32
mstore(add(order, 160), mload(add(data, 132)))
// amount / 32
mstore(add(order, 192), mload(add(data, 164)))
// limitPrice / 32
mstore(add(order, 224), mload(add(data, 196)))
// triggerPrice / 32
mstore(add(order, 256), mload(add(data, 228)))
// chainID / 32
mstore(add(order, 288), mload(add(data, 260)))
// expiredAt + perpetualIndex + brokerFeeLimit + flags + salt + v + signType / 26
tmp := mload(add(data, 292))
}
order.expiredAt = uint64(bytes8(tmp));
order.perpetualIndex = uint32(bytes4(tmp << 64));
order.brokerFeeLimit = uint32(bytes4(tmp << 96));
order.flags = uint32(bytes4(tmp << 128));
order.salt = uint32(bytes4(tmp << 160));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
import "@openzeppelin/contracts/utils/EnumerableSet.sol";
import "@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import "./SafeMathExt.sol";
library Utils {
using SafeMathExt for int256;
using SafeMathExt for uint256;
using SafeMathUpgradeable for uint256;
using SignedSafeMathUpgradeable for int256;
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.Bytes32Set;
/*
* @dev Check if two numbers have the same sign. Zero has the same sign with any number
*/
function hasTheSameSign(int256 x, int256 y) internal pure returns (bool) {
if (x == 0 || y == 0) {
return true;
}
return (x ^ y) >> 255 == 0;
}
/**
* @dev Check if the trader has opened position in the trade.
* Example: 2, 1 => true; 2, -1 => false; -2, -3 => true
* @param amount The position of the trader after the trade
* @param delta The update position amount of the trader after the trade
* @return True if the trader has opened position in the trade
*/
function hasOpenedPosition(int256 amount, int256 delta) internal pure returns (bool) {
if (amount == 0) {
return false;
}
return Utils.hasTheSameSign(amount, delta);
}
/*
* @dev Split the delta to two numbers.
* Use for splitting the trading amount to the amount to close position and the amount to open position.
* Examples: 2, 1 => 0, 1; 2, -1 => -1, 0; 2, -3 => -2, -1
*/
function splitAmount(int256 amount, int256 delta) internal pure returns (int256, int256) {
if (Utils.hasTheSameSign(amount, delta)) {
return (0, delta);
} else if (amount.abs() >= delta.abs()) {
return (delta, 0);
} else {
return (amount.neg(), amount.add(delta));
}
}
/*
* @dev Check if amount will be away from zero or cross zero if added the delta.
* Use for checking if trading amount will make trader open position.
* Example: 2, 1 => true; 2, -1 => false; 2, -3 => true
*/
function isOpen(int256 amount, int256 delta) internal pure returns (bool) {
return Utils.hasTheSameSign(amount, delta) || amount.abs() < delta.abs();
}
/*
* @dev Get the id of the current chain
*/
function chainID() internal pure returns (uint256 id) {
assembly {
id := chainid()
}
}
// function toArray(
// EnumerableSet.AddressSet storage set,
// uint256 begin,
// uint256 end
// ) internal view returns (address[] memory result) {
// require(end > begin, "begin should be lower than end");
// uint256 length = set.length();
// if (begin >= length) {
// return result;
// }
// uint256 safeEnd = end.min(length);
// result = new address[](safeEnd.sub(begin));
// for (uint256 i = begin; i < safeEnd; i++) {
// result[i.sub(begin)] = set.at(i);
// }
// return result;
// }
function toArray(
EnumerableSetUpgradeable.AddressSet storage set,
uint256 begin,
uint256 end
) internal view returns (address[] memory result) {
require(end > begin, "begin should be lower than end");
uint256 length = set.length();
if (begin >= length) {
return result;
}
uint256 safeEnd = end.min(length);
result = new address[](safeEnd.sub(begin));
for (uint256 i = begin; i < safeEnd; i++) {
result[i.sub(begin)] = set.at(i);
}
return result;
}
function toArray(
EnumerableSetUpgradeable.Bytes32Set storage set,
uint256 begin,
uint256 end
) internal view returns (bytes32[] memory result) {
require(end > begin, "begin should be lower than end");
uint256 length = set.length();
if (begin >= length) {
return result;
}
uint256 safeEnd = end.min(length);
result = new bytes32[](safeEnd.sub(begin));
for (uint256 i = begin; i < safeEnd; i++) {
result[i.sub(begin)] = set.at(i);
}
return result;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts-upgradeable/utils/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import "../libraries/Constant.sol";
import "../libraries/Math.sol";
import "../libraries/SafeMathExt.sol";
import "../libraries/Utils.sol";
import "../module/MarginAccountModule.sol";
import "../module/PerpetualModule.sol";
import "../Type.sol";
library AMMModule {
using Math for int256;
using SafeMathExt for int256;
using SignedSafeMathUpgradeable for int256;
using SafeCastUpgradeable for uint256;
using MarginAccountModule for PerpetualStorage;
using PerpetualModule for PerpetualStorage;
struct Context {
int256 indexPrice;
int256 position;
int256 positionValue;
// squareValue is 10^36, others are 10^18
int256 squareValue;
int256 positionMargin;
int256 availableCash;
}
/**
* @dev Get the trading result when trader trades with AMM, divided into two parts:
* - AMM closes its position
* - AMM opens its position.
*
* @param liquidityPool The liquidity pool object of AMM.
* @param perpetualIndex The index of the perpetual in the liquidity pool to trade.
* @param tradeAmount The trading amount of position, positive if AMM longs, negative if AMM shorts.
* @param partialFill Whether to allow partially trading. Set to true when liquidation trading,
* set to false when normal trading.
* @return deltaCash The update cash(collateral) of AMM after the trade.
* @return deltaPosition The update position of AMM after the trade.
*/
function queryTradeWithAMM(
LiquidityPoolStorage storage liquidityPool,
uint256 perpetualIndex,
int256 tradeAmount,
bool partialFill
) public view returns (int256 deltaCash, int256 deltaPosition) {
require(tradeAmount != 0, "trading amount is zero");
Context memory context = prepareContext(liquidityPool, perpetualIndex);
PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
(int256 closePosition, int256 openPosition) = Utils.splitAmount(
context.position,
tradeAmount
);
// AMM close position
int256 closeBestPrice;
(deltaCash, closeBestPrice) = ammClosePosition(context, perpetual, closePosition);
context.availableCash = context.availableCash.add(deltaCash);
context.position = context.position.add(closePosition);
// AMM open position
(int256 openDeltaCash, int256 openDeltaPosition, int256 openBestPrice) = ammOpenPosition(
context,
perpetual,
openPosition,
partialFill
);
deltaCash = deltaCash.add(openDeltaCash);
deltaPosition = closePosition.add(openDeltaPosition);
int256 bestPrice = closePosition != 0 ? closeBestPrice : openBestPrice;
// If price is better(for trader) than best price, change price to best price
deltaCash = deltaCash.max(bestPrice.wmul(deltaPosition).neg());
}
/**
* @dev Calculate the amount of share token to mint when liquidity provider adds liquidity to the liquidity pool.
* If adding liquidity at first time, which means total supply of share token is zero,
* the amount of share token to mint equals to the pool margin after adding liquidity.
*
* @param liquidityPool The liquidity pool object of AMM.
* @param shareTotalSupply The total supply of the share token before adding liquidity.
* @param cashToAdd The amount of cash(collateral) added to the liquidity pool.
* @return shareToMint The amount of share token to mint.
* @return addedPoolMargin The added amount of pool margin after adding liquidity.
*/
function getShareToMint(
LiquidityPoolStorage storage liquidityPool,
int256 shareTotalSupply,
int256 cashToAdd
) public view returns (int256 shareToMint, int256 addedPoolMargin) {
Context memory context = prepareContext(liquidityPool);
(int256 poolMargin, ) = getPoolMargin(context);
context.availableCash = context.availableCash.add(cashToAdd);
(int256 newPoolMargin, ) = getPoolMargin(context);
require(
liquidityPool.liquidityCap == 0 ||
newPoolMargin <= liquidityPool.liquidityCap.toInt256(),
"liquidity reaches cap"
);
addedPoolMargin = newPoolMargin.sub(poolMargin);
if (shareTotalSupply == 0) {
// first time, if there is pool margin left in pool, it belongs to the first person who adds liquidity
shareToMint = newPoolMargin;
} else {
// If share token's total supply is not zero and there is no money in pool,
// these share tokens have no value. This case should be avoided.
require(poolMargin > 0, "share token has no value");
shareToMint = newPoolMargin.sub(poolMargin).wfrac(shareTotalSupply, poolMargin);
}
}
/**
* @dev Calculate the amount of cash to add when liquidity provider adds liquidity to the liquidity pool.
* If adding liquidity at first time, which means total supply of share token is zero,
* the amount of cash to add equals to the share amount to mint minus pool margin before adding liquidity.
*
* @param liquidityPool The liquidity pool object of AMM.
* @param shareTotalSupply The total supply of the share token before adding liquidity.
* @param shareToMint The amount of share token to mint.
* @return cashToAdd The amount of cash(collateral) to add to the liquidity pool.
*/
function getCashToAdd(
LiquidityPoolStorage storage liquidityPool,
int256 shareTotalSupply,
int256 shareToMint
) public view returns (int256 cashToAdd) {
Context memory context = prepareContext(liquidityPool);
(int256 poolMargin, ) = getPoolMargin(context);
if (shareTotalSupply == 0) {
// first time, if there is pool margin left in pool, it belongs to the first person who adds liquidity
cashToAdd = shareToMint.sub(poolMargin).max(0);
int256 newPoolMargin = cashToAdd.add(poolMargin);
require(
liquidityPool.liquidityCap == 0 ||
newPoolMargin <= liquidityPool.liquidityCap.toInt256(),
"liquidity reaches cap"
);
} else {
// If share token's total supply is not zero and there is no money in pool,
// these share tokens have no value. This case should be avoided.
require(poolMargin > 0, "share token has no value");
int256 newPoolMargin = shareTotalSupply.add(shareToMint).wfrac(
poolMargin,
shareTotalSupply
);
require(
liquidityPool.liquidityCap == 0 ||
newPoolMargin <= liquidityPool.liquidityCap.toInt256(),
"liquidity reaches cap"
);
int256 minPoolMargin = context.squareValue.div(2).sqrt();
int256 newCash;
if (newPoolMargin <= minPoolMargin) {
// pool is still unsafe after adding liquidity
newCash = newPoolMargin.mul(2).sub(context.positionValue);
} else {
// context.squareValue is 10^36, so use div instead of wdiv
newCash = context.squareValue.div(newPoolMargin).div(2).add(newPoolMargin).sub(
context.positionValue
);
}
cashToAdd = newCash.sub(context.availableCash);
}
}
/**
* @dev Calculate the amount of cash(collateral) to return when liquidity provider removes liquidity from the liquidity pool.
* Removing liquidity is forbidden at several cases:
* 1. AMM is unsafe before removing liquidity
* 2. AMM is unsafe after removing liquidity
* 3. AMM will offer negative price at any perpetual after removing liquidity
* 4. AMM will exceed maximum leverage at any perpetual after removing liquidity
*
* @param liquidityPool The liquidity pool object of AMM.
* @param shareTotalSupply The total supply of the share token before removing liquidity.
* @param shareToRemove The amount of share token to redeem.
* @return cashToReturn The amount of cash(collateral) to return.
* @return removedInsuranceFund The part of insurance fund returned to LP if all perpetuals are in CLEARED state.
* @return removedDonatedInsuranceFund The part of donated insurance fund returned to LP if all perpetuals are in CLEARED state.
* @return removedPoolMargin The removed amount of pool margin after removing liquidity.
*/
function getCashToReturn(
LiquidityPoolStorage storage liquidityPool,
int256 shareTotalSupply,
int256 shareToRemove
)
public
view
returns (
int256 cashToReturn,
int256 removedInsuranceFund,
int256 removedDonatedInsuranceFund,
int256 removedPoolMargin
)
{
require(
shareTotalSupply > 0,
"total supply of share token is zero when removing liquidity"
);
Context memory context = prepareContext(liquidityPool);
require(isAMMSafe(context, 0), "AMM is unsafe before removing liquidity");
removedPoolMargin = calculatePoolMarginWhenSafe(context, 0);
require(removedPoolMargin > 0, "pool margin must be positive");
int256 poolMargin = shareTotalSupply.sub(shareToRemove).wfrac(
removedPoolMargin,
shareTotalSupply
);
removedPoolMargin = removedPoolMargin.sub(poolMargin);
{
int256 minPoolMargin = context.squareValue.div(2).sqrt();
require(poolMargin >= minPoolMargin, "AMM is unsafe after removing liquidity");
}
cashToReturn = calculateCashToReturn(context, poolMargin);
require(cashToReturn >= 0, "received margin is negative");
uint256 length = liquidityPool.perpetualCount;
bool allCleared = true;
for (uint256 i = 0; i < length; i++) {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
if (perpetual.state != PerpetualState.CLEARED) {
allCleared = false;
}
if (perpetual.state != PerpetualState.NORMAL) {
continue;
}
// prevent AMM offering negative price
require(
perpetual.getPosition(address(this)) <=
poolMargin.wdiv(perpetual.openSlippageFactor.value).wdiv(
perpetual.getIndexPrice()
),
"AMM is unsafe after removing liquidity"
);
}
// prevent AMM exceeding max leverage
require(
context.availableCash.add(context.positionValue).sub(cashToReturn) >=
context.positionMargin,
"AMM exceeds max leverage after removing liquidity"
);
if (allCleared) {
// get insurance fund proportionally
removedInsuranceFund = liquidityPool.insuranceFund.wfrac(
shareToRemove,
shareTotalSupply,
Round.FLOOR
);
removedDonatedInsuranceFund = liquidityPool.donatedInsuranceFund.wfrac(
shareToRemove,
shareTotalSupply,
Round.FLOOR
);
cashToReturn = cashToReturn.add(removedInsuranceFund).add(removedDonatedInsuranceFund);
}
}
/**
* @dev Calculate the amount of share token to redeem when liquidity provider removes liquidity from the liquidity pool.
* Removing liquidity is forbidden at several cases:
* 1. AMM is unsafe before removing liquidity
* 2. AMM is unsafe after removing liquidity
* 3. AMM will offer negative price at any perpetual after removing liquidity
* 4. AMM will exceed maximum leverage at any perpetual after removing liquidity
*
* @param liquidityPool The liquidity pool object of AMM.
* @param shareTotalSupply The total supply of the share token before removing liquidity.
* @param cashToReturn The cash(collateral) to return.
* @return shareToRemove The amount of share token to redeem.
* @return removedInsuranceFund The part of insurance fund returned to LP if all perpetuals are in CLEARED state.
* @return removedDonatedInsuranceFund The part of donated insurance fund returned to LP if all perpetuals are in CLEARED state.
* @return removedPoolMargin The removed amount of pool margin after removing liquidity.
*/
function getShareToRemove(
LiquidityPoolStorage storage liquidityPool,
int256 shareTotalSupply,
int256 cashToReturn
)
public
view
returns (
int256 shareToRemove,
int256 removedInsuranceFund,
int256 removedDonatedInsuranceFund,
int256 removedPoolMargin
)
{
require(
shareTotalSupply > 0,
"total supply of share token is zero when removing liquidity"
);
Context memory context = prepareContext(liquidityPool);
require(isAMMSafe(context, 0), "AMM is unsafe before removing liquidity");
int256 poolMargin = calculatePoolMarginWhenSafe(context, 0);
context.availableCash = context.availableCash.sub(cashToReturn);
require(isAMMSafe(context, 0), "AMM is unsafe after removing liquidity");
int256 newPoolMargin = calculatePoolMarginWhenSafe(context, 0);
removedPoolMargin = poolMargin.sub(newPoolMargin);
shareToRemove = poolMargin.sub(newPoolMargin).wfrac(shareTotalSupply, poolMargin);
uint256 length = liquidityPool.perpetualCount;
bool allCleared = true;
for (uint256 i = 0; i < length; i++) {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
if (perpetual.state != PerpetualState.CLEARED) {
allCleared = false;
}
if (perpetual.state != PerpetualState.NORMAL) {
continue;
}
// prevent AMM offering negative price
require(
perpetual.getPosition(address(this)) <=
newPoolMargin.wdiv(perpetual.openSlippageFactor.value).wdiv(
perpetual.getIndexPrice()
),
"AMM is unsafe after removing liquidity"
);
}
// prevent AMM exceeding max leverage
require(
context.availableCash.add(context.positionValue) >= context.positionMargin,
"AMM exceeds max leverage after removing liquidity"
);
if (allCleared) {
// get insurance fund proportionally
(
shareToRemove,
removedInsuranceFund,
removedDonatedInsuranceFund,
removedPoolMargin
) = getShareToRemoveWhenAllCleared(
liquidityPool,
cashToReturn,
poolMargin,
shareTotalSupply
);
}
}
/**
* @dev Calculate the amount of share token to redeem when liquidity provider removes liquidity from the liquidity pool.
* Only called when all perpetuals in the liquidity pool are in CLEARED state.
*
* @param liquidityPool The liquidity pool object of AMM.
* @param cashToReturn The cash(collateral) to return.
* @param poolMargin The pool margin before removing liquidity.
* @param shareTotalSupply The total supply of the share token before removing liquidity.
* @return shareToRemove The amount of share token to redeem.
* @return removedInsuranceFund The part of insurance fund returned to LP if all perpetuals are in CLEARED state.
* @return removedDonatedInsuranceFund The part of donated insurance fund returned to LP if all perpetuals are in CLEARED state.
* @return removedPoolMargin The part of pool margin returned to LP if all perpetuals are in CLEARED state.
*/
function getShareToRemoveWhenAllCleared(
LiquidityPoolStorage storage liquidityPool,
int256 cashToReturn,
int256 poolMargin,
int256 shareTotalSupply
)
public
view
returns (
int256 shareToRemove,
int256 removedInsuranceFund,
int256 removedDonatedInsuranceFund,
int256 removedPoolMargin
)
{
// get insurance fund proportionally
require(
poolMargin.add(liquidityPool.insuranceFund).add(liquidityPool.donatedInsuranceFund) > 0,
"all cleared, insufficient liquidity"
);
shareToRemove = shareTotalSupply.wfrac(
cashToReturn,
poolMargin.add(liquidityPool.insuranceFund).add(liquidityPool.donatedInsuranceFund)
);
removedInsuranceFund = liquidityPool.insuranceFund.wfrac(
shareToRemove,
shareTotalSupply,
Round.FLOOR
);
removedDonatedInsuranceFund = liquidityPool.donatedInsuranceFund.wfrac(
shareToRemove,
shareTotalSupply,
Round.FLOOR
);
removedPoolMargin = poolMargin.wfrac(shareToRemove, shareTotalSupply, Round.FLOOR);
}
/**
* @dev Calculate the pool margin of AMM when AMM is safe.
* Pool margin is how much collateral of the pool considering the AMM's positions of perpetuals.
*
* @param context Context object of AMM, but current perpetual is not included.
* @param slippageFactor The slippage factor of current perpetual.
* @return poolMargin The pool margin of AMM.
*/
function calculatePoolMarginWhenSafe(Context memory context, int256 slippageFactor)
internal
pure
returns (int256 poolMargin)
{
// The context doesn't include the current perpetual, add them.
int256 positionValue = context.indexPrice.wmul(context.position);
int256 margin = positionValue.add(context.positionValue).add(context.availableCash);
// 10^36, the same as context.squareValue
int256 tmp = positionValue.wmul(positionValue).mul(slippageFactor).add(context.squareValue);
int256 beforeSqrt = margin.mul(margin).sub(tmp.mul(2));
require(beforeSqrt >= 0, "AMM is unsafe when calculating pool margin");
poolMargin = beforeSqrt.sqrt().add(margin).div(2);
require(poolMargin >= 0, "pool margin is negative when calculating pool margin");
}
/**
* @dev Check if AMM is safe
* @param context Context object of AMM, but current perpetual is not included.
* @param slippageFactor The slippage factor of current perpetual.
* @return bool True if AMM is safe.
*/
function isAMMSafe(Context memory context, int256 slippageFactor) internal pure returns (bool) {
int256 positionValue = context.indexPrice.wmul(context.position);
// 10^36, the same as context.squareValue
int256 minAvailableCash = positionValue.wmul(positionValue).mul(slippageFactor);
minAvailableCash = minAvailableCash.add(context.squareValue).mul(2).sqrt().sub(
context.positionValue.add(positionValue)
);
return context.availableCash >= minAvailableCash;
}
/**
* @dev Get the trading result when AMM closes its position.
* If the AMM is unsafe, the trading price is the best price.
* If trading price is too bad, it will be limited to index price * (1 +/- max close price discount)
*
* @param context Context object of AMM, but current perpetual is not included.
* @param perpetual The perpetual object to trade.
* @param tradeAmount The amount of position to trade.
* Positive for long and negative for short from AMM's perspective.
* @return deltaCash The update cash(collateral) of AMM after the trade.
* @return bestPrice The best price, is used for clipping to spread price if needed outside.
* If AMM is safe, best price = middle price * (1 +/- half spread).
* If AMM is unsafe and normal case, best price = index price.
*/
function ammClosePosition(
Context memory context,
PerpetualStorage storage perpetual,
int256 tradeAmount
) internal view returns (int256 deltaCash, int256 bestPrice) {
if (tradeAmount == 0) {
return (0, 0);
}
int256 positionBefore = context.position;
int256 indexPrice = context.indexPrice;
int256 slippageFactor = perpetual.closeSlippageFactor.value;
int256 maxClosePriceDiscount = perpetual.maxClosePriceDiscount.value;
int256 halfSpread = tradeAmount < 0
? perpetual.halfSpread.value
: perpetual.halfSpread.value.neg();
if (isAMMSafe(context, slippageFactor)) {
int256 poolMargin = calculatePoolMarginWhenSafe(context, slippageFactor);
require(poolMargin > 0, "pool margin must be positive");
bestPrice = getMidPrice(poolMargin, indexPrice, positionBefore, slippageFactor).wmul(
halfSpread.add(Constant.SIGNED_ONE)
);
deltaCash = getDeltaCash(
poolMargin,
positionBefore,
positionBefore.add(tradeAmount),
indexPrice,
slippageFactor
);
} else {
bestPrice = indexPrice;
deltaCash = bestPrice.wmul(tradeAmount).neg();
}
int256 priceLimit = tradeAmount > 0
? Constant.SIGNED_ONE.add(maxClosePriceDiscount)
: Constant.SIGNED_ONE.sub(maxClosePriceDiscount);
// prevent too bad price
deltaCash = deltaCash.max(indexPrice.wmul(priceLimit).wmul(tradeAmount).neg());
// prevent negative price
require(
!Utils.hasTheSameSign(deltaCash, tradeAmount),
"price is negative when AMM closes position"
);
}
/**
* @dev Get the trading result when AMM opens its position.
* AMM can't open position when unsafe and can't open position to exceed the maximum position
*
* @param context Context object of AMM, but current perpetual is not included.
* @param perpetual The perpetual object to trade
* @param tradeAmount The trading amount of position, positive if AMM longs, negative if AMM shorts
* @param partialFill Whether to allow partially trading. Set to true when liquidation trading,
* set to false when normal trading
* @return deltaCash The update cash(collateral) of AMM after the trade
* @return deltaPosition The update position of AMM after the trade
* @return bestPrice The best price, is used for clipping to spread price if needed outside.
* Equal to middle price * (1 +/- half spread)
*/
function ammOpenPosition(
Context memory context,
PerpetualStorage storage perpetual,
int256 tradeAmount,
bool partialFill
)
internal
view
returns (
int256 deltaCash,
int256 deltaPosition,
int256 bestPrice
)
{
if (tradeAmount == 0) {
return (0, 0, 0);
}
int256 slippageFactor = perpetual.openSlippageFactor.value;
if (!isAMMSafe(context, slippageFactor)) {
require(partialFill, "AMM is unsafe when open");
return (0, 0, 0);
}
int256 poolMargin = calculatePoolMarginWhenSafe(context, slippageFactor);
require(poolMargin > 0, "pool margin must be positive");
int256 indexPrice = context.indexPrice;
int256 positionBefore = context.position;
int256 positionAfter = positionBefore.add(tradeAmount);
int256 maxPosition = getMaxPosition(
context,
poolMargin,
perpetual.ammMaxLeverage.value,
slippageFactor,
positionAfter > 0
);
if (positionAfter.abs() > maxPosition.abs()) {
require(partialFill, "trade amount exceeds max amount");
// trade to max position if partialFill
deltaPosition = maxPosition.sub(positionBefore);
// current position already exeeds max position before trade, can't open
if (Utils.hasTheSameSign(deltaPosition, tradeAmount.neg())) {
return (0, 0, 0);
}
positionAfter = maxPosition;
} else {
deltaPosition = tradeAmount;
}
deltaCash = getDeltaCash(
poolMargin,
positionBefore,
positionAfter,
indexPrice,
slippageFactor
);
// prevent negative price
require(
!Utils.hasTheSameSign(deltaCash, deltaPosition),
"price is negative when AMM opens position"
);
int256 halfSpread = tradeAmount < 0
? perpetual.halfSpread.value
: perpetual.halfSpread.value.neg();
bestPrice = getMidPrice(poolMargin, indexPrice, positionBefore, slippageFactor).wmul(
halfSpread.add(Constant.SIGNED_ONE)
);
}
/**
* @dev Calculate the status of AMM
*
* @param liquidityPool The reference of liquidity pool storage.
* @return context Context object of AMM, but current perpetual is not included.
*/
function prepareContext(LiquidityPoolStorage storage liquidityPool)
internal
view
returns (Context memory context)
{
context = prepareContext(liquidityPool, liquidityPool.perpetualCount);
}
/**
* @dev Calculate the status of AMM, but specified perpetual index is not included.
*
* @param liquidityPool The reference of liquidity pool storage.
* @param perpetualIndex The index of the perpetual in the liquidity pool to distinguish,
* set to liquidityPool.perpetualCount to skip distinguishing.
* @return context Context object of AMM.
*/
function prepareContext(LiquidityPoolStorage storage liquidityPool, uint256 perpetualIndex)
internal
view
returns (Context memory context)
{
int256 maintenanceMargin;
uint256 length = liquidityPool.perpetualCount;
for (uint256 i = 0; i < length; i++) {
PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
// only involve normal market
if (perpetual.state != PerpetualState.NORMAL) {
continue;
}
int256 position = perpetual.getPosition(address(this));
int256 indexPrice = perpetual.getIndexPrice();
require(indexPrice > 0, "index price must be positive");
context.availableCash = context.availableCash.add(
perpetual.getAvailableCash(address(this))
);
maintenanceMargin = maintenanceMargin.add(
indexPrice.wmul(position).wmul(perpetual.maintenanceMarginRate).abs()
);
if (i == perpetualIndex) {
context.indexPrice = indexPrice;
context.position = position;
} else {
// To avoid returning more cash than pool has because of precision error,
// cashToReturn should be smaller, which means positionValue should be smaller, squareValue should be bigger
context.positionValue = context.positionValue.add(
indexPrice.wmul(position, Round.FLOOR)
);
// 10^36
context.squareValue = context.squareValue.add(
position
.wmul(position, Round.CEIL)
.wmul(indexPrice, Round.CEIL)
.wmul(indexPrice, Round.CEIL)
.mul(perpetual.openSlippageFactor.value)
);
context.positionMargin = context.positionMargin.add(
indexPrice.wmul(position).abs().wdiv(perpetual.ammMaxLeverage.value)
);
}
}
context.availableCash = context.availableCash.add(liquidityPool.poolCash);
// prevent margin balance < maintenance margin.
// call setEmergencyState(SET_ALL_PERPETUALS_TO_EMERGENCY_STATE) when AMM is maintenance margin unsafe
require(
context.availableCash.add(context.positionValue).add(
context.indexPrice.wmul(context.position)
) >= maintenanceMargin,
"AMM is mm unsafe"
);
}
/**
* @dev Calculate the cash(collateral) to return when removing liquidity.
*
* @param context Context object of AMM, but current perpetual is not included.
* @param poolMargin The pool margin of AMM before removing liquidity.
* @return cashToReturn The cash(collateral) to return.
*/
function calculateCashToReturn(Context memory context, int256 poolMargin)
public
pure
returns (int256 cashToReturn)
{
if (poolMargin == 0) {
// remove all
return context.availableCash;
}
require(poolMargin > 0, "pool margin must be positive when removing liquidity");
// context.squareValue is 10^36, so use div instead of wdiv
cashToReturn = context.squareValue.div(poolMargin).div(2).add(poolMargin).sub(
context.positionValue
);
cashToReturn = context.availableCash.sub(cashToReturn);
}
/**
* @dev Get the middle price offered by AMM
*
* @param poolMargin The pool margin of AMM.
* @param indexPrice The index price of the perpetual.
* @param position The position of AMM in the perpetual.
* @param slippageFactor The slippage factor of AMM in the perpetual.
* @return midPrice A middle price offered by AMM.
*/
function getMidPrice(
int256 poolMargin,
int256 indexPrice,
int256 position,
int256 slippageFactor
) internal pure returns (int256 midPrice) {
midPrice = Constant
.SIGNED_ONE
.sub(indexPrice.wmul(position).wfrac(slippageFactor, poolMargin))
.wmul(indexPrice);
}
/**
* @dev Get update cash(collateral) of AMM if trader trades against AMM.
*
* @param poolMargin The pool margin of AMM.
* @param positionBefore The position of AMM in the perpetual before trading.
* @param positionAfter The position of AMM in the perpetual after trading.
* @param indexPrice The index price of the perpetual.
* @param slippageFactor The slippage factor of AMM in the perpetual.
* @return deltaCash The update cash(collateral) of AMM after trading.
*/
function getDeltaCash(
int256 poolMargin,
int256 positionBefore,
int256 positionAfter,
int256 indexPrice,
int256 slippageFactor
) internal pure returns (int256 deltaCash) {
deltaCash = positionAfter.add(positionBefore).wmul(indexPrice).div(2).wfrac(
slippageFactor,
poolMargin
);
deltaCash = Constant.SIGNED_ONE.sub(deltaCash).wmul(indexPrice).wmul(
positionBefore.sub(positionAfter)
);
}
/**
* @dev Get the max position of AMM in the perpetual when AMM is opening position, calculated by three restrictions:
* 1. AMM must be safe after the trade.
* 2. AMM mustn't exceed maximum leverage in any perpetual after the trade.
* 3. AMM must offer positive price in any perpetual after the trade. It's easy to prove that, in the
* perpetual, AMM definitely offers positive price when AMM holds short position.
*
* @param context Context object of AMM, but current perpetual is not included.
* @param poolMargin The pool margin of AMM.
* @param ammMaxLeverage The max leverage of AMM in the perpetual.
* @param slippageFactor The slippage factor of AMM in the perpetual.
* @return maxPosition The max position of AMM in the perpetual.
*/
function getMaxPosition(
Context memory context,
int256 poolMargin,
int256 ammMaxLeverage,
int256 slippageFactor,
bool isLongSide
) internal pure returns (int256 maxPosition) {
int256 indexPrice = context.indexPrice;
int256 beforeSqrt = poolMargin.mul(poolMargin).mul(2).sub(context.squareValue).wdiv(
slippageFactor
);
if (beforeSqrt <= 0) {
// 1. already unsafe, can't open position
// 2. initial AMM is also this case, position = 0, available cash = 0, pool margin = 0
return 0;
}
int256 maxPosition3 = beforeSqrt.sqrt().wdiv(indexPrice);
int256 maxPosition2;
// context.squareValue is 10^36, so use div instead of wdiv
beforeSqrt = poolMargin.sub(context.positionMargin).add(
context.squareValue.div(poolMargin).div(2)
);
beforeSqrt = beforeSqrt.wmul(ammMaxLeverage).wmul(ammMaxLeverage).wmul(slippageFactor);
beforeSqrt = poolMargin.sub(beforeSqrt.mul(2));
if (beforeSqrt < 0) {
// never exceed max leverage
maxPosition2 = type(int256).max;
} else {
// might be negative, clip to zero
maxPosition2 = poolMargin.sub(beforeSqrt.mul(poolMargin).sqrt()).max(0);
maxPosition2 = maxPosition2.wdiv(ammMaxLeverage).wdiv(slippageFactor).wdiv(indexPrice);
}
maxPosition = maxPosition3.min(maxPosition2);
if (isLongSide) {
// long side has one more restriction than short side
int256 maxPosition1 = poolMargin.wdiv(slippageFactor).wdiv(indexPrice);
maxPosition = maxPosition.min(maxPosition1);
} else {
maxPosition = maxPosition.neg();
}
}
/**
* @dev Get pool margin of AMM, equal to 1/2 margin of AMM when AMM is unsafe.
* Marin of AMM: cash + index price1 * position1 + index price2 * position2 + ...
*
* @param context Context object of AMM, but current perpetual is not included.
* @return poolMargin The pool margin of AMM.
* @return isSafe True if AMM is safe or false.
*/
function getPoolMargin(Context memory context)
internal
pure
returns (int256 poolMargin, bool isSafe)
{
isSafe = isAMMSafe(context, 0);
if (isSafe) {
poolMargin = calculatePoolMarginWhenSafe(context, 0);
} else {
poolMargin = context.availableCash.add(context.positionValue).div(2);
require(poolMargin >= 0, "pool margin is negative when getting pool margin");
}
}
/**
* @dev Get pool margin of AMM, prepare context first.
* @param liquidityPool The liquidity pool object
* @return int256 The pool margin of AMM
* @return bool True if AMM is safe
*/
function getPoolMargin(LiquidityPoolStorage storage liquidityPool)
public
view
returns (int256, bool)
{
return getPoolMargin(prepareContext(liquidityPool));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/SafeCastUpgradeable.sol";
import "../interface/IDecimals.sol";
import "../libraries/Constant.sol";
import "../Type.sol";
/**
* @title Collateral Module
* @dev Handle underlying collaterals.
* In this file, parameter named with:
* - [amount] means internal amount
* - [rawAmount] means amount in decimals of underlying collateral
*/
library CollateralModule {
using SafeMathUpgradeable for uint256;
using SafeCastUpgradeable for int256;
using SafeCastUpgradeable for uint256;
using SignedSafeMathUpgradeable for int256;
using SafeERC20Upgradeable for IERC20Upgradeable;
uint256 internal constant SYSTEM_DECIMALS = 18;
/**
* @notice Initialize the collateral of the liquidity pool. Set up address, scaler and decimals of collateral
* @param liquidityPool The liquidity pool object
* @param collateral The address of the collateral
* @param collateralDecimals The decimals of the collateral, must less than SYSTEM_DECIMALS,
* must equal to decimals() if the function exists
*/
function initializeCollateral(
LiquidityPoolStorage storage liquidityPool,
address collateral,
uint256 collateralDecimals
) public {
require(collateralDecimals <= SYSTEM_DECIMALS, "collateral decimals is out of range");
try IDecimals(collateral).decimals() returns (uint8 decimals) {
require(decimals == collateralDecimals, "decimals not match");
} catch {}
uint256 factor = 10**(SYSTEM_DECIMALS.sub(collateralDecimals));
liquidityPool.scaler = (factor == 0 ? 1 : factor);
liquidityPool.collateralToken = collateral;
liquidityPool.collateralDecimals = collateralDecimals;
}
/**
* @notice Transfer collateral from the account to the liquidity pool.
* @param liquidityPool The liquidity pool object
* @param account The address of the account
* @param amount The amount of erc20 token to transfer. Always use decimals 18.
*/
function transferFromUser(
LiquidityPoolStorage storage liquidityPool,
address account,
int256 amount
) public {
if (amount <= 0) {
return;
}
uint256 rawAmount = _toRawAmountRoundUp(liquidityPool, amount);
IERC20Upgradeable collateralToken = IERC20Upgradeable(liquidityPool.collateralToken);
uint256 previousBalance = collateralToken.balanceOf(address(this));
collateralToken.safeTransferFrom(account, address(this), rawAmount);
uint256 postBalance = collateralToken.balanceOf(address(this));
require(postBalance.sub(previousBalance) == rawAmount, "incorrect transferred in amount");
}
/**
* @notice Transfer collateral from the liquidity pool to the account.
* @param liquidityPool The liquidity pool object
* @param account The address of the account
* @param amount The amount of collateral to transfer. always use decimals 18.
*/
function transferToUser(
LiquidityPoolStorage storage liquidityPool,
address account,
int256 amount
) public {
if (amount <= 0) {
return;
}
uint256 rawAmount = _toRawAmount(liquidityPool, amount);
IERC20Upgradeable collateralToken = IERC20Upgradeable(liquidityPool.collateralToken);
uint256 previousBalance = collateralToken.balanceOf(address(this));
collateralToken.safeTransfer(account, rawAmount);
uint256 postBalance = collateralToken.balanceOf(address(this));
require(previousBalance.sub(postBalance) == rawAmount, "incorrect transferred out amount");
}
function _toRawAmount(LiquidityPoolStorage storage liquidityPool, int256 amount)
private
view
returns (uint256 rawAmount)
{
rawAmount = amount.toUint256().div(liquidityPool.scaler);
}
function _toRawAmountRoundUp(LiquidityPoolStorage storage liquidityPool, int256 amount)
private
view
returns (uint256 rawAmount)
{
rawAmount = amount.toUint256();
rawAmount = rawAmount.div(liquidityPool.scaler).add(
rawAmount % liquidityPool.scaler > 0 ? 1 : 0
);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/SafeCastUpgradeable.sol";
import "../libraries/SafeMathExt.sol";
import "../libraries/Utils.sol";
import "../libraries/OrderData.sol";
import "../Type.sol";
library MarginAccountModule {
using SafeMathExt for int256;
using SafeCastUpgradeable for uint256;
using SignedSafeMathUpgradeable for int256;
using OrderData for uint32;
/**
* @dev Get the initial margin of the trader in the perpetual.
* Initial margin = price * abs(position) * initial margin rate
* @param perpetual The perpetual object
* @param trader The address of the trader
* @param price The price to calculate the initial margin
* @return initialMargin The initial margin of the trader in the perpetual
*/
function getInitialMargin(
PerpetualStorage storage perpetual,
address trader,
int256 price
) internal view returns (int256 initialMargin) {
initialMargin = perpetual
.marginAccounts[trader]
.position
.wmul(price)
.wmul(perpetual.initialMarginRate)
.abs();
}
/**
* @dev Get the maintenance margin of the trader in the perpetual.
* Maintenance margin = price * abs(position) * maintenance margin rate
* @param perpetual The perpetual object
* @param trader The address of the trader
* @param price The price to calculate the maintenance margin
* @return maintenanceMargin The maintenance margin of the trader in the perpetual
*/
function getMaintenanceMargin(
PerpetualStorage storage perpetual,
address trader,
int256 price
) internal view returns (int256 maintenanceMargin) {
maintenanceMargin = perpetual
.marginAccounts[trader]
.position
.wmul(price)
.wmul(perpetual.maintenanceMarginRate)
.abs();
}
/**
* @dev Get the available cash of the trader in the perpetual.
* Available cash = cash - position * unit accumulative funding
* @param perpetual The perpetual object
* @param trader The address of the trader
* @return availableCash The available cash of the trader in the perpetual
*/
function getAvailableCash(PerpetualStorage storage perpetual, address trader)
internal
view
returns (int256 availableCash)
{
MarginAccount storage account = perpetual.marginAccounts[trader];
availableCash = account.cash.sub(account.position.wmul(perpetual.unitAccumulativeFunding));
}
/**
* @dev Get the position of the trader in the perpetual
* @param perpetual The perpetual object
* @param trader The address of the trader
* @return position The position of the trader in the perpetual
*/
function getPosition(PerpetualStorage storage perpetual, address trader)
internal
view
returns (int256 position)
{
position = perpetual.marginAccounts[trader].position;
}
/**
* @dev Get the margin of the trader in the perpetual.
* Margin = available cash + position * price
* @param perpetual The perpetual object
* @param trader The address of the trader
* @param price The price to calculate the margin
* @return margin The margin of the trader in the perpetual
*/
function getMargin(
PerpetualStorage storage perpetual,
address trader,
int256 price
) internal view returns (int256 margin) {
margin = perpetual.marginAccounts[trader].position.wmul(price).add(
getAvailableCash(perpetual, trader)
);
}
/**
* @dev Get the settleable margin of the trader in the perpetual.
* This is the margin trader can withdraw when the state of the perpetual is "CLEARED".
* If the state of the perpetual is not "CLEARED", the settleable margin is always zero
* @param perpetual The perpetual object
* @param trader The address of the trader
* @param price The price to calculate the settleable margin
* @return margin The settleable margin of the trader in the perpetual
*/
function getSettleableMargin(
PerpetualStorage storage perpetual,
address trader,
int256 price
) internal view returns (int256 margin) {
margin = getMargin(perpetual, trader, price);
if (margin > 0) {
int256 rate = (getPosition(perpetual, trader) == 0)
? perpetual.redemptionRateWithoutPosition
: perpetual.redemptionRateWithPosition;
// make sure total redemption margin < total collateral of perpetual
margin = margin.wmul(rate, Round.FLOOR);
} else {
margin = 0;
}
}
/**
* @dev Get the available margin of the trader in the perpetual.
* Available margin = margin - (initial margin + keeper gas reward), keeper gas reward = 0 if position = 0
* @param perpetual The perpetual object
* @param trader The address of the trader
* @param price The price to calculate available margin
* @return availableMargin The available margin of the trader in the perpetual
*/
function getAvailableMargin(
PerpetualStorage storage perpetual,
address trader,
int256 price
) internal view returns (int256 availableMargin) {
int256 threshold = getPosition(perpetual, trader) == 0
? 0
: getInitialMargin(perpetual, trader, price).add(perpetual.keeperGasReward);
availableMargin = getMargin(perpetual, trader, price).sub(threshold);
}
/**
* @dev Check if the trader is initial margin safe in the perpetual, which means available margin >= 0
* @param perpetual The perpetual object
* @param trader The address of the trader
* @param price The price to calculate the available margin
* @return isSafe True if the trader is initial margin safe in the perpetual
*/
function isInitialMarginSafe(
PerpetualStorage storage perpetual,
address trader,
int256 price
) internal view returns (bool isSafe) {
isSafe = (getAvailableMargin(perpetual, trader, price) >= 0);
}
/**
* @dev Check if the trader is maintenance margin safe in the perpetual, which means
* margin >= maintenance margin + keeper gas reward. Keeper gas reward = 0 if position = 0
* @param perpetual The perpetual object
* @param trader The address of the trader
* @param price The price to calculate the maintenance margin
* @return isSafe True if the trader is maintenance margin safe in the perpetual
*/
function isMaintenanceMarginSafe(
PerpetualStorage storage perpetual,
address trader,
int256 price
) internal view returns (bool isSafe) {
int256 threshold = getPosition(perpetual, trader) == 0
? 0
: getMaintenanceMargin(perpetual, trader, price).add(perpetual.keeperGasReward);
isSafe = getMargin(perpetual, trader, price) >= threshold;
}
/**
* @dev Check if the trader is margin safe in the perpetual, which means margin >= keeper gas reward.
* Keeper gas reward = 0 if position = 0
* @param perpetual The perpetual object
* @param trader The address of the trader
* @param price The price to calculate the margin
* @return isSafe True if the trader is margin safe in the perpetual
*/
function isMarginSafe(
PerpetualStorage storage perpetual,
address trader,
int256 price
) internal view returns (bool isSafe) {
int256 threshold = getPosition(perpetual, trader) == 0 ? 0 : perpetual.keeperGasReward;
isSafe = getMargin(perpetual, trader, price) >= threshold;
}
/**
* @dev Check if the account of the trader is empty in the perpetual, which means cash = 0 and position = 0
* @param perpetual The perpetual object
* @param trader The address of the trader
* @return isEmpty True if the account of the trader is empty in the perpetual
*/
function isEmptyAccount(PerpetualStorage storage perpetual, address trader)
internal
view
returns (bool isEmpty)
{
MarginAccount storage account = perpetual.marginAccounts[trader];
isEmpty = (account.cash == 0 && account.position == 0);
}
/**
* @dev Update the trader's cash in the perpetual
* @param perpetual The perpetual object
* @param trader The address of the trader
* @param deltaCash The update cash(collateral) of the trader's account in the perpetual
*/
function updateCash(
PerpetualStorage storage perpetual,
address trader,
int256 deltaCash
) internal {
if (deltaCash == 0) {
return;
}
MarginAccount storage account = perpetual.marginAccounts[trader];
account.cash = account.cash.add(deltaCash);
}
/**
* @dev Update the trader's account in the perpetual
* @param perpetual The perpetual object
* @param trader The address of the trader
* @param deltaPosition The update position of the trader's account in the perpetual
* @param deltaCash The update cash(collateral) of the trader's account in the perpetual
*/
function updateMargin(
PerpetualStorage storage perpetual,
address trader,
int256 deltaPosition,
int256 deltaCash
) internal returns (int256 deltaOpenInterest) {
MarginAccount storage account = perpetual.marginAccounts[trader];
int256 oldPosition = account.position;
account.position = account.position.add(deltaPosition);
account.cash = account.cash.add(deltaCash).add(
perpetual.unitAccumulativeFunding.wmul(deltaPosition)
);
if (oldPosition > 0) {
deltaOpenInterest = oldPosition.neg();
}
if (account.position > 0) {
deltaOpenInterest = deltaOpenInterest.add(account.position);
}
perpetual.openInterest = perpetual.openInterest.add(deltaOpenInterest);
}
/**
* @dev Reset the trader's account in the perpetual to empty, which means position = 0 and cash = 0
* @param perpetual The perpetual object
* @param trader The address of the trader
*/
function resetAccount(PerpetualStorage storage perpetual, address trader) internal {
MarginAccount storage account = perpetual.marginAccounts[trader];
account.cash = 0;
account.position = 0;
}
// deprecated
function setTargetLeverage(
PerpetualStorage storage perpetual,
address trader,
int256 targetLeverage
) internal {
perpetual.marginAccounts[trader].targetLeverage = targetLeverage;
}
function getTargetLeverage(PerpetualStorage storage perpetual, address trader)
internal
view
returns (int256)
{
require(perpetual.initialMarginRate != 0, "initialMarginRate is not set");
int256 maxLeverage = Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate);
int256 targetLeverage = perpetual.marginAccounts[trader].targetLeverage;
targetLeverage = targetLeverage == 0
? perpetual.defaultTargetLeverage.value
: targetLeverage;
return targetLeverage.min(maxLeverage);
}
function getTargetLeverageWithFlags(
PerpetualStorage storage perpetual,
address trader,
uint32 flags
) internal view returns (int256 targetLeverage) {
require(perpetual.initialMarginRate != 0, "initialMarginRate is not set");
int256 maxLeverage = Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate);
bool _oldUseTargetLeverage = flags.oldUseTargetLeverage();
bool _newUseTargetLeverage = flags.newUseTargetLeverage();
require(!(_oldUseTargetLeverage && _newUseTargetLeverage), "invalid flags");
if (_oldUseTargetLeverage) {
targetLeverage = perpetual.marginAccounts[trader].targetLeverage;
} else {
targetLeverage = flags.getTargetLeverageByFlags();
}
targetLeverage = targetLeverage == 0
? perpetual.defaultTargetLeverage.value
: targetLeverage;
targetLeverage = targetLeverage.min(maxLeverage);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/SafeCastUpgradeable.sol";
import "../interface/IOracle.sol";
import "../libraries/SafeMathExt.sol";
import "../libraries/Utils.sol";
import "../Type.sol";
import "./MarginAccountModule.sol";
library PerpetualModule {
using SafeMathUpgradeable for uint256;
using SafeMathExt for int256;
using SafeCastUpgradeable for int256;
using SafeCastUpgradeable for uint256;
using AddressUpgradeable for address;
using SignedSafeMathUpgradeable for int256;
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
using MarginAccountModule for PerpetualStorage;
int256 constant FUNDING_INTERVAL = 3600 * 8;
uint256 internal constant INDEX_INITIAL_MARGIN_RATE = 0;
uint256 internal constant INDEX_MAINTENANCE_MARGIN_RATE = 1;
uint256 internal constant INDEX_OPERATOR_FEE_RATE = 2;
uint256 internal constant INDEX_LP_FEE_RATE = 3;
uint256 internal constant INDEX_REFERRAL_REBATE_RATE = 4;
uint256 internal constant INDEX_LIQUIDATION_PENALTY_RATE = 5;
uint256 internal constant INDEX_KEEPER_GAS_REWARD = 6;
uint256 internal constant INDEX_INSURANCE_FUND_RATE = 7;
uint256 internal constant INDEX_MAX_OPEN_INTEREST_RATE = 8;
uint256 internal constant INDEX_HALF_SPREAD = 0;
uint256 internal constant INDEX_OPEN_SLIPPAGE_FACTOR = 1;
uint256 internal constant INDEX_CLOSE_SLIPPAGE_FACTOR = 2;
uint256 internal constant INDEX_FUNDING_RATE_LIMIT = 3;
uint256 internal constant INDEX_AMM_MAX_LEVERAGE = 4;
uint256 internal constant INDEX_AMM_CLOSE_PRICE_DISCOUNT = 5;
uint256 internal constant INDEX_FUNDING_RATE_FACTOR = 6;
uint256 internal constant INDEX_DEFAULT_TARGET_LEVERAGE = 7;
uint256 internal constant INDEX_BASE_FUNDING_RATE = 8;
event Deposit(uint256 perpetualIndex, address indexed trader, int256 amount);
event Withdraw(uint256 perpetualIndex, address indexed trader, int256 amount);
event Clear(uint256 perpetualIndex, address indexed trader);
event Settle(uint256 perpetualIndex, address indexed trader, int256 amount);
event SetNormalState(uint256 perpetualIndex);
event SetEmergencyState(uint256 perpetualIndex, int256 settlementPrice, uint256 settlementTime);
event SetClearedState(uint256 perpetualIndex);
event UpdateUnitAccumulativeFunding(uint256 perpetualIndex, int256 unitAccumulativeFunding);
event SetPerpetualBaseParameter(uint256 perpetualIndex, int256[9] baseParams);
event SetPerpetualRiskParameter(
uint256 perpetualIndex,
int256[9] riskParams,
int256[9] minRiskParamValues,
int256[9] maxRiskParamValues
);
event UpdatePerpetualRiskParameter(uint256 perpetualIndex, int256[9] riskParams);
event SetOracle(uint256 perpetualIndex, address indexed oldOracle, address indexed newOracle);
event UpdatePrice(
uint256 perpetualIndex,
address indexed oracle,
int256 markPrice,
uint256 markPriceUpdateTime,
int256 indexPrice,
uint256 indexPriceUpdateTime
);
event UpdateFundingRate(uint256 perpetualIndex, int256 fundingRate);
/**
* @dev Get the mark price of the perpetual. If the state of the perpetual is not "NORMAL",
* return the settlement price
* @param perpetual The reference of perpetual storage.
* @return markPrice The mark price of current perpetual.
*/
function getMarkPrice(PerpetualStorage storage perpetual)
internal
view
returns (int256 markPrice)
{
markPrice = perpetual.state == PerpetualState.NORMAL
? perpetual.markPriceData.price
: perpetual.settlementPriceData.price;
}
/**
* @dev Get the index price of the perpetual. If the state of the perpetual is not "NORMAL",
* return the settlement price
* @param perpetual The reference of perpetual storage.
* @return indexPrice The index price of current perpetual.
*/
function getIndexPrice(PerpetualStorage storage perpetual)
internal
view
returns (int256 indexPrice)
{
indexPrice = perpetual.state == PerpetualState.NORMAL
? perpetual.indexPriceData.price
: perpetual.settlementPriceData.price;
}
/**
* @dev Get the margin to rebalance in the perpetual.
* Margin to rebalance = margin - initial margin
* @param perpetual The perpetual object
* @return marginToRebalance The margin to rebalance in the perpetual
*/
function getRebalanceMargin(PerpetualStorage storage perpetual)
public
view
returns (int256 marginToRebalance)
{
int256 price = getMarkPrice(perpetual);
marginToRebalance = perpetual.getMargin(address(this), price).sub(
perpetual.getInitialMargin(address(this), price)
);
}
/**
* @dev Initialize the perpetual. Set up its configuration and validate parameters.
* If the validation passed, set the state of perpetual to "INITIALIZING"
* [minRiskParamValues, maxRiskParamValues] represents the range that the operator could
* update directly without proposal.
*
* @param perpetual The reference of perpetual storage.
* @param id The id of the perpetual (currently the index of perpetual)
* @param oracle The address of oracle contract.
* @param baseParams An int array of base parameter values.
* @param riskParams An int array of risk parameter values.
* @param minRiskParamValues An int array of minimal risk parameter values.
* @param maxRiskParamValues An int array of maximum risk parameter values.
*/
function initialize(
PerpetualStorage storage perpetual,
uint256 id,
address oracle,
int256[9] calldata baseParams,
int256[9] calldata riskParams,
int256[9] calldata minRiskParamValues,
int256[9] calldata maxRiskParamValues
) public {
perpetual.id = id;
setOracle(perpetual, oracle);
setBaseParameter(perpetual, baseParams);
setRiskParameter(perpetual, riskParams, minRiskParamValues, maxRiskParamValues);
perpetual.state = PerpetualState.INITIALIZING;
}
/**
* @dev Set oracle address of perpetual. New oracle must be different from the old one.
*
* @param perpetual The reference of perpetual storage.
* @param newOracle The address of new oracle contract.
*/
function setOracle(PerpetualStorage storage perpetual, address newOracle) public {
require(newOracle != perpetual.oracle, "oracle not changed");
validateOracle(newOracle);
emit SetOracle(perpetual.id, perpetual.oracle, newOracle);
perpetual.oracle = newOracle;
}
/**
* @dev Set the base parameter of the perpetual. Can only called by the governor
* @param perpetual The perpetual object
* @param baseParams The new value of the base parameter
*/
function setBaseParameter(PerpetualStorage storage perpetual, int256[9] memory baseParams)
public
{
validateBaseParameters(perpetual, baseParams);
perpetual.initialMarginRate = baseParams[INDEX_INITIAL_MARGIN_RATE];
perpetual.maintenanceMarginRate = baseParams[INDEX_MAINTENANCE_MARGIN_RATE];
perpetual.operatorFeeRate = baseParams[INDEX_OPERATOR_FEE_RATE];
perpetual.lpFeeRate = baseParams[INDEX_LP_FEE_RATE];
perpetual.referralRebateRate = baseParams[INDEX_REFERRAL_REBATE_RATE];
perpetual.liquidationPenaltyRate = baseParams[INDEX_LIQUIDATION_PENALTY_RATE];
perpetual.keeperGasReward = baseParams[INDEX_KEEPER_GAS_REWARD];
perpetual.insuranceFundRate = baseParams[INDEX_INSURANCE_FUND_RATE];
perpetual.maxOpenInterestRate = baseParams[INDEX_MAX_OPEN_INTEREST_RATE];
emit SetPerpetualBaseParameter(perpetual.id, baseParams);
}
/**
* @dev Set the risk parameter of the perpetual. New parameters will be validate first to apply.
* Using group set instead of one-by-one set to avoid revert due to constrains between values.
*
* @param perpetual The reference of perpetual storage.
* @param riskParams An int array of risk parameter values.
* @param minRiskParamValues An int array of minimal risk parameter values.
* @param maxRiskParamValues An int array of maximum risk parameter values.
*/
function setRiskParameter(
PerpetualStorage storage perpetual,
int256[9] memory riskParams,
int256[9] memory minRiskParamValues,
int256[9] memory maxRiskParamValues
) public {
validateRiskParameters(perpetual, riskParams);
setOption(
perpetual.halfSpread,
riskParams[INDEX_HALF_SPREAD],
minRiskParamValues[INDEX_HALF_SPREAD],
maxRiskParamValues[INDEX_HALF_SPREAD]
);
setOption(
perpetual.openSlippageFactor,
riskParams[INDEX_OPEN_SLIPPAGE_FACTOR],
minRiskParamValues[INDEX_OPEN_SLIPPAGE_FACTOR],
maxRiskParamValues[INDEX_OPEN_SLIPPAGE_FACTOR]
);
setOption(
perpetual.closeSlippageFactor,
riskParams[INDEX_CLOSE_SLIPPAGE_FACTOR],
minRiskParamValues[INDEX_CLOSE_SLIPPAGE_FACTOR],
maxRiskParamValues[INDEX_CLOSE_SLIPPAGE_FACTOR]
);
setOption(
perpetual.fundingRateLimit,
riskParams[INDEX_FUNDING_RATE_LIMIT],
minRiskParamValues[INDEX_FUNDING_RATE_LIMIT],
maxRiskParamValues[INDEX_FUNDING_RATE_LIMIT]
);
setOption(
perpetual.ammMaxLeverage,
riskParams[INDEX_AMM_MAX_LEVERAGE],
minRiskParamValues[INDEX_AMM_MAX_LEVERAGE],
maxRiskParamValues[INDEX_AMM_MAX_LEVERAGE]
);
setOption(
perpetual.maxClosePriceDiscount,
riskParams[INDEX_AMM_CLOSE_PRICE_DISCOUNT],
minRiskParamValues[INDEX_AMM_CLOSE_PRICE_DISCOUNT],
maxRiskParamValues[INDEX_AMM_CLOSE_PRICE_DISCOUNT]
);
setOption(
perpetual.fundingRateFactor,
riskParams[INDEX_FUNDING_RATE_FACTOR],
minRiskParamValues[INDEX_FUNDING_RATE_FACTOR],
maxRiskParamValues[INDEX_FUNDING_RATE_FACTOR]
);
setOption(
perpetual.defaultTargetLeverage,
riskParams[INDEX_DEFAULT_TARGET_LEVERAGE],
minRiskParamValues[INDEX_DEFAULT_TARGET_LEVERAGE],
maxRiskParamValues[INDEX_DEFAULT_TARGET_LEVERAGE]
);
setOption(
perpetual.baseFundingRate,
riskParams[INDEX_BASE_FUNDING_RATE],
minRiskParamValues[INDEX_BASE_FUNDING_RATE],
maxRiskParamValues[INDEX_BASE_FUNDING_RATE]
);
emit SetPerpetualRiskParameter(
perpetual.id,
riskParams,
minRiskParamValues,
maxRiskParamValues
);
}
/**
* @dev Adjust the risk parameter. New values should always satisfied the constrains and min/max limit.
*
* @param perpetual The reference of perpetual storage.
* @param riskParams An int array of risk parameter values.
*/
function updateRiskParameter(PerpetualStorage storage perpetual, int256[9] memory riskParams)
public
{
validateRiskParameters(perpetual, riskParams);
updateOption(perpetual.halfSpread, riskParams[INDEX_HALF_SPREAD]);
updateOption(perpetual.openSlippageFactor, riskParams[INDEX_OPEN_SLIPPAGE_FACTOR]);
updateOption(perpetual.closeSlippageFactor, riskParams[INDEX_CLOSE_SLIPPAGE_FACTOR]);
updateOption(perpetual.fundingRateLimit, riskParams[INDEX_FUNDING_RATE_LIMIT]);
updateOption(perpetual.ammMaxLeverage, riskParams[INDEX_AMM_MAX_LEVERAGE]);
updateOption(perpetual.maxClosePriceDiscount, riskParams[INDEX_AMM_CLOSE_PRICE_DISCOUNT]);
updateOption(perpetual.fundingRateFactor, riskParams[INDEX_FUNDING_RATE_FACTOR]);
updateOption(perpetual.defaultTargetLeverage, riskParams[INDEX_DEFAULT_TARGET_LEVERAGE]);
updateOption(perpetual.baseFundingRate, riskParams[INDEX_BASE_FUNDING_RATE]);
emit UpdatePerpetualRiskParameter(perpetual.id, riskParams);
}
/**
* @dev Update the unitAccumulativeFunding variable in perpetual.
* After that, funding payment of every account in the perpetual is updated,
*
* nextUnitAccumulativeFunding = unitAccumulativeFunding
* + index * fundingRate * elapsedTime / fundingInterval
*
* @param perpetual The reference of perpetual storage.
* @param timeElapsed The elapsed time since last update.
*/
function updateFundingState(PerpetualStorage storage perpetual, int256 timeElapsed) public {
int256 deltaUnitLoss = timeElapsed
.mul(getIndexPrice(perpetual))
.wmul(perpetual.fundingRate)
.div(FUNDING_INTERVAL);
perpetual.unitAccumulativeFunding = perpetual.unitAccumulativeFunding.add(deltaUnitLoss);
emit UpdateUnitAccumulativeFunding(perpetual.id, perpetual.unitAccumulativeFunding);
}
/**
* @dev Update the funding rate of the perpetual.
*
* - funding rate = - index * position * factor / pool margin
* - funding rate += base funding rate when
* - open interest != 0 and position >= 0 and base funding rate < 0
* - open interest != 0 and position <= 0 and base funding rate > 0
* - funding rate = (+/-)limit when
* - pool margin = 0 and position != 0
* - abs(funding rate) > limit
*
* @param perpetual The reference of perpetual storage.
* @param poolMargin The pool margin of liquidity pool.
*/
function updateFundingRate(PerpetualStorage storage perpetual, int256 poolMargin) public {
int256 position = perpetual.getPosition(address(this));
int256 newFundingRate;
if (
((perpetual.baseFundingRate.value > 0 && position <= 0) ||
(perpetual.baseFundingRate.value < 0 && position >= 0)) &&
perpetual.openInterest != 0
) {
newFundingRate = perpetual.baseFundingRate.value;
} else {
newFundingRate = 0;
}
if (position != 0) {
int256 fundingRateLimit = perpetual.fundingRateLimit.value;
if (poolMargin != 0) {
newFundingRate = newFundingRate.add(
getIndexPrice(perpetual).wfrac(position, poolMargin).neg().wmul(
perpetual.fundingRateFactor.value
)
);
newFundingRate = newFundingRate.min(fundingRateLimit).max(fundingRateLimit.neg());
} else if (position > 0) {
newFundingRate = fundingRateLimit.neg();
} else {
newFundingRate = fundingRateLimit;
}
}
perpetual.fundingRate = newFundingRate;
emit UpdateFundingRate(perpetual.id, newFundingRate);
}
/**
* @dev Update the oracle price of the perpetual, including the index price and the mark price
* @param perpetual The reference of perpetual storage.
*/
function updatePrice(PerpetualStorage storage perpetual) internal {
IOracle oracle = IOracle(perpetual.oracle);
updatePriceData(perpetual.markPriceData, oracle.priceTWAPLong);
updatePriceData(perpetual.indexPriceData, oracle.priceTWAPShort);
emit UpdatePrice(
perpetual.id,
address(oracle),
perpetual.markPriceData.price,
perpetual.markPriceData.time,
perpetual.indexPriceData.price,
perpetual.indexPriceData.time
);
}
/**
* @dev Set the state of the perpetual to "NORMAL". The state must be "INITIALIZING" before
* @param perpetual The reference of perpetual storage.
*/
function setNormalState(PerpetualStorage storage perpetual) public {
require(
perpetual.state == PerpetualState.INITIALIZING,
"perpetual should be in initializing state"
);
perpetual.state = PerpetualState.NORMAL;
emit SetNormalState(perpetual.id);
}
/**
* @dev Set the state of the perpetual to "EMERGENCY". The state must be "NORMAL" before.
* The settlement price is the mark price at this time
* @param perpetual The reference of perpetual storage.
*/
function setEmergencyState(PerpetualStorage storage perpetual) public {
require(perpetual.state == PerpetualState.NORMAL, "perpetual should be in NORMAL state");
// use mark price as final price when emergency
perpetual.settlementPriceData = perpetual.markPriceData;
perpetual.totalAccount = perpetual.activeAccounts.length();
perpetual.state = PerpetualState.EMERGENCY;
emit SetEmergencyState(
perpetual.id,
perpetual.settlementPriceData.price,
perpetual.settlementPriceData.time
);
}
/**
* @dev Set the state of the perpetual to "CLEARED". The state must be "EMERGENCY" before.
* And settle the collateral of the perpetual, which means
* determining how much collateral should returned to every account.
* @param perpetual The reference of perpetual storage.
*/
function setClearedState(PerpetualStorage storage perpetual) public {
require(
perpetual.state == PerpetualState.EMERGENCY,
"perpetual should be in emergency state"
);
settleCollateral(perpetual);
perpetual.state = PerpetualState.CLEARED;
emit SetClearedState(perpetual.id);
}
/**
* @dev Deposit collateral to the trader's account of the perpetual, that will increase the cash amount in
* trader's margin account.
*
* If this is the first time the trader deposits in current perpetual, the address of trader will be
* push to a list, then the trader is defined as an 'Active' trader for this perpetual.
* List of active traders will be used during clearing.
*
* @param perpetual The reference of perpetual storage.
* @param trader The address of the trader.
* @param amount The amount of collateral to deposit.
* @return isInitialDeposit True if the trader's account is empty before depositing.
*/
function deposit(
PerpetualStorage storage perpetual,
address trader,
int256 amount
) public returns (bool isInitialDeposit) {
require(amount > 0, "amount should greater than 0");
perpetual.updateCash(trader, amount);
isInitialDeposit = registerActiveAccount(perpetual, trader);
emit Deposit(perpetual.id, trader, amount);
}
/**
* @dev Withdraw collateral from the trader's account of the perpetual, that will increase the cash amount in
* trader's margin account.
*
* Trader must be initial margin safe in the perpetual after withdrawing.
* Making the margin account 'Empty' will mark this account as a 'Deactive' trader then be removed from
* list of active traders.
*
* @param perpetual The reference of perpetual storage.
* @param trader The address of the trader.
* @param amount The amount of collateral to withdraw.
* @return isLastWithdrawal True if the trader's account is empty after withdrawing.
*/
function withdraw(
PerpetualStorage storage perpetual,
address trader,
int256 amount
) public returns (bool isLastWithdrawal) {
require(
perpetual.getPosition(trader) == 0 || !IOracle(perpetual.oracle).isMarketClosed(),
"market is closed"
);
require(amount > 0, "amount should greater than 0");
perpetual.updateCash(trader, amount.neg());
int256 markPrice = getMarkPrice(perpetual);
require(
perpetual.isInitialMarginSafe(trader, markPrice),
"margin is unsafe after withdrawal"
);
isLastWithdrawal = perpetual.isEmptyAccount(trader);
if (isLastWithdrawal) {
deregisterActiveAccount(perpetual, trader);
}
emit Withdraw(perpetual.id, trader, amount);
}
/**
* @dev Clear the active account of the perpetual which state is "EMERGENCY" and send gas reward of collateral
* to sender.
* If all active accounts are cleared, the clear progress is done and the perpetual's state will
* change to "CLEARED".
*
* @param perpetual The reference of perpetual storage.
* @param trader The address of the trader to clear.
* @return isAllCleared True if all the active accounts are cleared.
*/
function clear(PerpetualStorage storage perpetual, address trader)
public
returns (bool isAllCleared)
{
require(perpetual.activeAccounts.length() > 0, "no account to clear");
require(
perpetual.activeAccounts.contains(trader),
"account cannot be cleared or already cleared"
);
countMargin(perpetual, trader);
perpetual.activeAccounts.remove(trader);
isAllCleared = (perpetual.activeAccounts.length() == 0);
emit Clear(perpetual.id, trader);
}
/**
* @dev Check the margin balance of trader's account, update total margin.
* If the margin of the trader's account is not positive, it will be counted as 0.
*
* @param perpetual The reference of perpetual storage.
* @param trader The address of the trader to be counted.
*/
function countMargin(PerpetualStorage storage perpetual, address trader) public {
int256 margin = perpetual.getMargin(trader, getMarkPrice(perpetual));
if (margin <= 0) {
return;
}
if (perpetual.getPosition(trader) != 0) {
perpetual.totalMarginWithPosition = perpetual.totalMarginWithPosition.add(margin);
} else {
perpetual.totalMarginWithoutPosition = perpetual.totalMarginWithoutPosition.add(margin);
}
}
/**
* @dev Get the address of the next active account in the perpetual.
*
* @param perpetual The reference of perpetual storage.
* @return account The address of the next active account.
*/
function getNextActiveAccount(PerpetualStorage storage perpetual)
public
view
returns (address account)
{
require(perpetual.activeAccounts.length() > 0, "no active account");
account = perpetual.activeAccounts.at(0);
}
/**
* @dev If the state of the perpetual is "CLEARED".
* The traders is able to settle all margin balance left in account.
* How much collateral can be returned is determined by the ratio of margin balance left in account to the
* total amount of collateral in perpetual.
* The priority is:
* - accounts withou position;
* - accounts with positions;
* - accounts with negative margin balance will get nothing back.
*
* @param perpetual The reference of perpetual storage.
* @param trader The address of the trader to settle.
* @param marginToReturn The actual collateral will be returned to the trader.
*/
function settle(PerpetualStorage storage perpetual, address trader)
public
returns (int256 marginToReturn)
{
int256 price = getMarkPrice(perpetual);
marginToReturn = perpetual.getSettleableMargin(trader, price);
perpetual.resetAccount(trader);
emit Settle(perpetual.id, trader, marginToReturn);
}
/**
* @dev Settle the total collateral of the perpetual, which means update redemptionRateWithPosition
* and redemptionRateWithoutPosition variables.
* If the total collateral is not enough for the accounts without position,
* all the total collateral is given to them proportionally.
* If the total collateral is more than the accounts without position needs,
* the extra part of collateral is given to the accounts with position proportionally.
*
* @param perpetual The reference of perpetual storage.
*/
function settleCollateral(PerpetualStorage storage perpetual) public {
int256 totalCollateral = perpetual.totalCollateral;
// 2. cover margin without position
if (totalCollateral < perpetual.totalMarginWithoutPosition) {
// margin without positions get balance / total margin
// smaller rate to make sure total redemption margin < total collateral of perpetual
perpetual.redemptionRateWithoutPosition = perpetual.totalMarginWithoutPosition > 0
? totalCollateral.wdiv(perpetual.totalMarginWithoutPosition, Round.FLOOR)
: 0;
// margin with positions will get nothing
perpetual.redemptionRateWithPosition = 0;
} else {
// 3. covere margin with position
perpetual.redemptionRateWithoutPosition = Constant.SIGNED_ONE;
// smaller rate to make sure total redemption margin < total collateral of perpetual
perpetual.redemptionRateWithPosition = perpetual.totalMarginWithPosition > 0
? totalCollateral.sub(perpetual.totalMarginWithoutPosition).wdiv(
perpetual.totalMarginWithPosition,
Round.FLOOR
)
: 0;
}
}
/**
* @dev Register the trader's account to the active accounts in the perpetual
* @param perpetual The reference of perpetual storage.
* @param trader The address of the trader.
* @return True if the trader is added to account for the first time.
*/
function registerActiveAccount(PerpetualStorage storage perpetual, address trader)
internal
returns (bool)
{
return perpetual.activeAccounts.add(trader);
}
/**
* @dev Deregister the trader's account from the active accounts in the perpetual
* @param perpetual The reference of perpetual storage.
* @param trader The address of the trader.
* @return True if the trader is removed to account for the first time.
*/
function deregisterActiveAccount(PerpetualStorage storage perpetual, address trader)
internal
returns (bool)
{
return perpetual.activeAccounts.remove(trader);
}
/**
* @dev Update the price data, which means the price and the update time
* @param priceData The price data to update.
* @param priceGetter The function pointer to retrieve current price data.
*/
function updatePriceData(
OraclePriceData storage priceData,
function() external returns (int256, uint256) priceGetter
) internal {
(int256 price, uint256 time) = priceGetter();
require(price > 0 && time != 0, "invalid price data");
if (time >= priceData.time) {
priceData.price = price;
priceData.time = time;
}
}
/**
* @dev Increase the total collateral of the perpetual
* @param perpetual The reference of perpetual storage.
* @param amount The amount of collateral to increase
*/
function increaseTotalCollateral(PerpetualStorage storage perpetual, int256 amount) internal {
require(amount >= 0, "amount is negative");
perpetual.totalCollateral = perpetual.totalCollateral.add(amount);
}
/**
* @dev Decrease the total collateral of the perpetual
* @param perpetual The reference of perpetual storage.
* @param amount The amount of collateral to decrease
*/
function decreaseTotalCollateral(PerpetualStorage storage perpetual, int256 amount) internal {
require(amount >= 0, "amount is negative");
perpetual.totalCollateral = perpetual.totalCollateral.sub(amount);
require(perpetual.totalCollateral >= 0, "collateral is negative");
}
/**
* @dev Update the option
* @param option The option to update
* @param newValue The new value of the option, must between the minimum value and the maximum value
*/
function updateOption(Option storage option, int256 newValue) internal {
require(newValue >= option.minValue && newValue <= option.maxValue, "value out of range");
option.value = newValue;
}
/**
* @dev Set the option value, with constraints that newMinValue <= newValue <= newMaxValue.
*
* @param option The reference of option storage.
* @param newValue The new value of the option, must be within range of [newMinValue, newMaxValue].
* @param newMinValue The minimum value of the option.
* @param newMaxValue The maximum value of the option.
*/
function setOption(
Option storage option,
int256 newValue,
int256 newMinValue,
int256 newMaxValue
) internal {
require(newValue >= newMinValue && newValue <= newMaxValue, "value out of range");
option.value = newValue;
option.minValue = newMinValue;
option.maxValue = newMaxValue;
}
/**
* @dev Validate oracle contract, including each method of oracle
*
* @param oracle The address of oracle contract.
*/
function validateOracle(address oracle) public {
require(oracle != address(0), "invalid oracle address");
require(oracle.isContract(), "oracle must be contract");
bool success;
bytes memory data;
(success, data) = oracle.call(abi.encodeWithSignature("isMarketClosed()"));
require(success && data.length == 32, "invalid function: isMarketClosed");
(success, data) = oracle.call(abi.encodeWithSignature("isTerminated()"));
require(success && data.length == 32, "invalid function: isTerminated");
require(!abi.decode(data, (bool)), "oracle is terminated");
(success, data) = oracle.call(abi.encodeWithSignature("collateral()"));
require(success && data.length > 0, "invalid function: collateral");
string memory result;
result = abi.decode(data, (string));
require(keccak256(bytes(result)) != keccak256(bytes("")), "oracle's collateral is empty");
(success, data) = oracle.call(abi.encodeWithSignature("underlyingAsset()"));
require(success && data.length > 0, "invalid function: underlyingAsset");
result = abi.decode(data, (string));
require(
keccak256(bytes(result)) != keccak256(bytes("")),
"oracle's underlyingAsset is empty"
);
(success, data) = oracle.call(abi.encodeWithSignature("priceTWAPLong()"));
require(success && data.length > 0, "invalid function: priceTWAPLong");
(int256 price, uint256 timestamp) = abi.decode(data, (int256, uint256));
require(price > 0 && timestamp > 0, "oracle's twap long price is not updated");
(success, data) = oracle.call(abi.encodeWithSignature("priceTWAPShort()"));
require(success && data.length > 0, "invalid function: priceTWAPShort");
(price, timestamp) = abi.decode(data, (int256, uint256));
require(price > 0 && timestamp > 0, "oracle's twap short price is not updated");
}
/**
* @dev Validate the base parameters of the perpetual:
* 1. initial margin rate > 0
* 2. 0 < maintenance margin rate <= initial margin rate
* 3. 0 <= operator fee rate <= 0.01
* 4. 0 <= lp fee rate <= 0.01
* 5. 0 <= liquidation penalty rate < maintenance margin rate
* 6. keeper gas reward >= 0
*
* @param perpetual The reference of perpetual storage.
* @param baseParams The base parameters of the perpetual.
*/
function validateBaseParameters(PerpetualStorage storage perpetual, int256[9] memory baseParams)
public
view
{
require(baseParams[INDEX_INITIAL_MARGIN_RATE] > 0, "initialMarginRate <= 0");
require(
perpetual.initialMarginRate == 0 ||
baseParams[INDEX_INITIAL_MARGIN_RATE] <= perpetual.initialMarginRate,
"cannot increase initialMarginRate"
);
int256 maxLeverage = Constant.SIGNED_ONE.wdiv(baseParams[INDEX_INITIAL_MARGIN_RATE]);
require(
perpetual.defaultTargetLeverage.value <= maxLeverage,
"default target leverage exceeds max leverage"
);
require(
perpetual.maintenanceMarginRate == 0 ||
baseParams[INDEX_MAINTENANCE_MARGIN_RATE] <= perpetual.maintenanceMarginRate,
"cannot increase maintenanceMarginRate"
);
require(baseParams[INDEX_MAINTENANCE_MARGIN_RATE] > 0, "maintenanceMarginRate <= 0");
require(
baseParams[INDEX_MAINTENANCE_MARGIN_RATE] <= baseParams[INDEX_INITIAL_MARGIN_RATE],
"maintenanceMarginRate > initialMarginRate"
);
require(baseParams[INDEX_OPERATOR_FEE_RATE] >= 0, "operatorFeeRate < 0");
require(
baseParams[INDEX_OPERATOR_FEE_RATE] <= (Constant.SIGNED_ONE / 100),
"operatorFeeRate > 1%"
);
require(baseParams[INDEX_LP_FEE_RATE] >= 0, "lpFeeRate < 0");
require(baseParams[INDEX_LP_FEE_RATE] <= (Constant.SIGNED_ONE / 100), "lpFeeRate > 1%");
require(baseParams[INDEX_REFERRAL_REBATE_RATE] >= 0, "referralRebateRate < 0");
require(
baseParams[INDEX_REFERRAL_REBATE_RATE] <= Constant.SIGNED_ONE,
"referralRebateRate > 100%"
);
require(baseParams[INDEX_LIQUIDATION_PENALTY_RATE] >= 0, "liquidationPenaltyRate < 0");
require(
baseParams[INDEX_LIQUIDATION_PENALTY_RATE] <= baseParams[INDEX_MAINTENANCE_MARGIN_RATE],
"liquidationPenaltyRate > maintenanceMarginRate"
);
require(baseParams[INDEX_KEEPER_GAS_REWARD] >= 0, "keeperGasReward < 0");
require(baseParams[INDEX_INSURANCE_FUND_RATE] >= 0, "insuranceFundRate < 0");
require(baseParams[INDEX_MAX_OPEN_INTEREST_RATE] > 0, "maxOpenInterestRate <= 0");
}
/**
* @dev alidate the risk parameters of the perpetual
* 1. 0 <= half spread < 1
* 2. open slippage factor > 0
* 3. 0 < close slippage factor <= open slippage factor
* 4. funding rate limit >= 0
* 5. AMM max leverage > 0
* 6. 0 <= max close price discount < 1
*
* @param perpetual The reference of perpetual storage.
*/
function validateRiskParameters(PerpetualStorage storage perpetual, int256[9] memory riskParams)
public
view
{
// must set risk parameters after setting base parameters
require(perpetual.initialMarginRate > 0, "need to set base parameters first");
require(riskParams[INDEX_HALF_SPREAD] >= 0, "halfSpread < 0");
require(riskParams[INDEX_HALF_SPREAD] < Constant.SIGNED_ONE, "halfSpread >= 100%");
require(riskParams[INDEX_OPEN_SLIPPAGE_FACTOR] > 0, "openSlippageFactor < 0");
require(riskParams[INDEX_CLOSE_SLIPPAGE_FACTOR] > 0, "closeSlippageFactor < 0");
require(
riskParams[INDEX_CLOSE_SLIPPAGE_FACTOR] <= riskParams[INDEX_OPEN_SLIPPAGE_FACTOR],
"closeSlippageFactor > openSlippageFactor"
);
require(riskParams[INDEX_FUNDING_RATE_FACTOR] >= 0, "fundingRateFactor < 0");
require(riskParams[INDEX_FUNDING_RATE_LIMIT] >= 0, "fundingRateLimit < 0");
require(riskParams[INDEX_AMM_MAX_LEVERAGE] >= 0, "ammMaxLeverage < 0");
require(
riskParams[INDEX_AMM_MAX_LEVERAGE] <=
Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate, Round.FLOOR),
"ammMaxLeverage > 1 / initialMarginRate"
);
require(riskParams[INDEX_AMM_CLOSE_PRICE_DISCOUNT] >= 0, "maxClosePriceDiscount < 0");
require(
riskParams[INDEX_AMM_CLOSE_PRICE_DISCOUNT] < Constant.SIGNED_ONE,
"maxClosePriceDiscount >= 100%"
);
require(perpetual.initialMarginRate != 0, "initialMarginRate is not set");
int256 maxLeverage = Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate);
require(
riskParams[INDEX_DEFAULT_TARGET_LEVERAGE] <= maxLeverage,
"default target leverage exceeds max leverage"
);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
import "@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol";
/**
* @notice Perpetual state:
* - INVALID: Uninitialized or not non-existent perpetual;
* - INITIALIZING: Only when LiquidityPoolStorage.isRunning == false. Traders cannot perform operations;
* - NORMAL: Full functional state. Traders is able to perform all operations;
* - EMERGENCY: Perpetual is unsafe and only clear is available;
* - CLEARED: All margin account is cleared. Trade could withdraw remaining margin balance.
*/
enum PerpetualState {
INVALID,
INITIALIZING,
NORMAL,
EMERGENCY,
CLEARED
}
enum OrderType {
LIMIT,
MARKET,
STOP
}
/**
* @notice Data structure to store risk parameter value.
*/
struct Option {
int256 value;
int256 minValue;
int256 maxValue;
}
/**
* @notice Data structure to store oracle price data.
*/
struct OraclePriceData {
int256 price;
uint256 time;
}
/**
* @notice Data structure to store user margin information. See MarginAccountModule.sol for details.
*/
struct MarginAccount {
int256 cash;
int256 position;
int256 targetLeverage;
}
/**
* @notice Data structure of an order object.
*/
struct Order {
address trader;
address broker;
address relayer;
address referrer;
address liquidityPool;
int256 minTradeAmount;
int256 amount;
int256 limitPrice;
int256 triggerPrice;
uint256 chainID;
uint64 expiredAt;
uint32 perpetualIndex;
uint32 brokerFeeLimit;
uint32 flags;
uint32 salt;
}
/**
* @notice Core data structure, a core .
*/
struct LiquidityPoolStorage {
bool isRunning;
bool isFastCreationEnabled;
// addresses
address creator;
address operator;
address transferringOperator;
address governor;
address shareToken;
address accessController;
bool reserved3; // isWrapped
uint256 scaler;
uint256 collateralDecimals;
address collateralToken;
// pool attributes
int256 poolCash;
uint256 fundingTime;
uint256 reserved5;
uint256 operatorExpiration;
mapping(address => int256) reserved1;
bytes32[] reserved2;
// perpetuals
uint256 perpetualCount;
mapping(uint256 => PerpetualStorage) perpetuals;
// insurance fund
int256 insuranceFundCap;
int256 insuranceFund;
int256 donatedInsuranceFund;
address reserved4;
uint256 liquidityCap;
uint256 shareTransferDelay;
// reserved slot for future upgrade
bytes32[14] reserved;
}
/**
* @notice Core data structure, storing perpetual information.
*/
struct PerpetualStorage {
uint256 id;
PerpetualState state;
address oracle;
int256 totalCollateral;
int256 openInterest;
// prices
OraclePriceData indexPriceData;
OraclePriceData markPriceData;
OraclePriceData settlementPriceData;
// funding state
int256 fundingRate;
int256 unitAccumulativeFunding;
// base parameters
int256 initialMarginRate;
int256 maintenanceMarginRate;
int256 operatorFeeRate;
int256 lpFeeRate;
int256 referralRebateRate;
int256 liquidationPenaltyRate;
int256 keeperGasReward;
int256 insuranceFundRate;
int256 reserved1;
int256 maxOpenInterestRate;
// risk parameters
Option halfSpread;
Option openSlippageFactor;
Option closeSlippageFactor;
Option fundingRateLimit;
Option fundingRateFactor;
Option ammMaxLeverage;
Option maxClosePriceDiscount;
// users
uint256 totalAccount;
int256 totalMarginWithoutPosition;
int256 totalMarginWithPosition;
int256 redemptionRateWithoutPosition;
int256 redemptionRateWithPosition;
EnumerableSetUpgradeable.AddressSet activeAccounts;
// insurance fund
int256 reserved2;
int256 reserved3;
// accounts
mapping(address => MarginAccount) marginAccounts;
Option defaultTargetLeverage;
// keeper
address reserved4;
EnumerableSetUpgradeable.AddressSet ammKeepers;
EnumerableSetUpgradeable.AddressSet reserved5;
Option baseFundingRate;
// reserved slot for future upgrade
bytes32[9] reserved;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
import "./IProxyAdmin.sol";
interface IPoolCreator {
function upgradeAdmin() external view returns (IProxyAdmin proxyAdmin);
/**
* @notice Create a liquidity pool with the latest version.
* The sender will be the operator of pool.
*
* @param collateral he collateral address of the liquidity pool.
* @param collateralDecimals The collateral's decimals of the liquidity pool.
* @param nonce A random nonce to calculate the address of deployed contracts.
* @param initData A bytes array contains data to initialize new created liquidity pool.
* @return liquidityPool The address of the created liquidity pool.
*/
function createLiquidityPool(
address collateral,
uint256 collateralDecimals,
int256 nonce,
bytes calldata initData
) external returns (address liquidityPool, address governor);
/**
* @notice Upgrade a liquidity pool and governor pair then call a patch function on the upgraded contract (optional).
* This method checks the sender and forwards the request to ProxyAdmin to do upgrading.
*
* @param targetVersionKey The key of version to be upgrade up. The target version must be compatible with
* current version.
* @param dataForLiquidityPool The patch calldata for upgraded liquidity pool.
* @param dataForGovernor The patch calldata of upgraded governor.
*/
function upgradeToAndCall(
bytes32 targetVersionKey,
bytes memory dataForLiquidityPool,
bytes memory dataForGovernor
) external;
/**
* @notice Indicates the universe settle state.
* If the flag set to true:
* - all the pereptual created by this poolCreator can be settled immediately;
* - all the trading method will be unavailable.
*/
function isUniverseSettled() external view returns (bool);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
import "./IProxyAdmin.sol";
interface ITracer {
/**
* @notice Activate the perpetual for the trader. Active means the trader's account is not empty in
* the perpetual. Empty means cash and position are zero. Can only called by a liquidity pool.
*
* @param trader The address of the trader.
* @param perpetualIndex The index of the perpetual in the liquidity pool.
* @return True if the activation is successful.
*/
function activatePerpetualFor(address trader, uint256 perpetualIndex) external returns (bool);
/**
* @notice Deactivate the perpetual for the trader. Active means the trader's account is not empty in
* the perpetual. Empty means cash and position are zero. Can only called by a liquidity pool.
*
* @param trader The address of the trader.
* @param perpetualIndex The index of the perpetual in the liquidity pool.
* @return True if the deactivation is successful.
*/
function deactivatePerpetualFor(address trader, uint256 perpetualIndex) external returns (bool);
/**
* @notice Liquidity pool must call this method when changing its ownership to the new operator.
* Can only be called by a liquidity pool. This method does not affect 'ownership' or privileges
* of operator but only make a record for further query.
*
* @param liquidityPool The address of the liquidity pool.
* @param operator The address of the new operator, must be different from the old operator.
*/
function registerOperatorOfLiquidityPool(address liquidityPool, address operator) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
import "./IProxyAdmin.sol";
interface IVersionControl {
function owner() external view returns (address);
function getLatestVersion() external view returns (bytes32 latestVersionKey);
/**
* @notice Get the details of the version.
*
* @param versionKey The key of the version to get.
* @return liquidityPoolTemplate The address of the liquidity pool template.
* @return governorTemplate The address of the governor template.
* @return compatibility The compatibility of the specified version.
*/
function getVersion(bytes32 versionKey)
external
view
returns (
address liquidityPoolTemplate,
address governorTemplate,
uint256 compatibility
);
/**
* @notice Get the description of the implementation of liquidity pool.
* Description contains creator, create time, compatibility and note
*
* @param liquidityPool The address of the liquidity pool.
* @param governor The address of the governor.
* @return appliedVersionKey The version key of given liquidity pool and governor.
*/
function getAppliedVersionKey(address liquidityPool, address governor)
external
view
returns (bytes32 appliedVersionKey);
/**
* @notice Check if a key is valid (exists).
*
* @param versionKey The key of the version to test.
* @return isValid Return true if the version of given key is valid.
*/
function isVersionKeyValid(bytes32 versionKey) external view returns (bool isValid);
/**
* @notice Check if the implementation of liquidity pool target is compatible with the implementation base.
* Being compatible means having larger compatibility.
*
* @param targetVersionKey The key of the version to be upgraded to.
* @param baseVersionKey The key of the version to be upgraded from.
* @return isCompatible True if the target version is compatible with the base version.
*/
function isVersionCompatible(bytes32 targetVersionKey, bytes32 baseVersionKey)
external
view
returns (bool isCompatible);
/**
* @dev Get a certain number of implementations of liquidity pool within range [begin, end).
*
* @param begin The index of first element to retrieve.
* @param end The end index of element, exclusive.
* @return versionKeys An array contains current version keys.
*/
function listAvailableVersions(uint256 begin, uint256 end)
external
view
returns (bytes32[] memory versionKeys);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
import "./IProxyAdmin.sol";
interface IVariables {
function owner() external view returns (address);
/**
* @notice Get the address of the vault
* @return address The address of the vault
*/
function getVault() external view returns (address);
/**
* @notice Get the vault fee rate
* @return int256 The vault fee rate
*/
function getVaultFeeRate() external view returns (int256);
/**
* @notice Get the address of the access controller. It's always its own address.
*
* @return address The address of the access controller.
*/
function getAccessController() external view returns (address);
/**
* @notice Get the address of the symbol service.
*
* @return Address The address of the symbol service.
*/
function getSymbolService() external view returns (address);
/**
* @notice Set the vault address. Can only called by owner.
*
* @param newVault The new value of the vault fee rate
*/
function setVault(address newVault) external;
/**
* @notice Get the address of the mcb token.
* @dev [ConfirmBeforeDeployment]
*
* @return Address The address of the mcb token.
*/
function getMCBToken() external pure returns (address);
/**
* @notice Set the vault fee rate. Can only called by owner.
*
* @param newVaultFeeRate The new value of the vault fee rate
*/
function setVaultFeeRate(int256 newVaultFeeRate) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
interface IKeeperWhitelist {
/**
* @notice Add an address to keeper whitelist.
*/
function addKeeper(address keeper) external;
/**
* @notice Remove an address from keeper whitelist.
*/
function removeKeeper(address keeper) external;
/**
* @notice Check if an address is in keeper whitelist.
*/
function isKeeper(address keeper) external view returns (bool);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
interface IProxyAdmin {
function getProxyImplementation(address proxy) external view returns (address);
/**
* @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function upgrade(address proxy, address implementation) external;
/**
* @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
* {TransparentUpgradeableProxy-upgradeToAndCall}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function upgradeAndCall(
address proxy,
address implementation,
bytes memory data
) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMathUpgradeable {
/**
* @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) {
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) {
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) {
// 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) {
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) {
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) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @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) {
require(b <= a, "SafeMath: subtraction overflow");
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) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @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. 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) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
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) {
require(b > 0, "SafeMath: modulo by zero");
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) {
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.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* 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) {
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) {
require(b > 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
library Constant {
address internal constant INVALID_ADDRESS = address(0);
int256 internal constant SIGNED_ONE = 10**18;
uint256 internal constant UNSIGNED_ONE = 10**18;
uint256 internal constant PRIVILEGE_DEPOSIT = 0x1;
uint256 internal constant PRIVILEGE_WITHDRAW = 0x2;
uint256 internal constant PRIVILEGE_TRADE = 0x4;
uint256 internal constant PRIVILEGE_LIQUIDATE = 0x8;
uint256 internal constant PRIVILEGE_GUARD =
PRIVILEGE_DEPOSIT | PRIVILEGE_WITHDRAW | PRIVILEGE_TRADE | PRIVILEGE_LIQUIDATE;
// max number of uint256
uint256 internal constant SET_ALL_PERPETUALS_TO_EMERGENCY_STATE =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) { // Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*/
library EnumerableSetUpgradeable {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) { // Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.7.4;
library Math {
/**
* @dev Get the most significant bit of the number,
example: 0 ~ 1 => 0, 2 ~ 3 => 1, 4 ~ 7 => 2, 8 ~ 15 => 3,
about use 606 ~ 672 gas
* @param x The number
* @return uint8 The significant bit of the number
*/
function mostSignificantBit(uint256 x) internal pure returns (uint8) {
uint256 t;
uint8 r;
if ((t = (x >> 128)) > 0) {
x = t;
r += 128;
}
if ((t = (x >> 64)) > 0) {
x = t;
r += 64;
}
if ((t = (x >> 32)) > 0) {
x = t;
r += 32;
}
if ((t = (x >> 16)) > 0) {
x = t;
r += 16;
}
if ((t = (x >> 8)) > 0) {
x = t;
r += 8;
}
if ((t = (x >> 4)) > 0) {
x = t;
r += 4;
}
if ((t = (x >> 2)) > 0) {
x = t;
r += 2;
}
if ((t = (x >> 1)) > 0) {
x = t;
r += 1;
}
return r;
}
// https://en.wikipedia.org/wiki/Integer_square_root
/**
* @dev Get the square root of the number
* @param x The number, usually 10^36
* @return int256 The square root of the number, usually 10^18
*/
function sqrt(int256 x) internal pure returns (int256) {
require(x >= 0, "negative sqrt");
if (x < 3) {
return (x + 1) / 2;
}
// binary estimate
// inspired by https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_estimates
uint8 n = mostSignificantBit(uint256(x));
// make sure initial estimate > sqrt(x)
// 2^ceil((n + 1) / 2) as initial estimate
// 2^(n + 1) > x
// => 2^ceil((n + 1) / 2) > 2^((n + 1) / 2) > sqrt(x)
n = (n + 1) / 2 + 1;
// modified babylonian method
int256 next = int256(1 << n);
int256 y;
do {
y = next;
next = (next + x / next) >> 1;
} while (next < y);
return y;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @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
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 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");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(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");
// solhint-disable-next-line avoid-low-level-calls
(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");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
interface IOracle {
/**
* @dev The market is closed if the market is not in its regular trading period.
*/
function isMarketClosed() external returns (bool);
/**
* @dev The oracle service was shutdown and never online again.
*/
function isTerminated() external returns (bool);
/**
* @dev Get collateral symbol. Also known as quote.
*/
function collateral() external view returns (string memory);
/**
* @dev Get underlying asset symbol. Also known as base.
*/
function underlyingAsset() external view returns (string memory);
/**
* @dev Mark price. Used to evaluate the account margin balance and liquidation.
*
* It does not need to be a TWAP. This name is only for backward compatibility.
*/
function priceTWAPLong() external returns (int256 newPrice, uint256 newTimestamp);
/**
* @dev Index price. It is AMM reference price.
*
* It does not need to be a TWAP. This name is only for backward compatibility.
*/
function priceTWAPShort() external returns (int256 newPrice, uint256 newTimestamp);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @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 `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./IERC20Upgradeable.sol";
import "../../math/SafeMathUpgradeable.sol";
import "../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
using SafeMathUpgradeable for uint256;
using AddressUpgradeable for address;
function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20Upgradeable 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(IERC20Upgradeable 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'
// solhint-disable-next-line max-line-length
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(IERC20Upgradeable token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_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(IERC20Upgradeable 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
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
interface IDecimals {
function decimals() external view returns (uint8);
}{
"optimizer": {
"enabled": true,
"runs": 1000
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {
"contracts/module/AMMModule.sol": {
"AMMModule": "0x70a20957f426740c92231430c4855b717770083d"
},
"contracts/module/CollateralModule.sol": {
"CollateralModule": "0xa4f2ff555862066f3161a5055dbb485b05fe0d14"
},
"contracts/module/PerpetualModule.sol": {
"PerpetualModule": "0x310d3cfaca28a32d17d183e8dec599c18543e8ac"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"keeper","type":"address"}],"name":"AddAMMKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"addedCash","type":"int256"},{"indexed":false,"internalType":"int256","name":"mintedShare","type":"int256"},{"indexed":false,"internalType":"int256","name":"addedPoolMargin","type":"int256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"keeper","type":"address"}],"name":"AddTraderKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOperator","type":"address"}],"name":"ClaimOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"governor","type":"address"},{"indexed":false,"internalType":"address","name":"shareToken","type":"address"},{"indexed":false,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"address","name":"oracle","type":"address"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"int256[9]","name":"baseParams","type":"int256[9]"},{"indexed":false,"internalType":"int256[9]","name":"riskParams","type":"int256[9]"}],"name":"CreatePerpetual","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"amount","type":"int256"}],"name":"DonateInsuranceFund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorCheckIn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"keeper","type":"address"}],"name":"RemoveAMMKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"returnedCash","type":"int256"},{"indexed":false,"internalType":"int256","name":"burnedShare","type":"int256"},{"indexed":false,"internalType":"int256","name":"removedPoolMargin","type":"int256"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"keeper","type":"address"}],"name":"RemoveTraderKeeper","type":"event"},{"anonymous":false,"inputs":[],"name":"RevokeOperator","type":"event"},{"anonymous":false,"inputs":[],"name":"RunLiquidityPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256[4]","name":"value","type":"int256[4]"}],"name":"SetLiquidityPoolParameter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"targetLeverage","type":"int256"}],"name":"SetTargetLeverage","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"amount","type":"int256"}],"name":"TransferExcessInsuranceFundToLP","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOperator","type":"address"}],"name":"TransferOperatorTo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"poolMargin","type":"int256"}],"name":"UpdatePoolMargin","type":"event"},{"inputs":[],"name":"MAX_PERPETUAL_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_CHECK_IN_TIMEOUT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256[4]","name":"liquidityPoolParams","type":"int256[4]"}],"name":"validateLiquidityPoolParameter","outputs":[],"stateMutability":"pure","type":"function"}]Contract Creation Code
615ebb62000027600b82828239805160001a60731461001a57fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106103605760003560e01c806393c80555116101d2578063c9ab75711161010e578063e3e28986116100ac578063e8c38c5c11610086578063e8c38c5c146108d1578063ebf1f268146108f1578063f20d689614610911578063fd2c4b4d1461093157610360565b8063e3e289861461087e578063e57d06a81461089e578063e806e25c146108b157610360565b8063ccef4f11116100e8578063ccef4f111461080b578063d767897e1461082b578063d94dacbd1461084b578063e1858ce91461085e57610360565b8063c9ab7571146107c3578063ca66f475146107e3578063cbf231c1146107eb57610360565b8063ac76a8511161017b578063bb89ac2411610155578063bb89ac2414610743578063bdc5e84914610763578063c01fa80814610783578063c3872ccf146107a357610360565b8063ac76a851146106f0578063b1a4182a14610710578063b2f6decc1461072357610360565b8063a54b4d78116101ac578063a54b4d781461069d578063a99d455a146106bd578063a9f677f5146106dd57610360565b806393c805551461064a578063960e38ae1461065d578063990d20521461067d57610360565b806344467389116102a15780635980576b1161024a578063690501e311610224578063690501e3146105e457806370f546ce1461060457806383a1eaa8146106175780638d3416411461063757610360565b80635980576b146105845780635a8610b4146105a457806367d0b253146105c457610360565b806348ea454c1161027b57806348ea454c146105245780634e74d930146105445780634f28feeb1461056457610360565b806344467389146104d157806344a9d80a146104f157806348d21d001461050457610360565b8063316ad4d71161030e57806338dbaf71116102e857806338dbaf711461045e5780633d792b6e1461047e5780633d92b9a01461049e5780633eb02737146104be57610360565b8063316ad4d71461043057806332958b4b14610438578063374331ff1461044b57610360565b8063299cbfda1161033f578063299cbfda146103d05780632d7966d8146103f05780632ef9dab91461041057610360565b8062addc0914610365578063028c4ff61461038e5780632621d4c8146103b0575b600080fd5b6103786103733660046150e0565b610944565b60405161038591906153a1565b60405180910390f35b81801561039a57600080fd5b506103ae6103a9366004614f91565b610a20565b005b8180156103bc57600080fd5b506103ae6103cb366004614f14565b610bba565b8180156103dc57600080fd5b506103ae6103eb366004614cac565b610cb4565b8180156103fc57600080fd5b506103ae61040b366004614eac565b610dcc565b81801561041c57600080fd5b506103ae61042b366004614f59565b6112a6565b610378611420565b6103786104463660046151ca565b611427565b610378610459366004614c94565b6114c8565b81801561046a57600080fd5b506103ae610479366004614f59565b6114e1565b81801561048a57600080fd5b506103ae610499366004615059565b61159d565b8180156104aa57600080fd5b506103ae6104b9366004614f38565b61165f565b6103786104cc366004615270565b611720565b8180156104dd57600080fd5b506103ae6104ec3660046150b5565b611790565b6103786104ff366004614c94565b6117c3565b81801561051057600080fd5b506103ae61051f3660046150b5565b6117d8565b81801561053057600080fd5b506103ae61053f366004614f59565b611806565b81801561055057600080fd5b506103ae61055f366004614c94565b6118d5565b81801561057057600080fd5b506103ae61057f366004614cac565b6119be565b81801561059057600080fd5b506103ae61059f366004614c94565b611a85565b8180156105b057600080fd5b506103ae6105bf366004614f91565b611ad8565b6105d76105d2366004614f91565b611b5d565b6040516103859190615396565b8180156105f057600080fd5b506103ae6105ff366004614e75565b611bbc565b610378610612366004614f38565b611c31565b81801561062357600080fd5b506103ae610632366004614e75565b611cd5565b610378610645366004615127565b611e60565b610378610658366004615210565b611f44565b81801561066957600080fd5b506103ae610678366004614f38565b6120f3565b81801561068957600080fd5b506103ae610698366004614c94565b6121fa565b8180156106a957600080fd5b506103ae6106b8366004614f91565b6123af565b8180156106c957600080fd5b506103ae6106d8366004614c94565b612545565b6103786106eb366004615195565b6127b6565b8180156106fc57600080fd5b506103ae61070b366004614e75565b6127f6565b61037861071e366004614c94565b612875565b81801561072f57600080fd5b506103ae61073e366004614f59565b6128ff565b81801561074f57600080fd5b506103ae61075e366004614dff565b6129eb565b81801561076f57600080fd5b506103ae61077e366004614c94565b612cb4565b81801561078f57600080fd5b506103ae61079e366004614f59565b612d59565b8180156107af57600080fd5b506103ae6107be366004614fbf565b612e1a565b8180156107cf57600080fd5b506103ae6107de366004614d17565b612ecd565b610378613120565b8180156107f757600080fd5b50610378610806366004614f38565b613125565b81801561081757600080fd5b50610378610826366004614f38565b6132c3565b81801561083757600080fd5b506103ae610846366004614e75565b613398565b6103ae610859366004614b6e565b613474565b61087161086c366004614c94565b6134e6565b6040516103859190615303565b81801561088a57600080fd5b506103ae610899366004614ee8565b613570565b6105d76108ac366004614cd0565b6135e5565b8180156108bd57600080fd5b506103ae6108cc366004614f38565b6136a1565b8180156108dd57600080fd5b506103ae6108ec366004614f91565b613870565b8180156108fd57600080fd5b506103ae61090c366004615023565b6138ff565b81801561091d57600080fd5b506103ae61092c366004615023565b613985565b61037861093f366004615195565b613a0b565b60008061095087613a62565b9050600061095e8888613a90565b90508061098b57610982600061097c6109778b8b613ab1565b613aeb565b90613af8565b92505050610a17565b61099f866109998a8a613ab1565b90613b10565b92506109b96109b2826109998888613b45565b8490613b45565b925080156109d75760128801546109d4906109b29088613b10565b92505b6109eb6109e48288613b45565b8490613baa565b9250610a056109fe6109778a8a86613bf0565b8490613c2e565b9250610a12836000613af8565b925050505b95945050505050565b83600f01548310610a4c5760405162461bcd60e51b8152600401610a4390615b4c565b60405180910390fd5b610a5884848484611ad8565b60008381526010850160205260409081902090517fd13cfaa900000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac9163d13cfaa991610abc919086908690600401615c28565b60206040518083038186803b158015610ad457600080fd5b505af4158015610ae8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0c9190614b89565b15610bb45783546040517f439adb78000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b03169063439adb7890610b609085908790600401615355565b602060405180830381600087803b158015610b7a57600080fd5b505af1158015610b8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bb29190614b89565b505b50505050565b600f82015460005b81811015610bb457600081815260108501602052604090206002600182015460ff166004811115610bef57fe5b14610bfa5750610cac565b610c0381613c3e565b8060010160019054906101000a90046001600160a01b03166001600160a01b031663d1cc99766040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610c5557600080fd5b505af1158015610c69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c8d9190614b89565b8015610c97575083155b15610caa57610caa85826000015461165f565b505b600101610bc2565b610cbd82613ce2565b6001600160a01b0316816001600160a01b031614610ced5760405162461bcd60e51b8152600401610a43906154d3565b6001820180546001600160a01b0319166001600160a01b038316179055610d1742620d2f00613d08565b600c8301556002820180546001600160a01b03191690558154604051626e20ad60e11b8152620100009091046001600160a01b03169062dc415a90610d629030908590600401615317565b600060405180830381600087803b158015610d7c57600080fd5b505af1158015610d90573d6000803e3d6000fd5b50506040516001600160a01b03841692507f52663c8b1b35a6c6b1a720c76465d84626b103e3990f2f59618125f73c588cf09150600090a25050565b600480850154604080517f18160ddd00000000000000000000000000000000000000000000000000000000815290516001600160a01b0390921692600092610e6f9285926318160ddd928282019260209290829003018186803b158015610e3257600080fd5b505afa158015610e46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6a9190614be3565b613d62565b90506000808085158015610e835750600087135b15610f5b576040517feeffc15d0000000000000000000000000000000000000000000000000000000081527370a20957f426740c92231430c4855b717770083d9063eeffc15d90610edc908c9088908c906004016153aa565b60806040518083038186803b158015610ef457600080fd5b505af4158015610f08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2c9190614c4d565b9298509094509250905060008613610f565760405162461bcd60e51b8152600401610a43906155fc565b611054565b600086138015610f69575086155b1561103c576040517f68dddaa60000000000000000000000000000000000000000000000000000000081527370a20957f426740c92231430c4855b717770083d906368dddaa690610fc2908c9088908b906004016153aa565b60806040518083038186803b158015610fda57600080fd5b505af4158015610fee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110129190614c4d565b9299509094509250905060008713610f565760405162461bcd60e51b8152600401610a4390615735565b60405162461bcd60e51b8152600401610a43906159dc565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038616906370a0823190611099908b90600401615303565b60206040518083038186803b1580156110b157600080fd5b505afa1580156110c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110e99190614be3565b6110f288613daa565b11156111105760405162461bcd60e51b8152600401610a43906158a3565b6000611126836111208987613b45565b90613b45565b90506111318a6117c3565b8113156111505760405162461bcd60e51b8152600401610a43906156fe565b856001600160a01b0316639dc29fac8a6111698b613daa565b6040518363ffffffff1660e01b8152600401611186929190615355565b600060405180830381600087803b1580156111a057600080fd5b505af11580156111b4573d6000803e3d6000fd5b50506040516346a8e88f60e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d1492506346a8e88f91506111f3908d908d908c90600401615c28565b60006040518083038186803b15801561120b57600080fd5b505af415801561121f573d6000803e3d6000fd5b50505060128b0154611232915085613b45565b60128b015560138a01546112469084613b45565b60138b01556112558a82613e01565b886001600160a01b03167f3713fb0b3de95a2c80ba2e2cf5229918ac276b7d04f1526616e03c37b309ba58888a85604051611292939291906153aa565b60405180910390a250505050505050505050565b60008281526010840160205260408120601281015490911280156112d257508060120154816002015412155b156112e7576112e78484848460120154613870565b6112f381603001613e3d565b158061141157506040517fd87384fe00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90635e91c9f8908390839063d87384fe906113549084906004016153a1565b60206040518083038186803b15801561136c57600080fd5b505af4158015611380573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a49190614b52565b6040518363ffffffff1660e01b81526004016113c1929190615c47565b60206040518083038186803b1580156113d957600080fd5b505af41580156113ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114119190614b89565b15610bb457610bb484846136a1565b620d2f0081565b60008061143387613a62565b6020870151600b8901549192509061145890611450908390613b10565b885190613b45565b92508061146d57610982600061097c85613aeb565b6114778387613b10565b925061148a6109b2826109998888613b45565b925080156114a85760128801546114a5906109b29088613b10565b92505b6114b56109e48288613b45565b9250610a056109fe6109778a8a86613a0b565b60168101546000906114db906001613e48565b92915050565b82600f015482106115045760405162461bcd60e51b8152600401610a4390615b4c565b60008281526010840160205260409081902090517f5b30d53600000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90635b30d536906115679084908690600401615c47565b60006040518083038186803b15801561157f57600080fd5b505af4158015611593573d6000803e3d6000fd5b5050505050505050565b84600f015484106115c05760405162461bcd60e51b8152600401610a4390615b4c565b60008481526010860160205260409081902090517f967b9f2b00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac9063967b9f2b90611627908490889088908890600401615c73565b60006040518083038186803b15801561163f57600080fd5b505af4158015611653573d6000803e3d6000fd5b50505050505050505050565b81600f015481106116825760405162461bcd60e51b8152600401610a4390615b4c565b61168c8282613125565b5060008181526010830160205260409081902090516313743a2360e31b815273310d3cfaca28a32d17d183e8dec599c18543e8ac91639ba1d118916116d491906004016153a1565b60006040518083038186803b1580156116ec57600080fd5b505af4158015611700573d6000803e3d6000fd5b5050505061170f826002613e58565b61171c5761171c82613ebc565b5050565b600080600061174561173f888a60200151613b4590919063ffffffff16565b88613f97565b9150915081600014158015611758575080155b156117715761176a8989848989611427565b9250611784565b6117818989898986868b8b611f44565b92505b50509695505050505050565b8061179a576117be565b600082815260108401602052604090206117b49082613ff5565b6117be8382613e01565b505050565b60006114db8283600f0154611c31565b919050565b806117e2576117be565b600082815260108401602052604090206117fc9082614031565b6117be8382614088565b82600f015482106118295760405162461bcd60e51b8152600401610a4390615b4c565b6000828152601084016020526040902060390161184681836140b8565b6118625760405162461bcd60e51b8152600401610a439061550a565b600061186e82846140cd565b90508061188d5760405162461bcd60e51b8152600401610a4390615948565b826001600160a01b03167fec00c45379d4f167916464b153f75be2434e0ca4c60946c0f99723f3090cf3b2856040516118c691906153a1565b60405180910390a25050505050565b600f810154806118f75760405162461bcd60e51b8152600401610a439061576a565b60005b8181101561198557600081815260108401602052604090819020905163c94606dd60e01b815273310d3cfaca28a32d17d183e8dec599c18543e8ac9163c94606dd9161194991906004016153a1565b60006040518083038186803b15801561196157600080fd5b505af4158015611975573d6000803e3d6000fd5b5050600190920191506118fa9050565b50815460ff191660011782556040517f7b14db60328776e0e3ff7638ea4234333f5ba91c16cad0a6112c56c288f1bfde90600090a15050565b6001600160a01b0381166119e45760405162461bcd60e51b8152600401610a439061586c565b6119ed826140e2565b6001600160a01b0316816001600160a01b03161415611a1e5760405162461bcd60e51b8152600401610a43906156a1565b6002820180546001600160a01b0319166001600160a01b038316179055611a4842620d2f00613d08565b600c8301556040516001600160a01b038216907f865f83d7f78904d112c0cb736765ebd7d94da5905d36dca4df70d2d188fd517c90600090a25050565b611a9242620d2f00613d08565b600c820155611aa0816140e2565b6001600160a01b03167f838ffdc993783254a13d0672ce593ffa0dffb3219bab649f2f9f572f4d0fb37860405160405180910390a250565b604051631885d4f160e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d1490631885d4f190611b1390879086908690600401615c28565b60006040518083038186803b158015611b2b57600080fd5b505af4158015611b3f573d6000803e3d6000fd5b50505060008481526010860160205260409020610bb4915082613ff5565b6000838152601085016020526040812081611b81611b7b8387613a90565b85614108565b90506000611b8e83613a62565b905081611ba557611ba0838783614121565b611bb0565b611bb0838783614159565b98975050505050505050565b604051631885d4f160e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d1490631885d4f190611bf790869086908690600401615c28565b60006040518083038186803b158015611c0f57600080fd5b505af4158015611c23573d6000803e3d6000fd5b505050506117be8382614088565b600f820154600090815b81811015611cbc576000818152601086016020526040902084821480611c7457506002600182015460ff166004811115611c7157fe5b14155b15611c7f5750611cb4565b6000611c8a82613a62565b9050611caf611ca8611c9d843085614171565b6111208530866141a9565b86906141e0565b945050505b600101611c3b565b506009840154611ccd9083906141e0565b949350505050565b60008113611cf55760405162461bcd60e51b8152600401610a439061549c565b6040517ff72ee9c80000000000000000000000000000000000000000000000000000000081526000907370a20957f426740c92231430c4855b717770083d9063f72ee9c890611d4c908790859087906004016153aa565b604080518083038186803b158015611d6357600080fd5b505af4158015611d77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d9b9190614c2a565b604051631885d4f160e01b815290925073a4f2ff555862066f3161a5055dbb485b05fe0d149150631885d4f190611dda90879087908790600401615c28565b60006040518083038186803b158015611df257600080fd5b505af4158015611e06573d6000803e3d6000fd5b50505050611e148483614088565b826001600160a01b03167fc3919ccd011a100c0148bab574a26f47f5db31c2bc16903381f5429a1a4f1f9583600084604051611e52939291906153aa565b60405180910390a250505050565b600080611e6c8a613a62565b90506000611e7b8b8b846141a9565b90506000611e8a8c8c87614245565b905060008113611eac5760405162461bcd60e51b8152600401610a4390615b15565b611ec18382611eba8a61439c565b91906143b4565b9350611ed39050896111208d8d613a90565b15801590611edf575086155b15611f0657611eff88611120611ef5858d613b10565b611120878a6141e0565b9250611f24565b611f21816111208d60120154866141e090919063ffffffff16565b92505b611f356109fe6109778d8d86613bf0565b9b9a5050505050505050505050565b600080611f508a613a62565b90506000611f5f8b8b846127b6565b9050600080611f738663ffffffff166143f1565b90506000611f868763ffffffff166143fc565b9050818015611f925750805b15611faf5760405162461bcd60e51b8152600401610a4390615578565b8115611fc1578c604001519250611fd3565b611fd08763ffffffff1661440f565b92505b600c8e0154611ff45760405162461bcd60e51b8152600401610a439061566a565b60006120158f600c0154670de0b6b3a7640000613baa90919063ffffffff16565b905083156120235783612029565b60358f01545b93506120358482613af8565b93505050506000811361205a5760405162461bcd60e51b8152600401610a4390615b15565b6120688382611eba8a61439c565b60208c015190945061207a908b613b45565b15801590612086575087155b156120ad576120a68961112061209c868e613b10565b611120888b6141e0565b93506120cb565b6120c8826111208e60120154876141e090919063ffffffff16565b93505b6120e36120dc6109778e8e87613a0b565b8590613c2e565b9c9b505050505050505050505050565b8082600a0154106121035761171c565b600061211f610e6a84600a01548461442390919063ffffffff16565b600f84015490915060005b818110156121ee57600081815260108601602052604090206002600182015460ff16600481111561215757fe5b1461216257506121e6565b6040517f8f27c08f00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90638f27c08f906121b49084908890600401615ca4565b60006040518083038186803b1580156121cc57600080fd5b505af41580156121e0573d6000803e3d6000fd5b50505050505b60010161212a565b505050600a9190910155565b6040517fc12b64d500000000000000000000000000000000000000000000000000000000815260009081907370a20957f426740c92231430c4855b717770083d9063c12b64d59061224f9086906004016153a1565b604080518083038186803b15801561226657600080fd5b505af415801561227a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061229e9190614bfb565b915091507fc7ca5bca6c98d1e97e37f3d5325b86b0ee183f9c17703c11bbc7e1cf4bce480c826040516122d191906153a1565b60405180910390a1806122e357600091505b600f83015460005b81811015610bb257600081815260108601602052604090206002600182015460ff16600481111561231857fe5b1461232357506123a7565b6040517f3d1e253000000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90633d1e2530906123759084908990600401615ca4565b60006040518083038186803b15801561238d57600080fd5b505af41580156123a1573d6000803e3d6000fd5b50505050505b6001016122eb565b83600f015483106123d25760405162461bcd60e51b8152600401610a4390615b4c565b600083815260108501602052604090206123ec8585613125565b506040517fc480202a00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac9063c480202a9061244190849087908790600401615c28565b60206040518083038186803b15801561245957600080fd5b505af415801561246d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124919190614b89565b156125395784546040517ff81d65f1000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b03169063f81d65f1906124e59086908890600401615355565b602060405180830381600087803b1580156124ff57600080fd5b505af1158015612513573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125379190614b89565b505b610bb285858585613870565b600081600f0154116125695760405162461bcd60e51b8152600401610a4390615bf1565b60008060008084600f0154905060005b8181101561260757600081815260108701602052604090206002600182015460ff1660048111156125a657fe5b146125b157506125ff565b60006125bc82613a62565b90506125d36125cc833084614480565b87906141e0565b95506125e3611ca8833084614171565b94506125fa6125f38330846141a9565b88906141e0565b965050505b600101612579565b5060098501546126189085906141e0565b9350600082612628576000612634565b612634858460016144b8565b905060005b8281101561277f57600081815260108801602052604090206002600182015460ff16600481111561266657fe5b146126715750612777565b600061267c82613a62565b90506000612698856001612691863087614171565b91906144d6565b90506126a58330846141a9565b985060006126b3828b613b45565b905060008113156126d9576126c98430836144f4565b6126d48b8683611790565b6126fb565b60008112156126fb576126ed8430836144f4565b6126fb8b8661051f84613aeb565b600085815260108c0160205260409081902090516313743a2360e31b815273310d3cfaca28a32d17d183e8dec599c18543e8ac91639ba1d1189161274291906004016153a1565b60006040518083038186803b15801561275a57600080fd5b505af415801561276e573d6000803e3d6000fd5b50505050505050505b600101612639565b506000866009015412156127a55760405162461bcd60e51b8152600401610a4390615a81565b6127ae86613ebc565b505050505050565b6000611ccd6127e16127d986600b01548660200151613b1090919063ffffffff16565b855190613b45565b60208501516127f09085613b10565b906141e0565b80612800576117be565b6040516346a8e88f60e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d14906346a8e88f9061283b90869086908690600401615c28565b60006040518083038186803b15801561285357600080fd5b505af4158015612867573d6000803e3d6000fd5b505050506117be8382613e01565b60008160000160029054906101000a90046001600160a01b03166001600160a01b031663492c26b66040518163ffffffff1660e01b815260040160206040518083038186803b1580156128c757600080fd5b505afa1580156128db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114db9190614be3565b6001600160a01b0381166129255760405162461bcd60e51b8152600401610a4390615b83565b600082815260108401602052604080822090516324d41bd760e11b815273310d3cfaca28a32d17d183e8dec599c18543e8ac916349a837ae9161296d91908690600401615c47565b60206040518083038186803b15801561298557600080fd5b505af4158015612999573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129bd9190614be3565b9050600081136129df5760405162461bcd60e51b8152600401610a4390615541565b610bb484848484613870565b603086600f015410612a0f5760405162461bcd60e51b8152600401610a4390615633565b600f86015460008181526010880160205260409081902090517f4a5f050e00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90634a5f050e90612a8190849086908c908c908c908c908c90600401615cb2565b60006040518083038186803b158015612a9957600080fd5b505af4158015612aad573d6000803e3d6000fd5b5050505060008860000160029054906101000a90046001600160a01b03166001600160a01b03166307c4d4866040518163ffffffff1660e01b815260040160206040518083038186803b158015612b0357600080fd5b505afa158015612b17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b3b9190614b52565b6040517fb71906580000000000000000000000000000000000000000000000000000000081529091506001600160a01b0382169063b719065890612b859030908790600401615355565b600060405180830381600087803b158015612b9f57600080fd5b505af1158015612bb3573d6000803e3d6000fd5b50508a5460ff16159150612c2b90505760405163c94606dd60e01b815273310d3cfaca28a32d17d183e8dec599c18543e8ac9063c94606dd90612bfa9085906004016153a1565b60006040518083038186803b158015612c1257600080fd5b505af4158015612c26573d6000803e3d6000fd5b505050505b600f890180546001019055600389015460048a01547f8c198f1eb08b1a98128c3f5d8d36e943e11147088c5905a7edeb23732635b8df9185916001600160a01b039182169116612c7a8d6140e2565b60088e0154604051612ca195949392918f916001600160a01b03909116908f908f90615d07565b60405180910390a1505050505050505050565b6001810180546001600160a01b03191690558054604051626e20ad60e11b8152620100009091046001600160a01b03169062dc415a90612cfb903090600090600401615317565b600060405180830381600087803b158015612d1557600080fd5b505af1158015612d29573d6000803e3d6000fd5b50506040517fda0987738091b194f50e1fef7257fb806ac0b26fd3607a7fff79a9efa9e4804c925060009150a150565b82600f01548210612d7c5760405162461bcd60e51b8152600401610a4390615b4c565b60008281526010840160205260409020603901612d9981836140b8565b15612db65760405162461bcd60e51b8152600401610a43906157c7565b6000612dc2828461452b565b905080612de15760405162461bcd60e51b8152600401610a43906159a5565b826001600160a01b03167ffb9de28e25ee60a69b50d1ad2d6aeb820ad2cb5dceeedefe9282d37fb9c77448856040516118c691906153a1565b6000868152601088016020526040812090612e358288613a90565b905060008080612e4e612e48858b613b45565b8a613f97565b9150915081600014158015612e61575080155b15612e7a57612e73858b848b8b610944565b9250612e8d565b612e8a858b8b8b86868d8d611e60565b92505b6000831315612ea757612ea28c8c8c86610a20565b612ebf565b6000831215612ebf57612ebf8c8c8c6106b887613aeb565b505050505050505050505050565b6001600160a01b038516612ef35760405162461bcd60e51b8152600401610a4390615ade565b6001600160a01b038216612f195760405162461bcd60e51b8152600401610a43906158da565b60008060008084806020019051810190612f339190614ba5565b929650909450925090506001811015612f5e5760405162461bcd60e51b8152600401610a439061559f565b6040517f5fc2e39200000000000000000000000000000000000000000000000000000000815273a4f2ff555862066f3161a5055dbb485b05fe0d1490635fc2e39290612fb2908e908d908d90600401615c28565b60006040518083038186803b158015612fca57600080fd5b505af4158015612fde573d6000803e3d6000fd5b50505050898b60000160026101000a8154816001600160a01b0302191690836001600160a01b03160217905550896001600160a01b03166316d6b5f66040518163ffffffff1660e01b815260040160206040518083038186803b15801561304457600080fd5b505afa158015613058573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307c9190614b52565b60058c0180546001600160a01b03199081166001600160a01b039384161790915560018d0180549091169189169190911790556130bc42620d2f00613d08565b600c8c015560038b0180546001600160a01b039097166001600160a01b0319978816811790915560048c0180549097161790955589549215156101000261ff0019909316929092178955601189015560158801555060169095019490945550505050565b603081565b600082600f0154821061314a5760405162461bcd60e51b8152600401610a4390615b4c565b600082815260108401602052604090206002600182015460ff16600481111561316f57fe5b1461317e5760009150506114db565b6040517f84da931000000000000000000000000000000000000000000000000000000000815260009073310d3cfaca28a32d17d183e8dec599c18543e8ac906384da9310906131d19085906004016153a1565b60206040518083038186803b1580156131e957600080fd5b505af41580156131fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132219190614be3565b905080613233576000925050506114db565b600081131561327357600282015461324c908290613af8565b90506132633061325b83613aeb565b8491906144f4565b61326e8585836117d8565b611ccd565b600061327f8686611c31565b90506000811361329557600093505050506114db565b6132a28161097c8461439c565b91506132af8330846144f4565b6132ba868684611790565b50949350505050565b600081156114db5760128301546000906132dd90846141e0565b9050600083131561334a578360110154811315613345576011840154613304908290613b45565b9150836011015490507fc0357ed6fd11fd16f5c820f18e7835dcbd960c75f9bb28350a00573ac6c2b2e38260405161333c91906153a1565b60405180910390a15b61338d565b600081121561338d57601384015461336290826141e0565b60138501819055600013156133895760405162461bcd60e51b8152600401610a439061542e565b5060005b601284015592915050565b600081136133b85760405162461bcd60e51b8152600401610a4390615465565b604051631885d4f160e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d1490631885d4f1906133f390869086908690600401615c28565b60006040518083038186803b15801561340b57600080fd5b505af415801561341f573d6000803e3d6000fd5b50505060138401546134329150826141e0565b60138401556040517f35501c76a025a907c147d9a58f7a43504b042e3aeb4baff4fab606463bd8524b906134679083906153a1565b60405180910390a1505050565b6020810151600013156134995760405162461bcd60e51b8152600401610a43906157fe565b6040810151600013156134be5760405162461bcd60e51b8152600401610a4390615bba565b6060810151600113156134e35760405162461bcd60e51b8152600401610a4390615a4a565b50565b60008160000160029054906101000a90046001600160a01b03166001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561353857600080fd5b505afa15801561354c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114db9190614b52565b61357981613474565b8051825461ff0019169015156101000217825560208101516011830155604080820151601584015560608201516016840155517fac5bf8754fef31e548d743999f2235f9930fe919cf7ea07b525d98c8485439eb906135d990839061536e565b60405180910390a15050565b6000826001600160a01b0316846001600160a01b03161480610a17575060058501546040517fc2f3da1d0000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063c2f3da1d9061365190879087908790600401615331565b60206040518083038186803b15801561366957600080fd5b505afa15801561367d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a179190614b89565b81600f015481106136c45760405162461bcd60e51b8152600401610a4390615b4c565b60008181526010830160205260409081902090517f8e66efaf00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90638e66efaf906137279084903090600401615c47565b60006040518083038186803b15801561373f57600080fd5b505af4158015613753573d6000803e3d6000fd5b50506040517f39d281eb00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac92506339d281eb91506137a79084906004016153a1565b60006040518083038186803b1580156137bf57600080fd5b505af41580156137d3573d6000803e3d6000fd5b50506040516324d41bd760e11b81526000925073310d3cfaca28a32d17d183e8dec599c18543e8ac91506349a837ae906138139085903090600401615c47565b60206040518083038186803b15801561382b57600080fd5b505af415801561383f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138639190614be3565b9050610bb48484836117d8565b8061387a57610bb4565b6040516346a8e88f60e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d14906346a8e88f906138b590879086908690600401615c28565b60006040518083038186803b1580156138cd57600080fd5b505af41580156138e1573d6000803e3d6000fd5b50505060008481526010860160205260409020610bb4915082614031565b82600f015482106139225760405162461bcd60e51b8152600401610a4390615b4c565b60008281526010840160205260409081902090517fb7ce1b2700000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac9063b7ce1b27906115679084908690600401615c5e565b82600f015482106139a85760405162461bcd60e51b8152600401610a4390615b4c565b60008281526010840160205260409081902090517ffc13872000000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac9063fc138720906115679084908690600401615c5e565b6000808360200151600014613a4e57613a4985601201546127f0613a4488600c0154610999888a60200151613b1090919063ffffffff16565b61439c565b613a51565b60005b9050610a17816111208787876127b6565b60006002600183015460ff166004811115613a7957fe5b14613a885760088201546114db565b506006015490565b6001600160a01b031660009081526034909101602052604090206001015490565b6001600160a01b03811660009081526034830160205260408120600b8401546001820154611ccd91613ae39190613b10565b825490613b45565b60006114db600083613b45565b6000818312613b075781613b09565b825b9392505050565b6000670de0b6b3a7640000613b36613b288585614540565b670de0b6b3a76400006145e9565b81613b3d57fe5b059392505050565b6000818303818312801590613b5a5750838113155b80613b6f5750600083128015613b6f57508381135b613b095760405162461bcd60e51b8152600401808060200182810382526024815260200180615e626024913960400191505060405180910390fd5b600080821215613bcb57613bbd82613aeb565b9150613bc883613aeb565b92505b613b0982613bea613be486670de0b6b3a7640000614540565b856145e9565b90614668565b600080613bfd8585613a90565b15613c1a57613c1585601201546127f0878787614171565b613c1d565b60005b9050610a17816111208787876141a9565b600081831215613b075781613b09565b600181015461010090046001600160a01b0316613c636006830182630e222f9b614720565b613c7e82600401826001600160a01b031663ccbdbee2614720565b815460068301546007840154600485015460058601546040516001600160a01b038716957fec91322ffa6f23802efae915b1fc28a37d2d0986d25d2073c0a661498ad0076795613cd695919490939192909190615d66565b60405180910390a25050565b600081600c0154421115613cf75760006114db565b50600201546001600160a01b031690565b600082820183811015613b09576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000600160ff1b8210613da65760405162461bcd60e51b8152600401808060200182810382526028815260200180615e3a6028913960400191505060405180910390fd5b5090565b600080821215613da6576040805162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f736974697665604482015290519081900360640190fd5b6000811215613e225760405162461bcd60e51b8152600401610a4390615a13565b6009820154613e319082613b45565b82600901819055505050565b60006114db826147d2565b600081831015613b075781613b09565b600f820154600090815b81811015613eb157836004811115613e7657fe5b600082815260108701602052604090206001015460ff166004811115613e9857fe5b1415613ea9576001925050506114db565b600101613e62565b506000949350505050565b6000613ec7826140e2565b9050600082601301541380613ee0575060008260120154135b8015613ef457506001600160a01b03811615155b1561171c576000613f16836012015484601301546141e090919063ffffffff16565b60006013850181905560128501556040516346a8e88f60e01b815290915073a4f2ff555862066f3161a5055dbb485b05fe0d14906346a8e88f90613f6290869086908690600401615c28565b60006040518083038186803b158015613f7a57600080fd5b505af4158015613f8e573d6000803e3d6000fd5b50505050505050565b600080613fa484846147d6565b15613fb457506000905081613fee565b613fbd8361439c565b613fc68561439c565b12613fd657508190506000613fee565b613fdf84613aeb565b613fe985856141e0565b915091505b9250929050565b60008112156140165760405162461bcd60e51b8152600401610a43906153f7565b600282015461402590826141e0565b82600201819055505050565b60008112156140525760405162461bcd60e51b8152600401610a43906153f7565b60028201546140619082613b45565b600283018190556000131561171c5760405162461bcd60e51b8152600401610a4390615835565b60008112156140a95760405162461bcd60e51b8152600401610a4390615911565b6009820154613e3190826141e0565b6000613b09836001600160a01b0384166147f9565b6000613b09836001600160a01b038416614811565b600081600c01544211156140f75760006114db565b50600101546001600160a01b031690565b600082614117575060006114db565b613b0983836147d6565b60008061412e8585613a90565b1561413d578460120154614140565b60005b90508061414e8686866141a9565b121595945050505050565b600080614167858585613bf0565b1215949350505050565b600c8301546001600160a01b03831660009081526034850160205260408120600101549091611ccd91613a4491906109999086613b10565b6000611ccd6141b88585613ab1565b6001600160a01b03851660009081526034870160205260409020600101546127f09085613b10565b60008282018183128015906141f55750838112155b8061420a575060008312801561420a57508381125b613b095760405162461bcd60e51b8152600401808060200182810382526021815260200180615dd16021913960400191505060405180910390fd5b600083600c0154600014156142a1576040805162461bcd60e51b815260206004820152601c60248201527f696e697469616c4d617267696e52617465206973206e6f742073657400000000604482015290519081900360640190fd5b60006142c285600c0154670de0b6b3a7640000613baa90919063ffffffff16565b905060006142d58463ffffffff166143f1565b905060006142e88563ffffffff166143fc565b90508180156142f45750805b15614336576040805162461bcd60e51b815260206004820152600d60248201526c696e76616c696420666c61677360981b604482015290519081900360640190fd5b8115614361576001600160a01b03861660009081526034880160205260409020600201549350614373565b6143708563ffffffff1661440f565b93505b831561437f5783614385565b60358701545b93506143918484613af8565b979650505050505050565b600080821215613da6576143af82613aeb565b6114db565b6000806143c18585614540565b905060008312156143e3576143d583613aeb565b92506143e081613aeb565b90505b610a1783613bea83866145e9565b630800000016151590565b6000806144088361440f565b1392915050565b60071c620fffff16662386f26fc100000290565b60008282111561447a576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b600d8301546001600160a01b03831660009081526034850160205260408120600101549091611ccd91613a4491906109999086613b10565b6000611ccd6144cf85670de0b6b3a7640000614540565b84846148d7565b6000611ccd6144e58585614540565b670de0b6b3a7640000846148d7565b806144fe576117be565b6001600160a01b03821660009081526034840160205260409020805461452490836141e0565b9055505050565b6000613b09836001600160a01b0384166149b4565b60008261454f575060006114db565b826000191480156145635750600160ff1b82145b1561459f5760405162461bcd60e51b8152600401808060200182810382526027815260200180615e136027913960400191505060405180910390fd5b828202828482816145ac57fe5b0514613b095760405162461bcd60e51b8152600401808060200182810382526027815260200180615e136027913960400191505060405180910390fd5b600080821361463f576040805162461bcd60e51b815260206004820152601f60248201527f726f756e6448616c665570206f6e6c7920737570706f7274732079203e203000604482015290519081900360640190fd5b6000831261465b5761465483600284056141e0565b90506114db565b613b098360028405613b45565b6000816146bc576040805162461bcd60e51b815260206004820181905260248201527f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f604482015290519081900360640190fd5b816000191480156146d05750600160ff1b83145b1561470c5760405162461bcd60e51b8152600401808060200182810382526021815260200180615df26021913960400191505060405180910390fd5b600082848161471757fe5b05949350505050565b60008083836040518163ffffffff1660e01b81526004016040805180830381600087803b15801561475057600080fd5b505af1158015614764573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147889190614c82565b9150915060008213801561479b57508015155b6147b75760405162461bcd60e51b8152600401610a43906153c0565b84600101548110610bb2579084556001909301929092555050565b5490565b60008215806147e3575081155b156147f0575060016114db565b501860ff1d1590565b60009081526001919091016020526040902054151590565b600081815260018301602052604081205480156148cd578354600019808301919081019060009087908390811061484457fe5b906000526020600020015490508087600001848154811061486157fe5b60009182526020808320909101929092558281526001898101909252604090209084019055865487908061489157fe5b600190038181906000526020600020016000905590558660010160008781526020019081526020016000206000905560019450505050506114db565b60009150506114db565b60008261492b576040805162461bcd60e51b815260206004820152601060248201527f6469766973696f6e206279207a65726f00000000000000000000000000000000604482015290519081900360640190fd5b6149358484614668565b905082848161494057fe5b0761494a57613b09565b600061495685856147d6565b9050600083600181111561496657fe5b1480156149705750805b15614983576149808260016141e0565b91505b600183600181111561499157fe5b14801561499c575080155b156149ac57610a17826001613b45565b509392505050565b60006149c083836147f9565b6149f6575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556114db565b5060006114db565b600082601f830112614a0e578081fd5b6040516080810181811067ffffffffffffffff82111715614a2b57fe5b6040529050808260808101851015614a4257600080fd5b60005b6004811015614a64578135835260209283019290910190600101614a45565b50505092915050565b8061012081018310156114db57600080fd5b600082601f830112614a8f578081fd5b61012060405181810181811067ffffffffffffffff82111715614aae57fe5b60405291508183828101861015614ac457600080fd5b600092505b6009831015614a6457803582526001929092019160209182019101614ac9565b600060608284031215614afa578081fd5b6040516060810181811067ffffffffffffffff82111715614b1757fe5b80604052508091508235815260208301356020820152604083013560408201525092915050565b803563ffffffff811681146117d357600080fd5b600060208284031215614b63578081fd5b8151613b0981615dad565b600060808284031215614b7f578081fd5b613b0983836149fe565b600060208284031215614b9a578081fd5b8151613b0981615dc2565b60008060008060808587031215614bba578283fd5b8451614bc581615dc2565b60208601516040870151606090970151919890975090945092505050565b600060208284031215614bf4578081fd5b5051919050565b60008060408385031215614c0d578182fd5b825191506020830151614c1f81615dc2565b809150509250929050565b60008060408385031215614c3c578182fd5b505080516020909101519092909150565b60008060008060808587031215614c62578384fd5b505082516020840151604085015160609095015191969095509092509050565b60008060408385031215614c3c578081fd5b600060208284031215614ca5578081fd5b5035919050565b60008060408385031215614cbe578182fd5b823591506020830135614c1f81615dad565b60008060008060808587031215614ce5578182fd5b843593506020850135614cf781615dad565b92506040850135614d0781615dad565b9396929550929360600135925050565b600080600080600080600060e0888a031215614d31578485fd5b87359650602080890135614d4481615dad565b96506040890135614d5481615dad565b9550606089013594506080890135614d6b81615dad565b935060a0890135614d7b81615dad565b925060c089013567ffffffffffffffff80821115614d97578384fd5b818b0191508b601f830112614daa578384fd5b813581811115614db657fe5b614dc8601f8201601f19168501615d89565b91508082528c84828501011115614ddd578485fd5b8084840185840137848482840101525080935050505092959891949750929550565b6000806000806000806104c08789031215614e18578384fd5b863595506020870135614e2a81615dad565b9450614e398860408901614a6d565b9350614e49886101608901614a6d565b9250614e59886102808901614a6d565b9150614e69886103a08901614a6d565b90509295509295509295565b600080600060608486031215614e89578081fd5b833592506020840135614e9b81615dad565b929592945050506040919091013590565b60008060008060808587031215614ec1578182fd5b843593506020850135614ed381615dad565b93969395505050506040820135916060013590565b60008060a08385031215614efa578182fd5b82359150614f0b84602085016149fe565b90509250929050565b60008060408385031215614f26578182fd5b823591506020830135614c1f81615dc2565b60008060408385031215614f4a578182fd5b50508035926020909101359150565b600080600060608486031215614f6d578081fd5b83359250602084013591506040840135614f8681615dad565b809150509250925092565b60008060008060808587031215614fa6578182fd5b84359350602085013592506040850135614d0781615dad565b600080600080600080600060e0888a031215614fd9578081fd5b87359650602088013595506040880135614ff281615dad565b9450606088013593506080880135925060a0880135915061501560c08901614b3e565b905092959891949750929550565b60008060006101608486031215615038578081fd5b83359250602084013591506150508560408601614a7f565b90509250925092565b60008060008060006103a08688031215615071578283fd5b85359450602086013593506150898760408801614a7f565b9250615099876101608801614a7f565b91506150a9876102808801614a7f565b90509295509295909350565b6000806000606084860312156150c9578081fd5b505081359360208301359350604090920135919050565b600080600080600060a086880312156150f7578283fd5b85359450602086013561510981615dad565b94979496505050506040830135926060810135926080909101359150565b600080600080600080600080610100898b031215615143578182fd5b88359750602089013561515581615dad565b965060408901359550606089013594506080890135935060a0890135925060c0890135915061518660e08a01614b3e565b90509295985092959890939650565b600080600060a084860312156151a9578081fd5b833592506151ba8560208601614ae9565b9150608084013590509250925092565b600080600080600060e086880312156151e1578283fd5b853594506151f28760208801614ae9565b949794965050505060808301359260a08101359260c0909101359150565b600080600080600080600080610140898b03121561522c578182fd5b8835975061523d8a60208b01614ae9565b96506080890135955060a0890135945060c0890135935060e0890135925061010089013591506151866101208a01614b3e565b6000806000806000806101008789031215615289578384fd5b8635955061529a8860208901614ae9565b94506080870135935060a0870135925060c08701359150614e6960e08801614b3e565b8060005b6009811015610bb45781358452602093840193909101906001016152c1565b8060005b6009811015610bb45781518452602093840193909101906001016152e4565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b60808101818360005b6004811015614a64578151835260209283019290910190600101615377565b901515815260200190565b90815260200190565b9283526020830191909152604082015260600190565b60208082526012908201527f696e76616c696420707269636520646174610000000000000000000000000000604082015260600190565b60208082526012908201527f616d6f756e74206973206e656761746976650000000000000000000000000000604082015260600190565b6020808252601f908201527f6e6567617469766520646f6e6174656420696e737572616e63652066756e6400604082015260600190565b6020808252600e908201527f696e76616c696420616d6f756e74000000000000000000000000000000000000604082015260600190565b6020808252601c908201527f6361736820616d6f756e74206d75737420626520706f73697469766500000000604082015260600190565b60208082526017908201527f63616c6c6572206973206e6f74207175616c6966696564000000000000000000604082015260600190565b60208082526013908201527f6b6565706572206973206e6f7420616464656400000000000000000000000000604082015260600190565b60208082526013908201527f6e6f206d617267696e20746f20736574746c6500000000000000000000000000604082015260600190565b6020808252600d908201526c696e76616c696420666c61677360981b604082015260600190565b60208082526029908201527f7368617265207472616e736665722064656c61792073686f756c64206265206160408201527f74206c6561736520310000000000000000000000000000000000000000000000606082015260800190565b6020808252601f908201527f6361736820746f2072657475726e206d75737420626520706f73697469766500604082015260600190565b6020808252601d908201527f70657270657475616c20636f756e742065786365656473206c696d6974000000604082015260600190565b6020808252601c908201527f696e697469616c4d617267696e52617465206973206e6f742073657400000000604082015260600190565b60208082526023908201527f63616e6e6f74207472616e7366657220746f2063757272656e74206f7065726160408201527f746f720000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526016908201527f696e73756666696369656e7420706f6f6c206361736800000000000000000000604082015260600190565b6020808252818101527f736861726520746f2072656d6f7665206d75737420626520706f736974697665604082015260600190565b6020808252602b908201527f74686572652073686f756c64206265206174206c65617374203120706572706560408201527f7475616c20746f2072756e000000000000000000000000000000000000000000606082015260800190565b60208082526017908201527f6b656570657220697320616c7265616479206164646564000000000000000000604082015260600190565b60208082526014908201527f696e737572616e636546756e64436170203c2030000000000000000000000000604082015260600190565b60208082526016908201527f636f6c6c61746572616c206973206e6567617469766500000000000000000000604082015260600190565b60208082526017908201527f6e6577206f70657261746f7220697320696e76616c6964000000000000000000604082015260600190565b6020808252601a908201527f696e73756666696369656e742073686172652062616c616e6365000000000000604082015260600190565b60208082526013908201527f676f7665726e6f7220697320696e76616c696400000000000000000000000000604082015260600190565b6020808252601b908201527f696e637265617365206e6567617469766520706f6f6c20636173680000000000604082015260600190565b60208082526024908201527f6661696c20746f2072656d6f7665206b65657065722066726f6d20776869746560408201527f6c69737400000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252601f908201527f6661696c20746f20616464206b656570657220746f2077686974656c69737400604082015260600190565b60208082526011908201527f696e76616c696420706172616d65746572000000000000000000000000000000604082015260600190565b6020808252601b908201527f6465637265617365206e6567617469766520706f6f6c20636173680000000000604082015260600190565b60208082526016908201527f73686172655472616e7366657244656c6179203c203100000000000000000000604082015260600190565b60208082526022908201527f6e6567617469766520706f6f6c4361736820616674657220736574746c65206160408201527f6c6c000000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526015908201527f636f6c6c61746572616c20697320696e76616c69640000000000000000000000604082015260600190565b60208082526013908201527f746172676574206c65766572616765203d203000000000000000000000000000604082015260600190565b6020808252601c908201527f70657270657475616c20696e646578206f7574206f662072616e676500000000604082015260600190565b6020808252600e908201527f696e76616c696420747261646572000000000000000000000000000000000000604082015260600190565b60208082526010908201527f6c6971756964697479436170203c203000000000000000000000000000000000604082015260600190565b60208082526016908201527f6e6f2070657270657475616c20746f20736574746c6500000000000000000000604082015260600190565b9283526001600160a01b03919091166020830152604082015260600190565b9182526001600160a01b0316602082015260400190565b8281526101408101613b0960208301846152e0565b8481526103808101615c8860208301866152e0565b615c966101408301856152e0565b610a176102608301846152e0565b918252602082015260400190565b878152602081018790526001600160a01b03861660408201526104e08101615cdd60608301876152bd565b615ceb6101808301866152bd565b615cf96102a08301856152bd565b611bb06103c08301846152bd565b8881526001600160a01b038881166020830152878116604083015286811660608301528581166080830152841660a08201526103008101615d4b60c08301856152bd565b615d596101e08301846152bd565b9998505050505050505050565b948552602085019390935260408401919091526060830152608082015260a00190565b60405181810167ffffffffffffffff81118282101715615da557fe5b604052919050565b6001600160a01b03811681146134e357600080fd5b80151581146134e357600080fdfe5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f775369676e6564536166654d6174683a206469766973696f6e206f766572666c6f775369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7753616665436173743a2076616c756520646f65736e27742066697420696e20616e20696e743235365369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f77a26469706673582212203c5c2c0277e4d56ab6939bf95e43a812c661771cd5d81862f16273ab60bc4b9f64736f6c63430007040033
Deployed Bytecode
0x73bec03e567e7446f3ab51e5afae53e955de78fec430146080604052600436106103605760003560e01c806393c80555116101d2578063c9ab75711161010e578063e3e28986116100ac578063e8c38c5c11610086578063e8c38c5c146108d1578063ebf1f268146108f1578063f20d689614610911578063fd2c4b4d1461093157610360565b8063e3e289861461087e578063e57d06a81461089e578063e806e25c146108b157610360565b8063ccef4f11116100e8578063ccef4f111461080b578063d767897e1461082b578063d94dacbd1461084b578063e1858ce91461085e57610360565b8063c9ab7571146107c3578063ca66f475146107e3578063cbf231c1146107eb57610360565b8063ac76a8511161017b578063bb89ac2411610155578063bb89ac2414610743578063bdc5e84914610763578063c01fa80814610783578063c3872ccf146107a357610360565b8063ac76a851146106f0578063b1a4182a14610710578063b2f6decc1461072357610360565b8063a54b4d78116101ac578063a54b4d781461069d578063a99d455a146106bd578063a9f677f5146106dd57610360565b806393c805551461064a578063960e38ae1461065d578063990d20521461067d57610360565b806344467389116102a15780635980576b1161024a578063690501e311610224578063690501e3146105e457806370f546ce1461060457806383a1eaa8146106175780638d3416411461063757610360565b80635980576b146105845780635a8610b4146105a457806367d0b253146105c457610360565b806348ea454c1161027b57806348ea454c146105245780634e74d930146105445780634f28feeb1461056457610360565b806344467389146104d157806344a9d80a146104f157806348d21d001461050457610360565b8063316ad4d71161030e57806338dbaf71116102e857806338dbaf711461045e5780633d792b6e1461047e5780633d92b9a01461049e5780633eb02737146104be57610360565b8063316ad4d71461043057806332958b4b14610438578063374331ff1461044b57610360565b8063299cbfda1161033f578063299cbfda146103d05780632d7966d8146103f05780632ef9dab91461041057610360565b8062addc0914610365578063028c4ff61461038e5780632621d4c8146103b0575b600080fd5b6103786103733660046150e0565b610944565b60405161038591906153a1565b60405180910390f35b81801561039a57600080fd5b506103ae6103a9366004614f91565b610a20565b005b8180156103bc57600080fd5b506103ae6103cb366004614f14565b610bba565b8180156103dc57600080fd5b506103ae6103eb366004614cac565b610cb4565b8180156103fc57600080fd5b506103ae61040b366004614eac565b610dcc565b81801561041c57600080fd5b506103ae61042b366004614f59565b6112a6565b610378611420565b6103786104463660046151ca565b611427565b610378610459366004614c94565b6114c8565b81801561046a57600080fd5b506103ae610479366004614f59565b6114e1565b81801561048a57600080fd5b506103ae610499366004615059565b61159d565b8180156104aa57600080fd5b506103ae6104b9366004614f38565b61165f565b6103786104cc366004615270565b611720565b8180156104dd57600080fd5b506103ae6104ec3660046150b5565b611790565b6103786104ff366004614c94565b6117c3565b81801561051057600080fd5b506103ae61051f3660046150b5565b6117d8565b81801561053057600080fd5b506103ae61053f366004614f59565b611806565b81801561055057600080fd5b506103ae61055f366004614c94565b6118d5565b81801561057057600080fd5b506103ae61057f366004614cac565b6119be565b81801561059057600080fd5b506103ae61059f366004614c94565b611a85565b8180156105b057600080fd5b506103ae6105bf366004614f91565b611ad8565b6105d76105d2366004614f91565b611b5d565b6040516103859190615396565b8180156105f057600080fd5b506103ae6105ff366004614e75565b611bbc565b610378610612366004614f38565b611c31565b81801561062357600080fd5b506103ae610632366004614e75565b611cd5565b610378610645366004615127565b611e60565b610378610658366004615210565b611f44565b81801561066957600080fd5b506103ae610678366004614f38565b6120f3565b81801561068957600080fd5b506103ae610698366004614c94565b6121fa565b8180156106a957600080fd5b506103ae6106b8366004614f91565b6123af565b8180156106c957600080fd5b506103ae6106d8366004614c94565b612545565b6103786106eb366004615195565b6127b6565b8180156106fc57600080fd5b506103ae61070b366004614e75565b6127f6565b61037861071e366004614c94565b612875565b81801561072f57600080fd5b506103ae61073e366004614f59565b6128ff565b81801561074f57600080fd5b506103ae61075e366004614dff565b6129eb565b81801561076f57600080fd5b506103ae61077e366004614c94565b612cb4565b81801561078f57600080fd5b506103ae61079e366004614f59565b612d59565b8180156107af57600080fd5b506103ae6107be366004614fbf565b612e1a565b8180156107cf57600080fd5b506103ae6107de366004614d17565b612ecd565b610378613120565b8180156107f757600080fd5b50610378610806366004614f38565b613125565b81801561081757600080fd5b50610378610826366004614f38565b6132c3565b81801561083757600080fd5b506103ae610846366004614e75565b613398565b6103ae610859366004614b6e565b613474565b61087161086c366004614c94565b6134e6565b6040516103859190615303565b81801561088a57600080fd5b506103ae610899366004614ee8565b613570565b6105d76108ac366004614cd0565b6135e5565b8180156108bd57600080fd5b506103ae6108cc366004614f38565b6136a1565b8180156108dd57600080fd5b506103ae6108ec366004614f91565b613870565b8180156108fd57600080fd5b506103ae61090c366004615023565b6138ff565b81801561091d57600080fd5b506103ae61092c366004615023565b613985565b61037861093f366004615195565b613a0b565b60008061095087613a62565b9050600061095e8888613a90565b90508061098b57610982600061097c6109778b8b613ab1565b613aeb565b90613af8565b92505050610a17565b61099f866109998a8a613ab1565b90613b10565b92506109b96109b2826109998888613b45565b8490613b45565b925080156109d75760128801546109d4906109b29088613b10565b92505b6109eb6109e48288613b45565b8490613baa565b9250610a056109fe6109778a8a86613bf0565b8490613c2e565b9250610a12836000613af8565b925050505b95945050505050565b83600f01548310610a4c5760405162461bcd60e51b8152600401610a4390615b4c565b60405180910390fd5b610a5884848484611ad8565b60008381526010850160205260409081902090517fd13cfaa900000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac9163d13cfaa991610abc919086908690600401615c28565b60206040518083038186803b158015610ad457600080fd5b505af4158015610ae8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0c9190614b89565b15610bb45783546040517f439adb78000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b03169063439adb7890610b609085908790600401615355565b602060405180830381600087803b158015610b7a57600080fd5b505af1158015610b8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bb29190614b89565b505b50505050565b600f82015460005b81811015610bb457600081815260108501602052604090206002600182015460ff166004811115610bef57fe5b14610bfa5750610cac565b610c0381613c3e565b8060010160019054906101000a90046001600160a01b03166001600160a01b031663d1cc99766040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610c5557600080fd5b505af1158015610c69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c8d9190614b89565b8015610c97575083155b15610caa57610caa85826000015461165f565b505b600101610bc2565b610cbd82613ce2565b6001600160a01b0316816001600160a01b031614610ced5760405162461bcd60e51b8152600401610a43906154d3565b6001820180546001600160a01b0319166001600160a01b038316179055610d1742620d2f00613d08565b600c8301556002820180546001600160a01b03191690558154604051626e20ad60e11b8152620100009091046001600160a01b03169062dc415a90610d629030908590600401615317565b600060405180830381600087803b158015610d7c57600080fd5b505af1158015610d90573d6000803e3d6000fd5b50506040516001600160a01b03841692507f52663c8b1b35a6c6b1a720c76465d84626b103e3990f2f59618125f73c588cf09150600090a25050565b600480850154604080517f18160ddd00000000000000000000000000000000000000000000000000000000815290516001600160a01b0390921692600092610e6f9285926318160ddd928282019260209290829003018186803b158015610e3257600080fd5b505afa158015610e46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6a9190614be3565b613d62565b90506000808085158015610e835750600087135b15610f5b576040517feeffc15d0000000000000000000000000000000000000000000000000000000081527370a20957f426740c92231430c4855b717770083d9063eeffc15d90610edc908c9088908c906004016153aa565b60806040518083038186803b158015610ef457600080fd5b505af4158015610f08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2c9190614c4d565b9298509094509250905060008613610f565760405162461bcd60e51b8152600401610a43906155fc565b611054565b600086138015610f69575086155b1561103c576040517f68dddaa60000000000000000000000000000000000000000000000000000000081527370a20957f426740c92231430c4855b717770083d906368dddaa690610fc2908c9088908b906004016153aa565b60806040518083038186803b158015610fda57600080fd5b505af4158015610fee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110129190614c4d565b9299509094509250905060008713610f565760405162461bcd60e51b8152600401610a4390615735565b60405162461bcd60e51b8152600401610a43906159dc565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038616906370a0823190611099908b90600401615303565b60206040518083038186803b1580156110b157600080fd5b505afa1580156110c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110e99190614be3565b6110f288613daa565b11156111105760405162461bcd60e51b8152600401610a43906158a3565b6000611126836111208987613b45565b90613b45565b90506111318a6117c3565b8113156111505760405162461bcd60e51b8152600401610a43906156fe565b856001600160a01b0316639dc29fac8a6111698b613daa565b6040518363ffffffff1660e01b8152600401611186929190615355565b600060405180830381600087803b1580156111a057600080fd5b505af11580156111b4573d6000803e3d6000fd5b50506040516346a8e88f60e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d1492506346a8e88f91506111f3908d908d908c90600401615c28565b60006040518083038186803b15801561120b57600080fd5b505af415801561121f573d6000803e3d6000fd5b50505060128b0154611232915085613b45565b60128b015560138a01546112469084613b45565b60138b01556112558a82613e01565b886001600160a01b03167f3713fb0b3de95a2c80ba2e2cf5229918ac276b7d04f1526616e03c37b309ba58888a85604051611292939291906153aa565b60405180910390a250505050505050505050565b60008281526010840160205260408120601281015490911280156112d257508060120154816002015412155b156112e7576112e78484848460120154613870565b6112f381603001613e3d565b158061141157506040517fd87384fe00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90635e91c9f8908390839063d87384fe906113549084906004016153a1565b60206040518083038186803b15801561136c57600080fd5b505af4158015611380573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a49190614b52565b6040518363ffffffff1660e01b81526004016113c1929190615c47565b60206040518083038186803b1580156113d957600080fd5b505af41580156113ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114119190614b89565b15610bb457610bb484846136a1565b620d2f0081565b60008061143387613a62565b6020870151600b8901549192509061145890611450908390613b10565b885190613b45565b92508061146d57610982600061097c85613aeb565b6114778387613b10565b925061148a6109b2826109998888613b45565b925080156114a85760128801546114a5906109b29088613b10565b92505b6114b56109e48288613b45565b9250610a056109fe6109778a8a86613a0b565b60168101546000906114db906001613e48565b92915050565b82600f015482106115045760405162461bcd60e51b8152600401610a4390615b4c565b60008281526010840160205260409081902090517f5b30d53600000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90635b30d536906115679084908690600401615c47565b60006040518083038186803b15801561157f57600080fd5b505af4158015611593573d6000803e3d6000fd5b5050505050505050565b84600f015484106115c05760405162461bcd60e51b8152600401610a4390615b4c565b60008481526010860160205260409081902090517f967b9f2b00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac9063967b9f2b90611627908490889088908890600401615c73565b60006040518083038186803b15801561163f57600080fd5b505af4158015611653573d6000803e3d6000fd5b50505050505050505050565b81600f015481106116825760405162461bcd60e51b8152600401610a4390615b4c565b61168c8282613125565b5060008181526010830160205260409081902090516313743a2360e31b815273310d3cfaca28a32d17d183e8dec599c18543e8ac91639ba1d118916116d491906004016153a1565b60006040518083038186803b1580156116ec57600080fd5b505af4158015611700573d6000803e3d6000fd5b5050505061170f826002613e58565b61171c5761171c82613ebc565b5050565b600080600061174561173f888a60200151613b4590919063ffffffff16565b88613f97565b9150915081600014158015611758575080155b156117715761176a8989848989611427565b9250611784565b6117818989898986868b8b611f44565b92505b50509695505050505050565b8061179a576117be565b600082815260108401602052604090206117b49082613ff5565b6117be8382613e01565b505050565b60006114db8283600f0154611c31565b919050565b806117e2576117be565b600082815260108401602052604090206117fc9082614031565b6117be8382614088565b82600f015482106118295760405162461bcd60e51b8152600401610a4390615b4c565b6000828152601084016020526040902060390161184681836140b8565b6118625760405162461bcd60e51b8152600401610a439061550a565b600061186e82846140cd565b90508061188d5760405162461bcd60e51b8152600401610a4390615948565b826001600160a01b03167fec00c45379d4f167916464b153f75be2434e0ca4c60946c0f99723f3090cf3b2856040516118c691906153a1565b60405180910390a25050505050565b600f810154806118f75760405162461bcd60e51b8152600401610a439061576a565b60005b8181101561198557600081815260108401602052604090819020905163c94606dd60e01b815273310d3cfaca28a32d17d183e8dec599c18543e8ac9163c94606dd9161194991906004016153a1565b60006040518083038186803b15801561196157600080fd5b505af4158015611975573d6000803e3d6000fd5b5050600190920191506118fa9050565b50815460ff191660011782556040517f7b14db60328776e0e3ff7638ea4234333f5ba91c16cad0a6112c56c288f1bfde90600090a15050565b6001600160a01b0381166119e45760405162461bcd60e51b8152600401610a439061586c565b6119ed826140e2565b6001600160a01b0316816001600160a01b03161415611a1e5760405162461bcd60e51b8152600401610a43906156a1565b6002820180546001600160a01b0319166001600160a01b038316179055611a4842620d2f00613d08565b600c8301556040516001600160a01b038216907f865f83d7f78904d112c0cb736765ebd7d94da5905d36dca4df70d2d188fd517c90600090a25050565b611a9242620d2f00613d08565b600c820155611aa0816140e2565b6001600160a01b03167f838ffdc993783254a13d0672ce593ffa0dffb3219bab649f2f9f572f4d0fb37860405160405180910390a250565b604051631885d4f160e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d1490631885d4f190611b1390879086908690600401615c28565b60006040518083038186803b158015611b2b57600080fd5b505af4158015611b3f573d6000803e3d6000fd5b50505060008481526010860160205260409020610bb4915082613ff5565b6000838152601085016020526040812081611b81611b7b8387613a90565b85614108565b90506000611b8e83613a62565b905081611ba557611ba0838783614121565b611bb0565b611bb0838783614159565b98975050505050505050565b604051631885d4f160e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d1490631885d4f190611bf790869086908690600401615c28565b60006040518083038186803b158015611c0f57600080fd5b505af4158015611c23573d6000803e3d6000fd5b505050506117be8382614088565b600f820154600090815b81811015611cbc576000818152601086016020526040902084821480611c7457506002600182015460ff166004811115611c7157fe5b14155b15611c7f5750611cb4565b6000611c8a82613a62565b9050611caf611ca8611c9d843085614171565b6111208530866141a9565b86906141e0565b945050505b600101611c3b565b506009840154611ccd9083906141e0565b949350505050565b60008113611cf55760405162461bcd60e51b8152600401610a439061549c565b6040517ff72ee9c80000000000000000000000000000000000000000000000000000000081526000907370a20957f426740c92231430c4855b717770083d9063f72ee9c890611d4c908790859087906004016153aa565b604080518083038186803b158015611d6357600080fd5b505af4158015611d77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d9b9190614c2a565b604051631885d4f160e01b815290925073a4f2ff555862066f3161a5055dbb485b05fe0d149150631885d4f190611dda90879087908790600401615c28565b60006040518083038186803b158015611df257600080fd5b505af4158015611e06573d6000803e3d6000fd5b50505050611e148483614088565b826001600160a01b03167fc3919ccd011a100c0148bab574a26f47f5db31c2bc16903381f5429a1a4f1f9583600084604051611e52939291906153aa565b60405180910390a250505050565b600080611e6c8a613a62565b90506000611e7b8b8b846141a9565b90506000611e8a8c8c87614245565b905060008113611eac5760405162461bcd60e51b8152600401610a4390615b15565b611ec18382611eba8a61439c565b91906143b4565b9350611ed39050896111208d8d613a90565b15801590611edf575086155b15611f0657611eff88611120611ef5858d613b10565b611120878a6141e0565b9250611f24565b611f21816111208d60120154866141e090919063ffffffff16565b92505b611f356109fe6109778d8d86613bf0565b9b9a5050505050505050505050565b600080611f508a613a62565b90506000611f5f8b8b846127b6565b9050600080611f738663ffffffff166143f1565b90506000611f868763ffffffff166143fc565b9050818015611f925750805b15611faf5760405162461bcd60e51b8152600401610a4390615578565b8115611fc1578c604001519250611fd3565b611fd08763ffffffff1661440f565b92505b600c8e0154611ff45760405162461bcd60e51b8152600401610a439061566a565b60006120158f600c0154670de0b6b3a7640000613baa90919063ffffffff16565b905083156120235783612029565b60358f01545b93506120358482613af8565b93505050506000811361205a5760405162461bcd60e51b8152600401610a4390615b15565b6120688382611eba8a61439c565b60208c015190945061207a908b613b45565b15801590612086575087155b156120ad576120a68961112061209c868e613b10565b611120888b6141e0565b93506120cb565b6120c8826111208e60120154876141e090919063ffffffff16565b93505b6120e36120dc6109778e8e87613a0b565b8590613c2e565b9c9b505050505050505050505050565b8082600a0154106121035761171c565b600061211f610e6a84600a01548461442390919063ffffffff16565b600f84015490915060005b818110156121ee57600081815260108601602052604090206002600182015460ff16600481111561215757fe5b1461216257506121e6565b6040517f8f27c08f00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90638f27c08f906121b49084908890600401615ca4565b60006040518083038186803b1580156121cc57600080fd5b505af41580156121e0573d6000803e3d6000fd5b50505050505b60010161212a565b505050600a9190910155565b6040517fc12b64d500000000000000000000000000000000000000000000000000000000815260009081907370a20957f426740c92231430c4855b717770083d9063c12b64d59061224f9086906004016153a1565b604080518083038186803b15801561226657600080fd5b505af415801561227a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061229e9190614bfb565b915091507fc7ca5bca6c98d1e97e37f3d5325b86b0ee183f9c17703c11bbc7e1cf4bce480c826040516122d191906153a1565b60405180910390a1806122e357600091505b600f83015460005b81811015610bb257600081815260108601602052604090206002600182015460ff16600481111561231857fe5b1461232357506123a7565b6040517f3d1e253000000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90633d1e2530906123759084908990600401615ca4565b60006040518083038186803b15801561238d57600080fd5b505af41580156123a1573d6000803e3d6000fd5b50505050505b6001016122eb565b83600f015483106123d25760405162461bcd60e51b8152600401610a4390615b4c565b600083815260108501602052604090206123ec8585613125565b506040517fc480202a00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac9063c480202a9061244190849087908790600401615c28565b60206040518083038186803b15801561245957600080fd5b505af415801561246d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124919190614b89565b156125395784546040517ff81d65f1000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b03169063f81d65f1906124e59086908890600401615355565b602060405180830381600087803b1580156124ff57600080fd5b505af1158015612513573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125379190614b89565b505b610bb285858585613870565b600081600f0154116125695760405162461bcd60e51b8152600401610a4390615bf1565b60008060008084600f0154905060005b8181101561260757600081815260108701602052604090206002600182015460ff1660048111156125a657fe5b146125b157506125ff565b60006125bc82613a62565b90506125d36125cc833084614480565b87906141e0565b95506125e3611ca8833084614171565b94506125fa6125f38330846141a9565b88906141e0565b965050505b600101612579565b5060098501546126189085906141e0565b9350600082612628576000612634565b612634858460016144b8565b905060005b8281101561277f57600081815260108801602052604090206002600182015460ff16600481111561266657fe5b146126715750612777565b600061267c82613a62565b90506000612698856001612691863087614171565b91906144d6565b90506126a58330846141a9565b985060006126b3828b613b45565b905060008113156126d9576126c98430836144f4565b6126d48b8683611790565b6126fb565b60008112156126fb576126ed8430836144f4565b6126fb8b8661051f84613aeb565b600085815260108c0160205260409081902090516313743a2360e31b815273310d3cfaca28a32d17d183e8dec599c18543e8ac91639ba1d1189161274291906004016153a1565b60006040518083038186803b15801561275a57600080fd5b505af415801561276e573d6000803e3d6000fd5b50505050505050505b600101612639565b506000866009015412156127a55760405162461bcd60e51b8152600401610a4390615a81565b6127ae86613ebc565b505050505050565b6000611ccd6127e16127d986600b01548660200151613b1090919063ffffffff16565b855190613b45565b60208501516127f09085613b10565b906141e0565b80612800576117be565b6040516346a8e88f60e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d14906346a8e88f9061283b90869086908690600401615c28565b60006040518083038186803b15801561285357600080fd5b505af4158015612867573d6000803e3d6000fd5b505050506117be8382613e01565b60008160000160029054906101000a90046001600160a01b03166001600160a01b031663492c26b66040518163ffffffff1660e01b815260040160206040518083038186803b1580156128c757600080fd5b505afa1580156128db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114db9190614be3565b6001600160a01b0381166129255760405162461bcd60e51b8152600401610a4390615b83565b600082815260108401602052604080822090516324d41bd760e11b815273310d3cfaca28a32d17d183e8dec599c18543e8ac916349a837ae9161296d91908690600401615c47565b60206040518083038186803b15801561298557600080fd5b505af4158015612999573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129bd9190614be3565b9050600081136129df5760405162461bcd60e51b8152600401610a4390615541565b610bb484848484613870565b603086600f015410612a0f5760405162461bcd60e51b8152600401610a4390615633565b600f86015460008181526010880160205260409081902090517f4a5f050e00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90634a5f050e90612a8190849086908c908c908c908c908c90600401615cb2565b60006040518083038186803b158015612a9957600080fd5b505af4158015612aad573d6000803e3d6000fd5b5050505060008860000160029054906101000a90046001600160a01b03166001600160a01b03166307c4d4866040518163ffffffff1660e01b815260040160206040518083038186803b158015612b0357600080fd5b505afa158015612b17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b3b9190614b52565b6040517fb71906580000000000000000000000000000000000000000000000000000000081529091506001600160a01b0382169063b719065890612b859030908790600401615355565b600060405180830381600087803b158015612b9f57600080fd5b505af1158015612bb3573d6000803e3d6000fd5b50508a5460ff16159150612c2b90505760405163c94606dd60e01b815273310d3cfaca28a32d17d183e8dec599c18543e8ac9063c94606dd90612bfa9085906004016153a1565b60006040518083038186803b158015612c1257600080fd5b505af4158015612c26573d6000803e3d6000fd5b505050505b600f890180546001019055600389015460048a01547f8c198f1eb08b1a98128c3f5d8d36e943e11147088c5905a7edeb23732635b8df9185916001600160a01b039182169116612c7a8d6140e2565b60088e0154604051612ca195949392918f916001600160a01b03909116908f908f90615d07565b60405180910390a1505050505050505050565b6001810180546001600160a01b03191690558054604051626e20ad60e11b8152620100009091046001600160a01b03169062dc415a90612cfb903090600090600401615317565b600060405180830381600087803b158015612d1557600080fd5b505af1158015612d29573d6000803e3d6000fd5b50506040517fda0987738091b194f50e1fef7257fb806ac0b26fd3607a7fff79a9efa9e4804c925060009150a150565b82600f01548210612d7c5760405162461bcd60e51b8152600401610a4390615b4c565b60008281526010840160205260409020603901612d9981836140b8565b15612db65760405162461bcd60e51b8152600401610a43906157c7565b6000612dc2828461452b565b905080612de15760405162461bcd60e51b8152600401610a43906159a5565b826001600160a01b03167ffb9de28e25ee60a69b50d1ad2d6aeb820ad2cb5dceeedefe9282d37fb9c77448856040516118c691906153a1565b6000868152601088016020526040812090612e358288613a90565b905060008080612e4e612e48858b613b45565b8a613f97565b9150915081600014158015612e61575080155b15612e7a57612e73858b848b8b610944565b9250612e8d565b612e8a858b8b8b86868d8d611e60565b92505b6000831315612ea757612ea28c8c8c86610a20565b612ebf565b6000831215612ebf57612ebf8c8c8c6106b887613aeb565b505050505050505050505050565b6001600160a01b038516612ef35760405162461bcd60e51b8152600401610a4390615ade565b6001600160a01b038216612f195760405162461bcd60e51b8152600401610a43906158da565b60008060008084806020019051810190612f339190614ba5565b929650909450925090506001811015612f5e5760405162461bcd60e51b8152600401610a439061559f565b6040517f5fc2e39200000000000000000000000000000000000000000000000000000000815273a4f2ff555862066f3161a5055dbb485b05fe0d1490635fc2e39290612fb2908e908d908d90600401615c28565b60006040518083038186803b158015612fca57600080fd5b505af4158015612fde573d6000803e3d6000fd5b50505050898b60000160026101000a8154816001600160a01b0302191690836001600160a01b03160217905550896001600160a01b03166316d6b5f66040518163ffffffff1660e01b815260040160206040518083038186803b15801561304457600080fd5b505afa158015613058573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307c9190614b52565b60058c0180546001600160a01b03199081166001600160a01b039384161790915560018d0180549091169189169190911790556130bc42620d2f00613d08565b600c8c015560038b0180546001600160a01b039097166001600160a01b0319978816811790915560048c0180549097161790955589549215156101000261ff0019909316929092178955601189015560158801555060169095019490945550505050565b603081565b600082600f0154821061314a5760405162461bcd60e51b8152600401610a4390615b4c565b600082815260108401602052604090206002600182015460ff16600481111561316f57fe5b1461317e5760009150506114db565b6040517f84da931000000000000000000000000000000000000000000000000000000000815260009073310d3cfaca28a32d17d183e8dec599c18543e8ac906384da9310906131d19085906004016153a1565b60206040518083038186803b1580156131e957600080fd5b505af41580156131fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132219190614be3565b905080613233576000925050506114db565b600081131561327357600282015461324c908290613af8565b90506132633061325b83613aeb565b8491906144f4565b61326e8585836117d8565b611ccd565b600061327f8686611c31565b90506000811361329557600093505050506114db565b6132a28161097c8461439c565b91506132af8330846144f4565b6132ba868684611790565b50949350505050565b600081156114db5760128301546000906132dd90846141e0565b9050600083131561334a578360110154811315613345576011840154613304908290613b45565b9150836011015490507fc0357ed6fd11fd16f5c820f18e7835dcbd960c75f9bb28350a00573ac6c2b2e38260405161333c91906153a1565b60405180910390a15b61338d565b600081121561338d57601384015461336290826141e0565b60138501819055600013156133895760405162461bcd60e51b8152600401610a439061542e565b5060005b601284015592915050565b600081136133b85760405162461bcd60e51b8152600401610a4390615465565b604051631885d4f160e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d1490631885d4f1906133f390869086908690600401615c28565b60006040518083038186803b15801561340b57600080fd5b505af415801561341f573d6000803e3d6000fd5b50505060138401546134329150826141e0565b60138401556040517f35501c76a025a907c147d9a58f7a43504b042e3aeb4baff4fab606463bd8524b906134679083906153a1565b60405180910390a1505050565b6020810151600013156134995760405162461bcd60e51b8152600401610a43906157fe565b6040810151600013156134be5760405162461bcd60e51b8152600401610a4390615bba565b6060810151600113156134e35760405162461bcd60e51b8152600401610a4390615a4a565b50565b60008160000160029054906101000a90046001600160a01b03166001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561353857600080fd5b505afa15801561354c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114db9190614b52565b61357981613474565b8051825461ff0019169015156101000217825560208101516011830155604080820151601584015560608201516016840155517fac5bf8754fef31e548d743999f2235f9930fe919cf7ea07b525d98c8485439eb906135d990839061536e565b60405180910390a15050565b6000826001600160a01b0316846001600160a01b03161480610a17575060058501546040517fc2f3da1d0000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063c2f3da1d9061365190879087908790600401615331565b60206040518083038186803b15801561366957600080fd5b505afa15801561367d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a179190614b89565b81600f015481106136c45760405162461bcd60e51b8152600401610a4390615b4c565b60008181526010830160205260409081902090517f8e66efaf00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac90638e66efaf906137279084903090600401615c47565b60006040518083038186803b15801561373f57600080fd5b505af4158015613753573d6000803e3d6000fd5b50506040517f39d281eb00000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac92506339d281eb91506137a79084906004016153a1565b60006040518083038186803b1580156137bf57600080fd5b505af41580156137d3573d6000803e3d6000fd5b50506040516324d41bd760e11b81526000925073310d3cfaca28a32d17d183e8dec599c18543e8ac91506349a837ae906138139085903090600401615c47565b60206040518083038186803b15801561382b57600080fd5b505af415801561383f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138639190614be3565b9050610bb48484836117d8565b8061387a57610bb4565b6040516346a8e88f60e01b815273a4f2ff555862066f3161a5055dbb485b05fe0d14906346a8e88f906138b590879086908690600401615c28565b60006040518083038186803b1580156138cd57600080fd5b505af41580156138e1573d6000803e3d6000fd5b50505060008481526010860160205260409020610bb4915082614031565b82600f015482106139225760405162461bcd60e51b8152600401610a4390615b4c565b60008281526010840160205260409081902090517fb7ce1b2700000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac9063b7ce1b27906115679084908690600401615c5e565b82600f015482106139a85760405162461bcd60e51b8152600401610a4390615b4c565b60008281526010840160205260409081902090517ffc13872000000000000000000000000000000000000000000000000000000000815273310d3cfaca28a32d17d183e8dec599c18543e8ac9063fc138720906115679084908690600401615c5e565b6000808360200151600014613a4e57613a4985601201546127f0613a4488600c0154610999888a60200151613b1090919063ffffffff16565b61439c565b613a51565b60005b9050610a17816111208787876127b6565b60006002600183015460ff166004811115613a7957fe5b14613a885760088201546114db565b506006015490565b6001600160a01b031660009081526034909101602052604090206001015490565b6001600160a01b03811660009081526034830160205260408120600b8401546001820154611ccd91613ae39190613b10565b825490613b45565b60006114db600083613b45565b6000818312613b075781613b09565b825b9392505050565b6000670de0b6b3a7640000613b36613b288585614540565b670de0b6b3a76400006145e9565b81613b3d57fe5b059392505050565b6000818303818312801590613b5a5750838113155b80613b6f5750600083128015613b6f57508381135b613b095760405162461bcd60e51b8152600401808060200182810382526024815260200180615e626024913960400191505060405180910390fd5b600080821215613bcb57613bbd82613aeb565b9150613bc883613aeb565b92505b613b0982613bea613be486670de0b6b3a7640000614540565b856145e9565b90614668565b600080613bfd8585613a90565b15613c1a57613c1585601201546127f0878787614171565b613c1d565b60005b9050610a17816111208787876141a9565b600081831215613b075781613b09565b600181015461010090046001600160a01b0316613c636006830182630e222f9b614720565b613c7e82600401826001600160a01b031663ccbdbee2614720565b815460068301546007840154600485015460058601546040516001600160a01b038716957fec91322ffa6f23802efae915b1fc28a37d2d0986d25d2073c0a661498ad0076795613cd695919490939192909190615d66565b60405180910390a25050565b600081600c0154421115613cf75760006114db565b50600201546001600160a01b031690565b600082820183811015613b09576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000600160ff1b8210613da65760405162461bcd60e51b8152600401808060200182810382526028815260200180615e3a6028913960400191505060405180910390fd5b5090565b600080821215613da6576040805162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f736974697665604482015290519081900360640190fd5b6000811215613e225760405162461bcd60e51b8152600401610a4390615a13565b6009820154613e319082613b45565b82600901819055505050565b60006114db826147d2565b600081831015613b075781613b09565b600f820154600090815b81811015613eb157836004811115613e7657fe5b600082815260108701602052604090206001015460ff166004811115613e9857fe5b1415613ea9576001925050506114db565b600101613e62565b506000949350505050565b6000613ec7826140e2565b9050600082601301541380613ee0575060008260120154135b8015613ef457506001600160a01b03811615155b1561171c576000613f16836012015484601301546141e090919063ffffffff16565b60006013850181905560128501556040516346a8e88f60e01b815290915073a4f2ff555862066f3161a5055dbb485b05fe0d14906346a8e88f90613f6290869086908690600401615c28565b60006040518083038186803b158015613f7a57600080fd5b505af4158015613f8e573d6000803e3d6000fd5b50505050505050565b600080613fa484846147d6565b15613fb457506000905081613fee565b613fbd8361439c565b613fc68561439c565b12613fd657508190506000613fee565b613fdf84613aeb565b613fe985856141e0565b915091505b9250929050565b60008112156140165760405162461bcd60e51b8152600401610a43906153f7565b600282015461402590826141e0565b82600201819055505050565b60008112156140525760405162461bcd60e51b8152600401610a43906153f7565b60028201546140619082613b45565b600283018190556000131561171c5760405162461bcd60e51b8152600401610a4390615835565b60008112156140a95760405162461bcd60e51b8152600401610a4390615911565b6009820154613e3190826141e0565b6000613b09836001600160a01b0384166147f9565b6000613b09836001600160a01b038416614811565b600081600c01544211156140f75760006114db565b50600101546001600160a01b031690565b600082614117575060006114db565b613b0983836147d6565b60008061412e8585613a90565b1561413d578460120154614140565b60005b90508061414e8686866141a9565b121595945050505050565b600080614167858585613bf0565b1215949350505050565b600c8301546001600160a01b03831660009081526034850160205260408120600101549091611ccd91613a4491906109999086613b10565b6000611ccd6141b88585613ab1565b6001600160a01b03851660009081526034870160205260409020600101546127f09085613b10565b60008282018183128015906141f55750838112155b8061420a575060008312801561420a57508381125b613b095760405162461bcd60e51b8152600401808060200182810382526021815260200180615dd16021913960400191505060405180910390fd5b600083600c0154600014156142a1576040805162461bcd60e51b815260206004820152601c60248201527f696e697469616c4d617267696e52617465206973206e6f742073657400000000604482015290519081900360640190fd5b60006142c285600c0154670de0b6b3a7640000613baa90919063ffffffff16565b905060006142d58463ffffffff166143f1565b905060006142e88563ffffffff166143fc565b90508180156142f45750805b15614336576040805162461bcd60e51b815260206004820152600d60248201526c696e76616c696420666c61677360981b604482015290519081900360640190fd5b8115614361576001600160a01b03861660009081526034880160205260409020600201549350614373565b6143708563ffffffff1661440f565b93505b831561437f5783614385565b60358701545b93506143918484613af8565b979650505050505050565b600080821215613da6576143af82613aeb565b6114db565b6000806143c18585614540565b905060008312156143e3576143d583613aeb565b92506143e081613aeb565b90505b610a1783613bea83866145e9565b630800000016151590565b6000806144088361440f565b1392915050565b60071c620fffff16662386f26fc100000290565b60008282111561447a576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b600d8301546001600160a01b03831660009081526034850160205260408120600101549091611ccd91613a4491906109999086613b10565b6000611ccd6144cf85670de0b6b3a7640000614540565b84846148d7565b6000611ccd6144e58585614540565b670de0b6b3a7640000846148d7565b806144fe576117be565b6001600160a01b03821660009081526034840160205260409020805461452490836141e0565b9055505050565b6000613b09836001600160a01b0384166149b4565b60008261454f575060006114db565b826000191480156145635750600160ff1b82145b1561459f5760405162461bcd60e51b8152600401808060200182810382526027815260200180615e136027913960400191505060405180910390fd5b828202828482816145ac57fe5b0514613b095760405162461bcd60e51b8152600401808060200182810382526027815260200180615e136027913960400191505060405180910390fd5b600080821361463f576040805162461bcd60e51b815260206004820152601f60248201527f726f756e6448616c665570206f6e6c7920737570706f7274732079203e203000604482015290519081900360640190fd5b6000831261465b5761465483600284056141e0565b90506114db565b613b098360028405613b45565b6000816146bc576040805162461bcd60e51b815260206004820181905260248201527f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f604482015290519081900360640190fd5b816000191480156146d05750600160ff1b83145b1561470c5760405162461bcd60e51b8152600401808060200182810382526021815260200180615df26021913960400191505060405180910390fd5b600082848161471757fe5b05949350505050565b60008083836040518163ffffffff1660e01b81526004016040805180830381600087803b15801561475057600080fd5b505af1158015614764573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147889190614c82565b9150915060008213801561479b57508015155b6147b75760405162461bcd60e51b8152600401610a43906153c0565b84600101548110610bb2579084556001909301929092555050565b5490565b60008215806147e3575081155b156147f0575060016114db565b501860ff1d1590565b60009081526001919091016020526040902054151590565b600081815260018301602052604081205480156148cd578354600019808301919081019060009087908390811061484457fe5b906000526020600020015490508087600001848154811061486157fe5b60009182526020808320909101929092558281526001898101909252604090209084019055865487908061489157fe5b600190038181906000526020600020016000905590558660010160008781526020019081526020016000206000905560019450505050506114db565b60009150506114db565b60008261492b576040805162461bcd60e51b815260206004820152601060248201527f6469766973696f6e206279207a65726f00000000000000000000000000000000604482015290519081900360640190fd5b6149358484614668565b905082848161494057fe5b0761494a57613b09565b600061495685856147d6565b9050600083600181111561496657fe5b1480156149705750805b15614983576149808260016141e0565b91505b600183600181111561499157fe5b14801561499c575080155b156149ac57610a17826001613b45565b509392505050565b60006149c083836147f9565b6149f6575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556114db565b5060006114db565b600082601f830112614a0e578081fd5b6040516080810181811067ffffffffffffffff82111715614a2b57fe5b6040529050808260808101851015614a4257600080fd5b60005b6004811015614a64578135835260209283019290910190600101614a45565b50505092915050565b8061012081018310156114db57600080fd5b600082601f830112614a8f578081fd5b61012060405181810181811067ffffffffffffffff82111715614aae57fe5b60405291508183828101861015614ac457600080fd5b600092505b6009831015614a6457803582526001929092019160209182019101614ac9565b600060608284031215614afa578081fd5b6040516060810181811067ffffffffffffffff82111715614b1757fe5b80604052508091508235815260208301356020820152604083013560408201525092915050565b803563ffffffff811681146117d357600080fd5b600060208284031215614b63578081fd5b8151613b0981615dad565b600060808284031215614b7f578081fd5b613b0983836149fe565b600060208284031215614b9a578081fd5b8151613b0981615dc2565b60008060008060808587031215614bba578283fd5b8451614bc581615dc2565b60208601516040870151606090970151919890975090945092505050565b600060208284031215614bf4578081fd5b5051919050565b60008060408385031215614c0d578182fd5b825191506020830151614c1f81615dc2565b809150509250929050565b60008060408385031215614c3c578182fd5b505080516020909101519092909150565b60008060008060808587031215614c62578384fd5b505082516020840151604085015160609095015191969095509092509050565b60008060408385031215614c3c578081fd5b600060208284031215614ca5578081fd5b5035919050565b60008060408385031215614cbe578182fd5b823591506020830135614c1f81615dad565b60008060008060808587031215614ce5578182fd5b843593506020850135614cf781615dad565b92506040850135614d0781615dad565b9396929550929360600135925050565b600080600080600080600060e0888a031215614d31578485fd5b87359650602080890135614d4481615dad565b96506040890135614d5481615dad565b9550606089013594506080890135614d6b81615dad565b935060a0890135614d7b81615dad565b925060c089013567ffffffffffffffff80821115614d97578384fd5b818b0191508b601f830112614daa578384fd5b813581811115614db657fe5b614dc8601f8201601f19168501615d89565b91508082528c84828501011115614ddd578485fd5b8084840185840137848482840101525080935050505092959891949750929550565b6000806000806000806104c08789031215614e18578384fd5b863595506020870135614e2a81615dad565b9450614e398860408901614a6d565b9350614e49886101608901614a6d565b9250614e59886102808901614a6d565b9150614e69886103a08901614a6d565b90509295509295509295565b600080600060608486031215614e89578081fd5b833592506020840135614e9b81615dad565b929592945050506040919091013590565b60008060008060808587031215614ec1578182fd5b843593506020850135614ed381615dad565b93969395505050506040820135916060013590565b60008060a08385031215614efa578182fd5b82359150614f0b84602085016149fe565b90509250929050565b60008060408385031215614f26578182fd5b823591506020830135614c1f81615dc2565b60008060408385031215614f4a578182fd5b50508035926020909101359150565b600080600060608486031215614f6d578081fd5b83359250602084013591506040840135614f8681615dad565b809150509250925092565b60008060008060808587031215614fa6578182fd5b84359350602085013592506040850135614d0781615dad565b600080600080600080600060e0888a031215614fd9578081fd5b87359650602088013595506040880135614ff281615dad565b9450606088013593506080880135925060a0880135915061501560c08901614b3e565b905092959891949750929550565b60008060006101608486031215615038578081fd5b83359250602084013591506150508560408601614a7f565b90509250925092565b60008060008060006103a08688031215615071578283fd5b85359450602086013593506150898760408801614a7f565b9250615099876101608801614a7f565b91506150a9876102808801614a7f565b90509295509295909350565b6000806000606084860312156150c9578081fd5b505081359360208301359350604090920135919050565b600080600080600060a086880312156150f7578283fd5b85359450602086013561510981615dad565b94979496505050506040830135926060810135926080909101359150565b600080600080600080600080610100898b031215615143578182fd5b88359750602089013561515581615dad565b965060408901359550606089013594506080890135935060a0890135925060c0890135915061518660e08a01614b3e565b90509295985092959890939650565b600080600060a084860312156151a9578081fd5b833592506151ba8560208601614ae9565b9150608084013590509250925092565b600080600080600060e086880312156151e1578283fd5b853594506151f28760208801614ae9565b949794965050505060808301359260a08101359260c0909101359150565b600080600080600080600080610140898b03121561522c578182fd5b8835975061523d8a60208b01614ae9565b96506080890135955060a0890135945060c0890135935060e0890135925061010089013591506151866101208a01614b3e565b6000806000806000806101008789031215615289578384fd5b8635955061529a8860208901614ae9565b94506080870135935060a0870135925060c08701359150614e6960e08801614b3e565b8060005b6009811015610bb45781358452602093840193909101906001016152c1565b8060005b6009811015610bb45781518452602093840193909101906001016152e4565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b60808101818360005b6004811015614a64578151835260209283019290910190600101615377565b901515815260200190565b90815260200190565b9283526020830191909152604082015260600190565b60208082526012908201527f696e76616c696420707269636520646174610000000000000000000000000000604082015260600190565b60208082526012908201527f616d6f756e74206973206e656761746976650000000000000000000000000000604082015260600190565b6020808252601f908201527f6e6567617469766520646f6e6174656420696e737572616e63652066756e6400604082015260600190565b6020808252600e908201527f696e76616c696420616d6f756e74000000000000000000000000000000000000604082015260600190565b6020808252601c908201527f6361736820616d6f756e74206d75737420626520706f73697469766500000000604082015260600190565b60208082526017908201527f63616c6c6572206973206e6f74207175616c6966696564000000000000000000604082015260600190565b60208082526013908201527f6b6565706572206973206e6f7420616464656400000000000000000000000000604082015260600190565b60208082526013908201527f6e6f206d617267696e20746f20736574746c6500000000000000000000000000604082015260600190565b6020808252600d908201526c696e76616c696420666c61677360981b604082015260600190565b60208082526029908201527f7368617265207472616e736665722064656c61792073686f756c64206265206160408201527f74206c6561736520310000000000000000000000000000000000000000000000606082015260800190565b6020808252601f908201527f6361736820746f2072657475726e206d75737420626520706f73697469766500604082015260600190565b6020808252601d908201527f70657270657475616c20636f756e742065786365656473206c696d6974000000604082015260600190565b6020808252601c908201527f696e697469616c4d617267696e52617465206973206e6f742073657400000000604082015260600190565b60208082526023908201527f63616e6e6f74207472616e7366657220746f2063757272656e74206f7065726160408201527f746f720000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526016908201527f696e73756666696369656e7420706f6f6c206361736800000000000000000000604082015260600190565b6020808252818101527f736861726520746f2072656d6f7665206d75737420626520706f736974697665604082015260600190565b6020808252602b908201527f74686572652073686f756c64206265206174206c65617374203120706572706560408201527f7475616c20746f2072756e000000000000000000000000000000000000000000606082015260800190565b60208082526017908201527f6b656570657220697320616c7265616479206164646564000000000000000000604082015260600190565b60208082526014908201527f696e737572616e636546756e64436170203c2030000000000000000000000000604082015260600190565b60208082526016908201527f636f6c6c61746572616c206973206e6567617469766500000000000000000000604082015260600190565b60208082526017908201527f6e6577206f70657261746f7220697320696e76616c6964000000000000000000604082015260600190565b6020808252601a908201527f696e73756666696369656e742073686172652062616c616e6365000000000000604082015260600190565b60208082526013908201527f676f7665726e6f7220697320696e76616c696400000000000000000000000000604082015260600190565b6020808252601b908201527f696e637265617365206e6567617469766520706f6f6c20636173680000000000604082015260600190565b60208082526024908201527f6661696c20746f2072656d6f7665206b65657065722066726f6d20776869746560408201527f6c69737400000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252601f908201527f6661696c20746f20616464206b656570657220746f2077686974656c69737400604082015260600190565b60208082526011908201527f696e76616c696420706172616d65746572000000000000000000000000000000604082015260600190565b6020808252601b908201527f6465637265617365206e6567617469766520706f6f6c20636173680000000000604082015260600190565b60208082526016908201527f73686172655472616e7366657244656c6179203c203100000000000000000000604082015260600190565b60208082526022908201527f6e6567617469766520706f6f6c4361736820616674657220736574746c65206160408201527f6c6c000000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526015908201527f636f6c6c61746572616c20697320696e76616c69640000000000000000000000604082015260600190565b60208082526013908201527f746172676574206c65766572616765203d203000000000000000000000000000604082015260600190565b6020808252601c908201527f70657270657475616c20696e646578206f7574206f662072616e676500000000604082015260600190565b6020808252600e908201527f696e76616c696420747261646572000000000000000000000000000000000000604082015260600190565b60208082526010908201527f6c6971756964697479436170203c203000000000000000000000000000000000604082015260600190565b60208082526016908201527f6e6f2070657270657475616c20746f20736574746c6500000000000000000000604082015260600190565b9283526001600160a01b03919091166020830152604082015260600190565b9182526001600160a01b0316602082015260400190565b8281526101408101613b0960208301846152e0565b8481526103808101615c8860208301866152e0565b615c966101408301856152e0565b610a176102608301846152e0565b918252602082015260400190565b878152602081018790526001600160a01b03861660408201526104e08101615cdd60608301876152bd565b615ceb6101808301866152bd565b615cf96102a08301856152bd565b611bb06103c08301846152bd565b8881526001600160a01b038881166020830152878116604083015286811660608301528581166080830152841660a08201526103008101615d4b60c08301856152bd565b615d596101e08301846152bd565b9998505050505050505050565b948552602085019390935260408401919091526060830152608082015260a00190565b60405181810167ffffffffffffffff81118282101715615da557fe5b604052919050565b6001600160a01b03811681146134e357600080fd5b80151581146134e357600080fdfe5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f775369676e6564536166654d6174683a206469766973696f6e206f766572666c6f775369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7753616665436173743a2076616c756520646f65736e27742066697420696e20616e20696e743235365369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f77a26469706673582212203c5c2c0277e4d56ab6939bf95e43a812c661771cd5d81862f16273ab60bc4b9f64736f6c63430007040033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.