Transactions
Token Transfers
Tokens
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- Vault
- Optimization enabled
- true
- Compiler version
- v0.7.6+commit.7338295f
- Optimization runs
- 200
- EVM Version
- istanbul
- Verified at
- 2024-06-07T06:56:07.986465Z
Constructor Arguments
0x0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f1
Arg [0] (address) : 0x3cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f1
contracts/protocol/core/Vault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;
import "../../dependencies/openzeppelin/contracts/SafeMath.sol";
import "../../dependencies/openzeppelin/contracts/ReentrancyGuard.sol";
import "../../dependencies/openzeppelin/contracts/IERC20.sol";
import "../../dependencies/openzeppelin/contracts/SafeERC20.sol";
import "../../interfaces/IUSDPH.sol";
import "../../interfaces/IVault.sol";
import "../../interfaces/IVaultConfigurator.sol";
import "../../interfaces/IPhamePriceOracle.sol";
import "../libraries/helpers/VaultErrors.sol";
import "../libraries/logic/TokenConfigsLogic.sol";
import "../libraries/utils/VaultUtils.sol";
import "../libraries/utils/GeneralUtils.sol";
import "../libraries/types/DataTypes.sol";
contract Vault is IVault, ReentrancyGuard {
using SafeMath for uint256;
using SafeERC20 for IERC20;
using TokenConfigsLogic for DataTypes.TokenConfigs;
uint256 public constant BASIS_POINTS_DIVISOR = 10000;
uint256 public constant PRICE_PRECISION = 10 ** 30;
uint256 public constant USDPH_DECIMALS = 18;
uint256 public constant FUNDING_RATE_PRECISION = 1000000;
IAddressesProvider public immutable override addressesProvider;
DataTypes.VaultConfig private _vaultConfig;
DataTypes.TokenConfigs private _tokenConfigs;
mapping(address => mapping(address => DataTypes.AccountTokenConfig))
private _accountTokenConfigs;
// tokenBalances is used only to determine _transferIn values
mapping(address => uint256) public override tokenBalances;
// usdphAmounts tracks the amount of USDPH debt for each whitelisted token
mapping(address => uint256) public override usdphAmounts;
// poolAmounts tracks the number of received tokens that can be used for leverage
// this is tracked separately from tokenBalances to exclude funds that are deposited as margin collateral
mapping(address => uint256) public override poolAmounts;
// reservedAmounts tracks the number of tokens reserved for open leverage positions
mapping(address => uint256) public override reservedAmounts;
// guaranteedUsd tracks the amount of USD that is "guaranteed" by opened leverage positions
// this value is used to calculate the redemption values for selling of USDPH
// this is an estimated amount, it is possible for the actual guaranteed value to be lower
// in the case of sudden price decreases, the guaranteed value should be corrected
// after liquidations are carried out
mapping(address => uint256) public override guaranteedUsd;
// cumulativeFundingRates tracks the funding rates based on utilization
mapping(address => uint256) public override cumulativeFundingRates;
// lastFundingTimes tracks the last time funding was updated for a token
mapping(address => uint256) public override lastFundingTimes;
// positions tracks all open positions
mapping(bytes32 => Position) public positions;
// feeReserves tracks the amount of fees per token
mapping(address => uint256) public override feeReserves;
mapping(address => uint256) public override globalShortSizes;
constructor(IAddressesProvider addressesProvider_) {
addressesProvider = addressesProvider_;
}
function setVaultConfig(
DataTypes.VaultConfig calldata vaultConfig
) external override {
_validateConfigurator();
_vaultConfig = vaultConfig;
}
function getVaultConfig()
external
view
override
returns (DataTypes.VaultConfig memory)
{
return _vaultConfig;
}
function allWhitelistedTokensLength()
external
view
override
returns (uint256)
{
return _tokenConfigs.allWhitelistedTokens.length;
}
function totalTokenWeights() external view override returns (uint256) {
return _tokenConfigs.totalTokenWeights;
}
function allWhitelistedTokens(
uint256 i
) external view override returns (address) {
return _tokenConfigs.allWhitelistedTokens[i];
}
function setTokenConfig(
address _token,
DataTypes.TokenConfig calldata tokenConfig
) external override {
_validateConfigurator();
_tokenConfigs.setTokenConfig(_token, tokenConfig);
}
function clearTokenConfig(address _token) external override {
_validateConfigurator();
_tokenConfigs.clearTokenConfig(_token);
}
function getTokenConfig(
address _token
) external view override returns (DataTypes.TokenConfig memory) {
return _tokenConfigs.tokenConfigs[_token];
}
function setAccountTokenConfig(
address _account,
address _token,
DataTypes.AccountTokenConfig calldata accountTokenConfig
) external override {
_validateConfigurator();
_accountTokenConfigs[_account][_token] = accountTokenConfig;
}
function getAccountTokenConfig(
address _account,
address _token
) external view override returns (DataTypes.AccountTokenConfig memory) {
return _accountTokenConfigs[_account][_token];
}
function setUsdphAmount(address _token, uint256 _amount) external override {
_validateConfigurator();
uint256 usdphAmount = usdphAmounts[_token];
if (_amount > usdphAmount) {
_increaseUsdphAmount(_token, _amount.sub(usdphAmount));
return;
}
_decreaseUsdphAmount(_token, usdphAmount.sub(_amount));
}
function withdrawFees(
address _token,
address _receiver
) external override returns (uint256) {
_validateManager();
uint256 amount = feeReserves[_token];
if (amount == 0) {
return 0;
}
feeReserves[_token] = 0;
_transferOut(_token, amount, _receiver);
return amount;
}
// deposit into the pool without minting USDPH tokens
// useful in allowing the pool to become over-collaterised
function directPoolDeposit(address _token) external override nonReentrant {
_validate(
_tokenConfigs.tokenConfigs[_token].isWhitelisted,
VaultErrors.V_TOKEN_NOT_WHITELISTED
);
uint256 tokenAmount = _transferIn(_token);
_validate(tokenAmount > 0, VaultErrors.V_INVALID_TOKEN_AMOUNT);
_increasePoolAmount(_token, tokenAmount);
emit DirectPoolDeposit(_token, tokenAmount);
}
function buyUSDPH(
address _token,
address _receiver
) external override nonReentrant returns (uint256) {
_validateManager();
_validate(
_tokenConfigs.tokenConfigs[_token].isWhitelisted,
VaultErrors.V_TOKEN_NOT_WHITELISTED
);
address usdph = addressesProvider.getUsdph();
uint256 tokenAmount = _transferIn(_token);
_validate(tokenAmount > 0, VaultErrors.V_INVALID_TOKEN_AMOUNT);
updateCumulativeFundingRate(_token);
uint256 price = IPhamePriceOracle(addressesProvider.getPriceOracle())
.getPrice(_token, false);
uint256 usdphAmount = tokenAmount.mul(price).div(PRICE_PRECISION);
usdphAmount = _adjustForDecimals(usdphAmount, _token, usdph);
_validate(usdphAmount > 0, VaultErrors.V_INVALID_USDPH_AMOUNT);
uint256 feeBps = VaultUtils.getBuyUsdphFeeBps(
addressesProvider,
_token,
usdphAmount
);
(uint256 amountAfterFees, uint256 feeAmount) = _collectSwapFees(
_token,
tokenAmount,
feeBps
);
uint256 mintAmount = amountAfterFees.mul(price).div(PRICE_PRECISION);
mintAmount = _adjustForDecimals(mintAmount, _token, usdph);
_increaseUsdphAmount(_token, mintAmount);
_increasePoolAmount(_token, amountAfterFees);
IUSDPH(usdph).mint(_receiver, mintAmount);
emit BuyUSDPH(_receiver, _token, tokenAmount, mintAmount, feeBps);
_payLpFees(_token, feeAmount);
return mintAmount;
}
function buyUSDPHwithoutFee(
address _token
) external override nonReentrant returns (uint256) {
_validateManager();
_validate(
_tokenConfigs.tokenConfigs[_token].isWhitelisted,
VaultErrors.V_TOKEN_NOT_WHITELISTED
);
address usdph = addressesProvider.getUsdph();
uint256 tokenAmount = _transferIn(_token);
_validate(tokenAmount > 0, VaultErrors.V_INVALID_TOKEN_AMOUNT);
updateCumulativeFundingRate(_token);
uint256 price = IPhamePriceOracle(addressesProvider.getPriceOracle())
.getPrice(_token, false);
uint256 usdphAmount = tokenAmount.mul(price).div(PRICE_PRECISION);
usdphAmount = _adjustForDecimals(usdphAmount, _token, usdph);
_validate(usdphAmount > 0, VaultErrors.V_INVALID_USDPH_AMOUNT);
uint256 mintAmount = tokenAmount.mul(price).div(PRICE_PRECISION);
mintAmount = _adjustForDecimals(mintAmount, _token, usdph);
usdphAmounts[_token] = usdphAmounts[_token].add(mintAmount);
emit IncreaseUsdphAmount(_token, mintAmount);
_increasePoolAmount(_token, tokenAmount);
IUSDPH(usdph).mint(msg.sender, mintAmount);
emit BuyUSDPH(msg.sender, _token, tokenAmount, mintAmount, 0);
return mintAmount;
}
function sellUSDPH(
address _token,
address _receiver
) external override nonReentrant returns (uint256) {
_validateManager();
_validate(
_tokenConfigs.tokenConfigs[_token].isWhitelisted,
VaultErrors.V_TOKEN_NOT_WHITELISTED
);
address usdph = addressesProvider.getUsdph();
uint256 usdphAmount = _transferIn(usdph);
_validate(usdphAmount > 0, VaultErrors.V_INVALID_USDPH_AMOUNT);
updateCumulativeFundingRate(_token);
uint256 redemptionAmount = getRedemptionAmount(_token, usdphAmount);
_validate(
redemptionAmount > 0,
VaultErrors.V_INVALID_REDEMPTION_AMOUNT
);
_decreaseUsdphAmount(_token, usdphAmount);
_decreasePoolAmount(_token, redemptionAmount);
IUSDPH(usdph).burn(address(this), usdphAmount);
// the _transferIn call increased the value of tokenBalances[usdph]
// usually decreases in token balances are synced by calling _transferOut
// however, for usdph, the tokens are burnt, so tokenBalances should
// be manually updated record the decrease in tokens
tokenBalances[usdph] = IERC20(usdph).balanceOf(address(this));
uint256 feeBps = VaultUtils.getSellUsdphFeeBps(
addressesProvider,
_token,
usdphAmount
);
(uint256 amountOut, uint256 feeAmount) = _collectSwapFees(
_token,
redemptionAmount,
feeBps
);
_validate(amountOut > 0, VaultErrors.V_INVALID_AMOUNT_OUT);
_transferOut(_token, amountOut, _receiver);
emit SellUSDPH(_receiver, _token, usdphAmount, amountOut, feeBps);
_payLpFees(_token, feeAmount);
return amountOut;
}
function swap(
address _tokenIn,
address _tokenOut,
address _receiver
) external override nonReentrant returns (uint256) {
_validate(_vaultConfig.isSwapEnabled, VaultErrors.V_SWAPS_NOT_ENABLED);
_validate(
_tokenConfigs.tokenConfigs[_tokenIn].isWhitelisted,
VaultErrors.V_TOKEN_NOT_WHITELISTED
);
_validate(
_tokenConfigs.tokenConfigs[_tokenOut].isWhitelisted,
VaultErrors.V_TOKEN_NOT_WHITELISTED
);
_validate(_tokenIn != _tokenOut, VaultErrors.V_SAME_TOKEN);
updateCumulativeFundingRate(_tokenIn);
updateCumulativeFundingRate(_tokenOut);
uint256 amountIn = _transferIn(_tokenIn);
_validate(amountIn > 0, VaultErrors.V_INVALID_AMOUNT_IN);
IPhamePriceOracle priceOracle = IPhamePriceOracle(
addressesProvider.getPriceOracle()
);
uint256 priceIn = priceOracle.getPrice(_tokenIn, false);
uint256 priceOut = priceOracle.getPrice(_tokenOut, true);
uint256 amountOut = amountIn.mul(priceIn).div(priceOut);
amountOut = _adjustForDecimals(amountOut, _tokenIn, _tokenOut);
// adjust usdphAmounts by the same usdphAmount as debt is shifted between the assets
uint256 usdphAmount = amountIn.mul(priceIn).div(PRICE_PRECISION);
usdphAmount = _adjustForDecimals(
usdphAmount,
_tokenIn,
addressesProvider.getUsdph()
);
uint256 feeBps = VaultUtils.getSwapFeeBps(
addressesProvider,
_tokenIn,
_tokenOut,
usdphAmount
);
(uint256 amountOutAfterFees, uint256 feeAmount) = _collectSwapFees(
_tokenOut,
amountOut,
feeBps
);
_increaseUsdphAmount(_tokenIn, usdphAmount);
_decreaseUsdphAmount(_tokenOut, usdphAmount);
_increasePoolAmount(_tokenIn, amountIn);
_decreasePoolAmount(_tokenOut, amountOut);
_validate(
poolAmounts[_tokenOut] >=
_tokenConfigs.tokenConfigs[_tokenOut].bufferAmount,
VaultErrors.V_POOL_LESS_THAN_BUFFER
);
_transferOut(_tokenOut, amountOutAfterFees, _receiver);
emit Swap(
_receiver,
_tokenIn,
_tokenOut,
amountIn,
amountOut,
amountOutAfterFees,
feeBps
);
_payLpFees(_tokenOut, feeAmount);
return amountOutAfterFees;
}
function increasePosition(
address _account,
address _collateralToken,
address _indexToken,
uint256 _sizeDelta,
bool _isLong
) external override nonReentrant {
_validateRouter();
_tokenConfigs.validateTokens(_collateralToken, _indexToken, _isLong);
_validate(
_vaultConfig.isLeverageEnabled,
VaultErrors.V_LEVERAGE_NOT_ENABLED
);
updateCumulativeFundingRate(_collateralToken);
bytes32 key = _getPositionKey(
_account,
_collateralToken,
_indexToken,
_isLong
);
Position storage position = positions[key];
uint256 price = IPhamePriceOracle(addressesProvider.getPriceOracle())
.getPrice(_indexToken, _isLong);
if (_sizeDelta > 0) {
_validate(
position.size.add(_sizeDelta) <=
getAccountTokenPositionSizeLimit(
_account,
_indexToken,
_isLong
),
VaultErrors.V_MAX_POSITION_SIZE_EXCEEDED
);
}
if (position.size == 0) {
position.averagePrice = price;
}
if (position.size > 0 && _sizeDelta > 0) {
position.averagePrice = _getNextAveragePrice(
_indexToken,
position.size,
position.averagePrice,
_isLong,
price,
_sizeDelta,
position.lastIncreasedTime
);
}
(uint256 fee, uint256 feeAmount) = _collectMarginFees(
_account,
_collateralToken,
_indexToken,
_isLong,
_sizeDelta,
position.size,
position.entryFundingRate
);
uint256 collateralDelta = _transferIn(_collateralToken);
uint256 collateralDeltaUsd = tokenToUsdMin(
_collateralToken,
collateralDelta
);
position.collateral = position.collateral.add(collateralDeltaUsd);
_validate(
position.collateral >= fee,
VaultErrors.V_INSUFFICIENT_COLLATERAL_FOR_FEES
);
position.collateral = position.collateral.sub(fee);
position.entryFundingRate = cumulativeFundingRates[_collateralToken];
position.size = position.size.add(_sizeDelta);
position.lastIncreasedTime = block.timestamp;
_validate(position.size > 0, VaultErrors.V_INVALID_POSITION_SIZE);
_validatePosition(position.size, position.collateral);
validateLiquidation(
_account,
_collateralToken,
_indexToken,
_isLong,
true
);
// reserve tokens to pay profits on the position
uint256 reserveDelta = usdToToken(_collateralToken, _sizeDelta, true);
position.reserveAmount = position.reserveAmount.add(reserveDelta);
_increaseReservedAmount(_collateralToken, reserveDelta);
if (_isLong) {
// guaranteedUsd stores the sum of (position.size - position.collateral) for all positions
// if a fee is charged on the collateral then guaranteedUsd should be increased by that fee amount
// since (position.size - position.collateral) would have increased by `fee`
_adjustGuaranteedUsd(
_collateralToken,
_sizeDelta.add(fee),
collateralDeltaUsd,
_sizeDelta > 0 // don't check max size if only adding collateral
);
// treat the deposited collateral as part of the pool
_increasePoolAmount(_collateralToken, collateralDelta);
// fees need to be deducted from the pool since fees are deducted from position.collateral
// and collateral is treated as part of the pool
_decreasePoolAmount(
_collateralToken,
usdToToken(_collateralToken, fee, false)
);
} else {
_increaseGlobalShortSize(_indexToken, _sizeDelta);
}
emit IncreasePosition(
key,
_account,
_collateralToken,
_indexToken,
collateralDeltaUsd,
_sizeDelta,
_isLong,
price,
fee
);
emit UpdatePosition(
key,
position.size,
position.collateral,
position.averagePrice,
position.entryFundingRate,
position.reserveAmount,
position.realisedPnl,
price
);
_payLpFees(_collateralToken, feeAmount);
}
function decreasePosition(
address _account,
address _collateralToken,
address _indexToken,
uint256 _collateralDelta,
uint256 _sizeDelta,
bool _isLong,
address _receiver
) external override nonReentrant returns (uint256) {
_validateRouter();
return
_decreasePosition(
_account,
_collateralToken,
_indexToken,
_collateralDelta,
_sizeDelta,
_isLong,
_receiver
);
}
function _decreasePosition(
address _account,
address _collateralToken,
address _indexToken,
uint256 _collateralDelta,
uint256 _sizeDelta,
bool _isLong,
address _receiver
) private returns (uint256) {
updateCumulativeFundingRate(_collateralToken);
bytes32 key = _getPositionKey(
_account,
_collateralToken,
_indexToken,
_isLong
);
Position storage position = positions[key];
_validate(position.size > 0, VaultErrors.V_EMPTY_POSITION);
_validate(
position.size >= _sizeDelta,
VaultErrors.V_POSITION_SIZE_EXCEEDED
);
_validate(
position.collateral >= _collateralDelta,
VaultErrors.V_POSITION_COLLATERAL_EXCEEDED
);
uint256 collateral = position.collateral;
// scrop variables to avoid stack too deep VaultErrors
{
uint256 reserveDelta = position.reserveAmount.mul(_sizeDelta).div(
position.size
);
position.reserveAmount = position.reserveAmount.sub(reserveDelta);
_decreaseReservedAmount(_collateralToken, reserveDelta);
}
(uint256 usdOut, uint256 usdOutAfterFee) = _reduceCollateral(
_account,
_collateralToken,
_indexToken,
_collateralDelta,
_sizeDelta,
_isLong
);
if (position.size != _sizeDelta) {
position.entryFundingRate = cumulativeFundingRates[
_collateralToken
];
position.size = position.size.sub(_sizeDelta);
_validatePosition(position.size, position.collateral);
validateLiquidation(
_account,
_collateralToken,
_indexToken,
_isLong,
true
);
if (_isLong) {
_adjustGuaranteedUsd(
_collateralToken,
collateral.sub(position.collateral),
_sizeDelta,
true
);
}
uint256 price = IPhamePriceOracle(
addressesProvider.getPriceOracle()
).getPrice(_indexToken, !_isLong);
emit DecreasePosition(
key,
_account,
_collateralToken,
_indexToken,
_collateralDelta,
_sizeDelta,
_isLong,
price,
usdOut.sub(usdOutAfterFee)
);
emit UpdatePosition(
key,
position.size,
position.collateral,
position.averagePrice,
position.entryFundingRate,
position.reserveAmount,
position.realisedPnl,
price
);
} else {
if (_isLong) {
_adjustGuaranteedUsd(
_collateralToken,
collateral,
_sizeDelta,
true
);
}
uint256 price = IPhamePriceOracle(
addressesProvider.getPriceOracle()
).getPrice(_indexToken, !_isLong);
emit DecreasePosition(
key,
_account,
_collateralToken,
_indexToken,
_collateralDelta,
_sizeDelta,
_isLong,
price,
usdOut.sub(usdOutAfterFee)
);
emit ClosePosition(
key,
position.size,
position.collateral,
position.averagePrice,
position.entryFundingRate,
position.reserveAmount,
position.realisedPnl
);
delete positions[key];
}
if (!_isLong) {
_decreaseGlobalShortSize(_indexToken, _sizeDelta);
}
if (usdOut > 0) {
if (_isLong) {
_decreasePoolAmount(
_collateralToken,
usdToToken(_collateralToken, usdOut, false)
);
}
uint256 amountOutAfterFees = usdToToken(
_collateralToken,
usdOutAfterFee,
false
);
_transferOut(_collateralToken, amountOutAfterFees, _receiver);
return amountOutAfterFees;
}
return 0;
}
function liquidatePosition(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
address _feeReceiver
) external override nonReentrant {
// only position manager can liquidate
_validate(
addressesProvider.getPositionManager() == msg.sender,
VaultErrors.V_INVALID_LIQUIDATOR
);
updateCumulativeFundingRate(_collateralToken);
bytes32 key = _getPositionKey(
_account,
_collateralToken,
_indexToken,
_isLong
);
Position memory position = positions[key];
_validate(position.size > 0, VaultErrors.V_EMPTY_POSITION);
(uint256 liquidationState, uint256 marginFees) = validateLiquidation(
_account,
_collateralToken,
_indexToken,
_isLong,
false
);
_validate(
liquidationState != 0,
VaultErrors.V_POSITION_CANNOT_BE_LIQUIDATED
);
if (liquidationState == 2) {
// max leverage exceeded but there is collateral remaining after deducting losses so decreasePosition instead
_decreasePosition(
_account,
_collateralToken,
_indexToken,
0,
position.size,
_isLong,
_account
);
return;
}
uint256 feeTokens = usdToToken(_collateralToken, marginFees, false);
feeReserves[_collateralToken] = feeReserves[_collateralToken].add(
feeTokens
);
emit CollectMarginFees(_collateralToken, marginFees, feeTokens);
_decreaseReservedAmount(_collateralToken, position.reserveAmount);
if (_isLong) {
_adjustGuaranteedUsd(
_collateralToken,
0,
position.size.sub(position.collateral),
false
);
_decreasePoolAmount(
_collateralToken,
usdToToken(_collateralToken, marginFees, false)
);
}
uint256 markPrice = IPhamePriceOracle(
addressesProvider.getPriceOracle()
).getPrice(_indexToken, !_isLong);
emit LiquidatePosition(
key,
_account,
_collateralToken,
_indexToken,
_isLong,
position.size,
position.collateral,
position.reserveAmount,
position.realisedPnl,
markPrice
);
if (!_isLong && marginFees < position.collateral) {
uint256 remainingCollateral = position.collateral.sub(marginFees);
_increasePoolAmount(
_collateralToken,
usdToToken(_collateralToken, remainingCollateral, false)
);
}
if (!_isLong) {
_decreaseGlobalShortSize(_indexToken, position.size);
}
delete positions[key];
// pay the fee receiver using the pool, we assume that in general the liquidated amount should be sufficient to cover
// the liquidation fees
uint256 liquidationFeeUsd = _vaultConfig.liquidationFeeUsd;
_decreasePoolAmount(
_collateralToken,
usdToToken(_collateralToken, liquidationFeeUsd, false)
);
_transferOut(
_collateralToken,
usdToToken(_collateralToken, liquidationFeeUsd, false),
_feeReceiver
);
_payLpFees(_collateralToken, feeTokens);
}
// validateLiquidation returns (state, fees)
function validateLiquidation(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
bool _raise
) public view override returns (uint256, uint256) {
return
VaultUtils.validateLiquidation(
addressesProvider,
_account,
_collateralToken,
_indexToken,
_isLong,
_raise
);
}
function getRedemptionAmount(
address _token,
uint256 _usdphAmount
) public view override returns (uint256) {
uint256 price = IPhamePriceOracle(addressesProvider.getPriceOracle())
.getPrice(_token, true);
uint256 redemptionAmount = _usdphAmount.mul(PRICE_PRECISION).div(price);
return
_adjustForDecimals(
redemptionAmount,
addressesProvider.getUsdph(),
_token
);
}
function _adjustForDecimals(
uint256 _amount,
address _tokenDiv,
address _tokenMul
) private view returns (uint256) {
address usdph = addressesProvider.getUsdph();
uint256 decimalsDiv = _tokenDiv == usdph
? USDPH_DECIMALS
: _tokenConfigs.tokenConfigs[_tokenDiv].decimals;
uint256 decimalsMul = _tokenMul == usdph
? USDPH_DECIMALS
: _tokenConfigs.tokenConfigs[_tokenMul].decimals;
return _amount.mul(10 ** decimalsMul).div(10 ** decimalsDiv);
}
function tokenToUsdMin(
address _token,
uint256 _tokenAmount
) public view override returns (uint256) {
if (_tokenAmount == 0) {
return 0;
}
return
_tokenAmount
.mul(
IPhamePriceOracle(addressesProvider.getPriceOracle())
.getPrice(_token, false)
)
.div(10 ** _tokenConfigs.tokenConfigs[_token].decimals);
}
function usdToToken(
address _token,
uint256 _usdAmount,
bool _maximise
) private view returns (uint256) {
if (_usdAmount == 0) {
return 0;
}
return
_usdAmount
.mul(10 ** _tokenConfigs.tokenConfigs[_token].decimals)
.div(
IPhamePriceOracle(addressesProvider.getPriceOracle())
.getPrice(_token, !_maximise)
);
}
function getPosition(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong
)
public
view
override
returns (
uint256,
uint256,
uint256,
uint256,
uint256,
uint256,
bool,
uint256
)
{
Position memory position = positions[
_getPositionKey(_account, _collateralToken, _indexToken, _isLong)
];
uint256 realisedPnl = position.realisedPnl > 0
? uint256(position.realisedPnl)
: uint256(-position.realisedPnl);
return (
position.size, // 0
position.collateral, // 1
position.averagePrice, // 2
position.entryFundingRate, // 3
position.reserveAmount, // 4
realisedPnl, // 5
position.realisedPnl >= 0, // 6
position.lastIncreasedTime // 7
);
}
function updateCumulativeFundingRate(address _collateralToken) public {
uint256 fundingInterval = _tokenConfigs
.tokenConfigs[_collateralToken]
.fundingInterval;
if (lastFundingTimes[_collateralToken] == 0) {
lastFundingTimes[_collateralToken] = block
.timestamp
.div(fundingInterval)
.mul(fundingInterval);
return;
}
if (
lastFundingTimes[_collateralToken].add(fundingInterval) >
block.timestamp
) {
return;
}
uint256 fundingRate = getNextFundingRate(_collateralToken);
cumulativeFundingRates[_collateralToken] = cumulativeFundingRates[
_collateralToken
].add(fundingRate);
lastFundingTimes[_collateralToken] = block
.timestamp
.div(fundingInterval)
.mul(fundingInterval);
emit UpdateFundingRate(
_collateralToken,
cumulativeFundingRates[_collateralToken]
);
}
function getNextFundingRate(
address _token
) public view override returns (uint256) {
return VaultUtils.getNextFundingRate(addressesProvider, _token);
}
// for longs: nextAveragePrice = (nextPrice * nextSize)/ (nextSize + delta)
// for shorts: nextAveragePrice = (nextPrice * nextSize) / (nextSize - delta)
function _getNextAveragePrice(
address _indexToken,
uint256 _size,
uint256 _averagePrice,
bool _isLong,
uint256 _nextPrice,
uint256 _sizeDelta,
uint256 _lastIncreasedTime
) private view returns (uint256) {
(bool hasProfit, uint256 delta) = getDelta(
_indexToken,
_size,
_averagePrice,
_isLong,
_lastIncreasedTime
);
uint256 nextSize = _size.add(_sizeDelta);
uint256 divisor;
if (_isLong) {
divisor = hasProfit ? nextSize.add(delta) : nextSize.sub(delta);
} else {
divisor = hasProfit ? nextSize.sub(delta) : nextSize.add(delta);
}
return _nextPrice.mul(nextSize).div(divisor);
}
function getMinProfitTime(
address _indexToken,
uint256 _size,
bool _isLong
) public view override returns (uint256) {
return
VaultUtils.getMinProfitTime(
addressesProvider,
_indexToken,
_size,
_isLong
);
}
function getDelta(
address _indexToken,
uint256 _size,
uint256 _averagePrice,
bool _isLong,
uint256 _lastIncreasedTime
) public view override returns (bool, uint256) {
return
VaultUtils.getDelta(
addressesProvider,
_indexToken,
_size,
_averagePrice,
_isLong,
_lastIncreasedTime
);
}
function getFundingFee(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
uint256 _size,
uint256 _entryFundingRate
) public view returns (uint256) {
return
VaultUtils.getFundingFee(
this,
_account,
_collateralToken,
_indexToken,
_isLong,
_size,
_entryFundingRate
);
}
// cases to consider
// 1. initialAmount is far from targetAmount, action increases balance slightly => high rebate
// 2. initialAmount is far from targetAmount, action increases balance largely => high rebate
// 3. initialAmount is close to targetAmount, action increases balance slightly => low rebate
// 4. initialAmount is far from targetAmount, action reduces balance slightly => high tax
// 5. initialAmount is far from targetAmount, action reduces balance largely => high tax
// 6. initialAmount is close to targetAmount, action reduces balance largely => low tax
// 7. initialAmount is above targetAmount, nextAmount is below targetAmount and vice versa
// 8. a large swap should have similar fees as the same trade split into multiple smaller swaps
function getFeeBps(
address _token,
uint256 _usdphDelta,
uint256 _feeBps,
uint256 _taxBps,
bool _increment
) public view override returns (uint256) {
return
VaultUtils.getFeeBps(
addressesProvider,
_token,
_usdphDelta,
_feeBps,
_taxBps,
_increment
);
}
function getAccountTokenPositionSizeLimit(
address _account,
address _token,
bool _isLong
) public view override returns (uint256) {
DataTypes.AccountTokenConfig
memory accountTokenConfig = _accountTokenConfigs[_account][_token];
DataTypes.TokenConfig memory tokenConfig = _tokenConfigs.tokenConfigs[
_token
];
if (_isLong) {
if (accountTokenConfig.isLongBlacklisted) {
return 0;
} else if (accountTokenConfig.maxPositionLongSizeOverride == 0) {
return tokenConfig.maxPositionLongSize;
} else {
return accountTokenConfig.maxPositionLongSizeOverride;
}
} else {
if (accountTokenConfig.isShortBlacklisted) {
return 0;
} else if (accountTokenConfig.maxPositionShortSizeOverride == 0) {
return tokenConfig.maxPositionShortSize;
} else {
return accountTokenConfig.maxPositionShortSizeOverride;
}
}
}
function _getPositionKey(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong
) private pure returns (bytes32) {
return
keccak256(
abi.encodePacked(
_account,
_collateralToken,
_indexToken,
_isLong
)
);
}
function _reduceCollateral(
address _account,
address _collateralToken,
address _indexToken,
uint256 _collateralDelta,
uint256 _sizeDelta,
bool _isLong
) private returns (uint256, uint256) {
Position storage position;
bool hasProfit;
uint256 adjustedDelta;
// scope variables to avoid stack too deep VaultErrors
{
bytes32 key = _getPositionKey(
_account,
_collateralToken,
_indexToken,
_isLong
);
position = positions[key];
(bool _hasProfit, uint256 delta) = getDelta(
_indexToken,
position.size,
position.averagePrice,
_isLong,
position.lastIncreasedTime
);
hasProfit = _hasProfit;
// get the proportional change in pnl
adjustedDelta = _sizeDelta.mul(delta).div(position.size);
emit UpdatePnl(key, hasProfit, adjustedDelta);
}
(uint256 fee, uint256 feeAmount) = _collectMarginFees(
_account,
_collateralToken,
_indexToken,
_isLong,
_sizeDelta,
position.size,
position.entryFundingRate
);
uint256 usdOut;
// transfer profits out
if (hasProfit && adjustedDelta > 0) {
usdOut = adjustedDelta;
position.realisedPnl = position.realisedPnl + int256(adjustedDelta);
// pay out realised profits from the pool amount for short positions
if (!_isLong) {
uint256 tokenAmount = usdToToken(
_collateralToken,
adjustedDelta,
false
);
_decreasePoolAmount(_collateralToken, tokenAmount);
}
}
if (!hasProfit && adjustedDelta > 0) {
position.collateral = position.collateral.sub(adjustedDelta);
// transfer realised losses to the pool for short positions
// realised losses for long positions are not transferred here as
// _increasePoolAmount was already called in increasePosition for longs
if (!_isLong) {
uint256 tokenAmount = usdToToken(
_collateralToken,
adjustedDelta,
false
);
_increasePoolAmount(_collateralToken, tokenAmount);
}
position.realisedPnl = position.realisedPnl - int256(adjustedDelta);
}
// reduce the position's collateral by _collateralDelta
// transfer _collateralDelta out
if (_collateralDelta > 0) {
usdOut = usdOut.add(_collateralDelta);
position.collateral = position.collateral.sub(_collateralDelta);
}
// if the position will be closed, then transfer the remaining collateral out
if (position.size == _sizeDelta) {
usdOut = usdOut.add(position.collateral);
position.collateral = 0;
}
// if the usdOut is more than the fee then deduct the fee from the usdOut directly
// else deduct the fee from the position's collateral
uint256 usdOutAfterFee = usdOut;
if (usdOut > fee) {
usdOutAfterFee = usdOut.sub(fee);
} else {
position.collateral = position.collateral.sub(fee);
if (_isLong) {
uint256 feeTokens = usdToToken(_collateralToken, fee, false);
_decreasePoolAmount(_collateralToken, feeTokens);
}
}
_payLpFees(_collateralToken, feeAmount);
return (usdOut, usdOutAfterFee);
}
function _validatePosition(
uint256 _size,
uint256 _collateral
) private pure {
if (_size == 0) {
_validate(
_collateral == 0,
VaultErrors.V_COLLATERAL_SHOULD_BE_WITHDRAWN
);
return;
}
_validate(
_size >= _collateral,
VaultErrors.V_SIZE_MUST_BE_MORE_THAN_COLLATERAL
);
}
function _collectSwapFees(
address _token,
uint256 _amount,
uint256 _feeBps
) private returns (uint256, uint256) {
uint256 afterFeeAmount = _amount
.mul(BASIS_POINTS_DIVISOR.sub(_feeBps))
.div(BASIS_POINTS_DIVISOR);
uint256 feeAmount = _amount.sub(afterFeeAmount);
feeReserves[_token] = feeReserves[_token].add(feeAmount);
emit CollectSwapFees(
_token,
tokenToUsdMin(_token, feeAmount),
feeAmount
);
return (afterFeeAmount, feeAmount);
}
function _payLpFees(address token, uint256 feeAmount) private {
uint256 lpFee = feeAmount
.mul(BASIS_POINTS_DIVISOR.sub(_vaultConfig.stakingBps))
.div(BASIS_POINTS_DIVISOR);
if (lpFee > 0) {
feeReserves[token] = feeReserves[token].sub(lpFee);
poolAmounts[token] = poolAmounts[token].add(lpFee);
emit PayLpFees(token, lpFee);
}
}
function _collectMarginFees(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
uint256 _sizeDelta,
uint256 _size,
uint256 _entryFundingRate
) private returns (uint256, uint256) {
uint256 feeUsd = VaultUtils
.getPositionFee(
addressesProvider,
_account,
_collateralToken,
_indexToken,
_isLong,
_sizeDelta
)
.add(
getFundingFee(
_account,
_collateralToken,
_indexToken,
_isLong,
_size,
_entryFundingRate
)
);
uint256 feeTokens = usdToToken(_collateralToken, feeUsd, false);
feeReserves[_collateralToken] = feeReserves[_collateralToken].add(
feeTokens
);
emit CollectMarginFees(_collateralToken, feeUsd, feeTokens);
return (feeUsd, feeTokens);
}
function _transferIn(address _token) private returns (uint256) {
uint256 prevBalance = tokenBalances[_token];
uint256 nextBalance = IERC20(_token).balanceOf(address(this));
tokenBalances[_token] = nextBalance;
return nextBalance.sub(prevBalance);
}
function _transferOut(
address _token,
uint256 _amount,
address _receiver
) private {
IERC20(_token).safeTransfer(_receiver, _amount);
tokenBalances[_token] = IERC20(_token).balanceOf(address(this));
}
function _increasePoolAmount(address _token, uint256 _amount) private {
poolAmounts[_token] = poolAmounts[_token].add(_amount);
uint256 balance = IERC20(_token).balanceOf(address(this));
if (poolAmounts[_token] > balance) {
poolAmounts[_token] = balance;
}
emit IncreasePoolAmount(_token, _amount);
}
function _decreasePoolAmount(address _token, uint256 _amount) private {
poolAmounts[_token] = poolAmounts[_token].sub(_amount);
_validate(
reservedAmounts[_token] <= poolAmounts[_token],
VaultErrors.V_RESERVE_EXCEEDS_POOL
);
emit DecreasePoolAmount(_token, _amount);
}
function _increaseUsdphAmount(address _token, uint256 _amount) private {
usdphAmounts[_token] = usdphAmounts[_token].add(_amount);
uint256 maxUsdphAmount = _tokenConfigs
.tokenConfigs[_token]
.maxUsdphAmount;
if (maxUsdphAmount != 0) {
_validate(
usdphAmounts[_token] <= maxUsdphAmount,
VaultErrors.V_MAX_USDPH_EXCEEDED
);
}
emit IncreaseUsdphAmount(_token, _amount);
}
function _decreaseUsdphAmount(address _token, uint256 _amount) private {
uint256 value = usdphAmounts[_token];
// since USDPH can be minted using multiple assets
// it is possible for the USDPH debt for a single asset to be less than zero
// the USDPH debt is capped to zero for this case
if (value <= _amount) {
usdphAmounts[_token] = 0;
emit DecreaseUsdphAmount(_token, value);
return;
}
usdphAmounts[_token] = value.sub(_amount);
emit DecreaseUsdphAmount(_token, _amount);
}
function _increaseReservedAmount(address _token, uint256 _amount) private {
reservedAmounts[_token] = reservedAmounts[_token].add(_amount);
_validate(
reservedAmounts[_token] <= poolAmounts[_token],
VaultErrors.V_RESERVE_EXCEEDS_POOL
);
emit IncreaseReservedAmount(_token, _amount);
}
function _decreaseReservedAmount(address _token, uint256 _amount) private {
reservedAmounts[_token] = reservedAmounts[_token].sub(
_amount,
"Vault: insufficient reserve"
);
emit DecreaseReservedAmount(_token, _amount);
}
function _adjustGuaranteedUsd(
address _token,
uint256 _increasedUsdAmount,
uint256 _decreasedUsdAmount,
bool _checkMaxSize
) private {
guaranteedUsd[_token] = guaranteedUsd[_token]
.add(_increasedUsdAmount)
.sub(_decreasedUsdAmount);
if (_checkMaxSize && _increasedUsdAmount > _decreasedUsdAmount) {
uint256 maxSize = _tokenConfigs
.tokenConfigs[_token]
.maxGlobalLongSize;
if (maxSize != 0) {
_validate(
guaranteedUsd[_token] <= maxSize,
VaultErrors.V_MAX_GLOBAL_LONG_EXCEEDED
);
}
}
if (_increasedUsdAmount > 0) {
emit IncreaseGuaranteedUsd(_token, _increasedUsdAmount);
}
if (_decreasedUsdAmount > 0) {
emit DecreaseGuaranteedUsd(_token, _decreasedUsdAmount);
}
}
function _increaseGlobalShortSize(address _token, uint256 _amount) private {
if (_amount == 0) {
return;
}
globalShortSizes[_token] = globalShortSizes[_token].add(_amount);
uint256 maxSize = _tokenConfigs.tokenConfigs[_token].maxGlobalShortSize;
if (maxSize != 0) {
_validate(
globalShortSizes[_token] <= maxSize,
VaultErrors.V_MAX_GLOBAL_SHORT_EXCEEDED
);
}
}
function _decreaseGlobalShortSize(address _token, uint256 _amount) private {
uint256 size = globalShortSizes[_token];
if (_amount > size) {
globalShortSizes[_token] = 0;
return;
}
globalShortSizes[_token] = size.sub(_amount);
}
// we have this validation as a function instead of a modifier to reduce contract size
function _validateManager() private view {
_validate(
msg.sender == addressesProvider.getPhlpManager(),
VaultErrors.V_NOT_PHLP_MANAGER
);
}
function _validateConfigurator() private view {
_validate(
msg.sender == addressesProvider.getVaultConfigurator(),
VaultErrors.V_NOT_CONFIGURATOR
);
}
function _validateRouter() private view {
_validate(
msg.sender == addressesProvider.getRouter(),
VaultErrors.V_NOT_ROUTER
);
}
function _validate(bool _condition, uint256 _errorCode) private pure {
GeneralUtils.validate(_condition, _errorCode);
}
}
contracts/dependencies/openzeppelin/contracts/Address.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
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);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data)
internal
returns (bytes memory)
{
return
functionDelegateCall(
target,
data,
"Address: low-level delegate call failed"
);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(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);
}
}
}
}
contracts/dependencies/openzeppelin/contracts/IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `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
);
}
contracts/dependencies/openzeppelin/contracts/ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
contracts/dependencies/openzeppelin/contracts/SafeERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./IERC20.sol";
import "./SafeMath.sol";
import "./Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(
token,
abi.encodeWithSelector(token.transfer.selector, to, value)
);
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(
token,
abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
);
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// 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(
IERC20 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(
IERC20 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(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(
data,
"SafeERC20: low-level call failed"
);
if (returndata.length > 0) {
// Return data is optional
// solhint-disable-next-line max-line-length
require(
abi.decode(returndata, (bool)),
"SafeERC20: ERC20 operation did not succeed"
);
}
}
}
contracts/dependencies/openzeppelin/contracts/SafeMath.sol
// 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 SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b)
internal
pure
returns (bool, uint256)
{
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;
}
}
contracts/interfaces/IAddressesProvider.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.6;
interface IAddressesProvider {
function getUsdph() external view returns (address);
function getPhlp() external view returns (address);
function getPhlpManager() external view returns (address);
function getVault() external view returns (address);
function getVaultConfigurator() external view returns (address);
function getRouter() external view returns (address);
function getOrderBook() external view returns (address);
function getPositionManager() external view returns (address);
function getPositionRouter() external view returns (address);
function getPriceOracle() external view returns (address);
function getShortsTracker() external view returns (address);
function getTreasury() external view returns (address payable);
function getFeeDistribution() external view returns (address);
}
contracts/interfaces/IPhamePriceOracle.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
interface IPhamePriceOracle {
function backupPriceOracle() external view returns (address);
function adjustmentBps(address _token) external view returns (uint256);
function isAdjustmentAdditive(address _token) external view returns (bool);
function setAdjustment(
address _token,
bool _isAdditive,
uint256 _adjustmentBps
) external;
function setSpreadBps(address _token, uint256 _spreadBps) external;
function setSpreadThresholdBps(uint256 _spreadThresholdBps) external;
function setPriceSampleSpace(uint256 _priceSampleSpace) external;
function setMaxStrictPriceDeviation(
uint256 _maxStrictPriceDeviation
) external;
function getPrice(
address _token,
bool _maximise
) external view returns (uint256);
function setTokenConfig(
address _token,
address _priceFeed,
uint256 _priceDecimals,
bool _isStrictStable
) external;
}
contracts/interfaces/IUSDPH.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
interface IUSDPH {
function mint(address _account, uint256 _amount) external;
function burn(address _account, uint256 _amount) external;
}
contracts/interfaces/IVault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;
import "../protocol/libraries/types/DataTypes.sol";
import "./IAddressesProvider.sol";
interface IVault {
struct Position {
uint256 size;
uint256 collateral;
uint256 averagePrice;
uint256 entryFundingRate;
uint256 reserveAmount;
int256 realisedPnl;
uint256 lastIncreasedTime;
}
event BuyUSDPH(
address account,
address token,
uint256 tokenAmount,
uint256 usdphAmount,
uint256 feeBps
);
event SellUSDPH(
address account,
address token,
uint256 usdphAmount,
uint256 tokenAmount,
uint256 feeBps
);
event Swap(
address account,
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOut,
uint256 amountOutAfterFees,
uint256 feeBps
);
event IncreasePosition(
bytes32 key,
address account,
address collateralToken,
address indexToken,
uint256 collateralDelta,
uint256 sizeDelta,
bool isLong,
uint256 price,
uint256 fee
);
event DecreasePosition(
bytes32 key,
address account,
address collateralToken,
address indexToken,
uint256 collateralDelta,
uint256 sizeDelta,
bool isLong,
uint256 price,
uint256 fee
);
event LiquidatePosition(
bytes32 key,
address account,
address collateralToken,
address indexToken,
bool isLong,
uint256 size,
uint256 collateral,
uint256 reserveAmount,
int256 realisedPnl,
uint256 markPrice
);
event UpdatePosition(
bytes32 key,
uint256 size,
uint256 collateral,
uint256 averagePrice,
uint256 entryFundingRate,
uint256 reserveAmount,
int256 realisedPnl,
uint256 markPrice
);
event ClosePosition(
bytes32 key,
uint256 size,
uint256 collateral,
uint256 averagePrice,
uint256 entryFundingRate,
uint256 reserveAmount,
int256 realisedPnl
);
event UpdateFundingRate(address token, uint256 fundingRate);
event UpdatePnl(bytes32 key, bool hasProfit, uint256 delta);
event CollectSwapFees(address token, uint256 feeUsd, uint256 feeTokens);
event CollectMarginFees(address token, uint256 feeUsd, uint256 feeTokens);
event PayLpFees(address token, uint256 feeTokens);
event DirectPoolDeposit(address token, uint256 amount);
event IncreasePoolAmount(address token, uint256 amount);
event DecreasePoolAmount(address token, uint256 amount);
event IncreaseUsdphAmount(address token, uint256 amount);
event DecreaseUsdphAmount(address token, uint256 amount);
event IncreaseReservedAmount(address token, uint256 amount);
event DecreaseReservedAmount(address token, uint256 amount);
event IncreaseGuaranteedUsd(address token, uint256 amount);
event DecreaseGuaranteedUsd(address token, uint256 amount);
function addressesProvider() external view returns (IAddressesProvider);
function getVaultConfig()
external
view
returns (DataTypes.VaultConfig memory);
function setVaultConfig(
DataTypes.VaultConfig calldata vaultConfig
) external;
function allWhitelistedTokensLength() external view returns (uint256);
function totalTokenWeights() external view returns (uint256);
function allWhitelistedTokens(uint256) external view returns (address);
function getTokenConfig(
address _token
) external view returns (DataTypes.TokenConfig memory);
function setTokenConfig(
address _token,
DataTypes.TokenConfig calldata tokenConfig
) external;
function clearTokenConfig(address _token) external;
function getAccountTokenConfig(
address _account,
address _token
) external view returns (DataTypes.AccountTokenConfig memory);
function setAccountTokenConfig(
address _account,
address _token,
DataTypes.AccountTokenConfig calldata accountTokenConfig
) external;
function getAccountTokenPositionSizeLimit(
address _account,
address _token,
bool _isLong
) external view returns (uint256);
function setUsdphAmount(address _token, uint256 _amount) external;
function tokenBalances(address _token) external view returns (uint256);
function lastFundingTimes(address _token) external view returns (uint256);
function withdrawFees(
address _token,
address _receiver
) external returns (uint256);
function directPoolDeposit(address _token) external;
function buyUSDPH(
address _token,
address _receiver
) external returns (uint256);
function buyUSDPHwithoutFee(address _token) external returns (uint256);
function sellUSDPH(
address _token,
address _receiver
) external returns (uint256);
function swap(
address _tokenIn,
address _tokenOut,
address _receiver
) external returns (uint256);
function increasePosition(
address _account,
address _collateralToken,
address _indexToken,
uint256 _sizeDelta,
bool _isLong
) external;
function decreasePosition(
address _account,
address _collateralToken,
address _indexToken,
uint256 _collateralDelta,
uint256 _sizeDelta,
bool _isLong,
address _receiver
) external returns (uint256);
function validateLiquidation(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
bool _raise
) external view returns (uint256, uint256);
function liquidatePosition(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
address _feeReceiver
) external;
function tokenToUsdMin(
address _token,
uint256 _tokenAmount
) external view returns (uint256);
function cumulativeFundingRates(
address _token
) external view returns (uint256);
function getNextFundingRate(address _token) external view returns (uint256);
function getFeeBps(
address _token,
uint256 _usdphDelta,
uint256 _feeBps,
uint256 _taxBps,
bool _increment
) external view returns (uint256);
function feeReserves(address _token) external view returns (uint256);
function globalShortSizes(address _token) external view returns (uint256);
function guaranteedUsd(address _token) external view returns (uint256);
function poolAmounts(address _token) external view returns (uint256);
function reservedAmounts(address _token) external view returns (uint256);
function usdphAmounts(address _token) external view returns (uint256);
function getRedemptionAmount(
address _token,
uint256 _usdphAmount
) external view returns (uint256);
function getMinProfitTime(
address _indexToken,
uint256 _size,
bool _isLong
) external view returns (uint256);
function getDelta(
address _indexToken,
uint256 _size,
uint256 _averagePrice,
bool _isLong,
uint256 _lastIncreasedTime
) external view returns (bool, uint256);
function getPosition(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong
)
external
view
returns (
uint256,
uint256,
uint256,
uint256,
uint256,
uint256,
bool,
uint256
);
}
contracts/interfaces/IVaultConfigurator.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;
interface IVaultConfigurator {
function setIsLeverageEnabled(bool _isLeverageEnabled) external;
function setStakingBps(uint256 _stakingBps) external;
function setUsdphAmount(address _token, uint256 _amount) external;
function setTokenConfig(
address _token,
uint256 _tokenDecimals,
uint256 _tokenWeight,
uint256 _maxUsdphAmount,
bool _isStable,
bool _isShortable
) external;
function setTokenMinProfit(
address _token,
uint256 _minProfitBps,
uint256 _minProfitTime,
bool _hasMinProfitTimeFloor,
uint256 _minProfitTimeCoefBps
) external;
function setTokenMaxSizes(
address _token,
uint256 maxGlobalLongSize,
uint256 maxGlobalShortSize,
uint256 maxPositionLongSize,
uint256 maxPositionShortSize
) external;
function setAccountTokenConfig(
address _account,
address _token,
bool _isLongBlacklisted,
bool _isShortBlacklisted,
uint256 _maxPositionLongSizeOverride,
uint256 _maxPositionShortSizeOverride
) external;
function getSwapFeeAndTaxBps(
bool isStableSwap
) external view returns (uint256, uint256);
}
contracts/protocol/libraries/helpers/Errors.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.6;
library Errors {
string public constant VC_INVALID_STAKING_BPS = "1"; // VaultConfigurator: invalid stakingBps
string public constant VC_INVALID_MAX_LEVERAGE = "2"; // VaultConfigurator: invalid maxLeverage
string public constant VC_INVALID_TAX_BPS = "3"; // VaultConfigurator: invalid taxBps
string public constant VC_INVALID_STABLE_TAX_BPS = "4"; // VaultConfigurator: invalid stableTaxBps
string public constant VC_INVALID_MINT_BURN_FEE_BPS = "5"; // VaultConfigurator: invalid mintBurnFeeBps
string public constant VC_INVALID_SWAP_FEE_BPS = "6"; // VaultConfigurator: invalid swapFeeBps
string public constant VC_INVALID_STABLE_SWAP_FEE_BPS = "7"; // VaultConfigurator: invalid stableSwapFeeBps
string public constant VC_INVALID_MARGIN_FEE_BPS = "8"; // VaultConfigurator: invalid marginFeeBps
string public constant VC_INVALID_LIQUIDATION_FEE_USD = "9"; // VaultConfigurator: invalid _liquidationFeeUsd
string public constant VC_INVALID_FUNDING_INTERVAL = "10"; // VaultConfigurator: invalid fundingInterval
string public constant VC_INVALID_FUNDING_RATE_FACTOR = "11"; // VaultConfigurator: invalid fundingRateFactor
string public constant VC_TOKEN_NOT_WHITELISTED = "13"; // VaultConfigurator: token not whitelisted
string public constant VC_INVALID_MIN_PROFIT_TIME_COEF_BPS = "16"; // VaultConfigurator: invalid minProfitTimeCoefBps
string public constant VU_INVALID_AVERAGE_PRICE = "38"; // VaultUtil: invalid averagePrice
string public constant VU_INVALID_MSG_SENDER = "41"; // VaultUtil: invalid msg.sender
string public constant TCL_MISMATCHED_TOKENS = "42"; // TokenConfigsLogic: mismatched tokens
string public constant TCL_COLLATERAL_TOKEN_NOT_WHITELISTED = "43"; // TokenConfigsLogic: collateralToken not whitelisted
string public constant TCL_COLLATERAL_TOKEN_MUST_NOT_BE_STABLE = "44"; // TokenConfigsLogic: collateralToken must not be a stableToken
string public constant TCL_COLLATERAL_TOKEN_MUST_BE_STABLE = "46"; // TokenConfigsLogic: collateralToken must be a stableToken
string public constant TCL_INDEX_TOKEN_MUST_NOT_BE_STABLE = "47"; // TokenConfigsLogic: indexToken must not be a stableToken
string public constant TCL_INDEX_TOKEN_NOT_SHORTABLE = "48"; // TokenConfigsLogic: indexToken not shortable
string public constant TCL_TOKEN_NOT_WHITELISTED = "200"; // TokenConfigsLogic: token not whitelisted
string public constant TCL_INVALID_TOKEN_WEIGHT = "201"; // TokenConfigsLogic: invalid token weight
}
contracts/protocol/libraries/helpers/VaultErrors.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.6;
library VaultErrors {
uint256 public constant V_TOKEN_NOT_WHITELISTED = 14; // Vault: token not whitelisted
uint256 public constant V_INVALID_TOKEN_AMOUNT = 15; // Vault: invalid tokenAmount
uint256 public constant V_INVALID_USDPH_AMOUNT = 18; // Vault: invalid usdphAmount
uint256 public constant V_INVALID_REDEMPTION_AMOUNT = 21; // Vault: invalid redemptionAmount
uint256 public constant V_INVALID_AMOUNT_OUT = 22; // Vault: invalid amountOut
uint256 public constant V_SWAPS_NOT_ENABLED = 23; // Vault: swaps not enabled
uint256 public constant V_SAME_TOKEN = 26; // Vault: same token in and out
uint256 public constant V_INVALID_AMOUNT_IN = 27; // Vault: invalid amountIn
uint256 public constant V_LEVERAGE_NOT_ENABLED = 28; // Vault: leverage not enabled
uint256 public constant V_INSUFFICIENT_COLLATERAL_FOR_FEES = 29; // Vault: insufficient collateral for fees
uint256 public constant V_INVALID_POSITION_SIZE = 30; // Vault: invalid position.size
uint256 public constant V_EMPTY_POSITION = 31; // Vault: empty position
uint256 public constant V_POSITION_SIZE_EXCEEDED = 32; // Vault: position size exceeded
uint256 public constant V_POSITION_COLLATERAL_EXCEEDED = 33; // Vault: position collateral exceeded
uint256 public constant V_INVALID_LIQUIDATOR = 34; // Vault: invalid liquidator
uint256 public constant V_POSITION_CANNOT_BE_LIQUIDATED = 36; // Vault: position cannot be liquidated
uint256 public constant V_COLLATERAL_SHOULD_BE_WITHDRAWN = 39; // Vault: collateral should be withdrawn
uint256 public constant V_SIZE_MUST_BE_MORE_THAN_COLLATERAL = 40; // Vault: size must be more than collateral
uint256 public constant V_RESERVE_EXCEEDS_POOL = 50; // Vault: reserve exceeds pool
uint256 public constant V_MAX_USDPH_EXCEEDED = 51; // Vault: max USDPH exceeded
uint256 public constant V_NOT_PHLP_MANAGER = 54; // Vault: caller is not PHLP Manager
uint256 public constant V_POOL_LESS_THAN_BUFFER = 100; // Vault: poolAmount < bufferAmount
uint256 public constant V_MAX_GLOBAL_LONG_EXCEEDED = 104; // Vault: max global long exceeded
uint256 public constant V_MAX_GLOBAL_SHORT_EXCEEDED = 101; // Vault: max global short exceeded
uint256 public constant V_MAX_POSITION_SIZE_EXCEEDED = 105; // Vault: max position long exceeded
uint256 public constant V_NOT_CONFIGURATOR = 102; // Vault: caller is not the vault configurator
uint256 public constant V_NOT_ROUTER = 103; // Vault: caller is not the router
}
contracts/protocol/libraries/logic/TokenConfigsLogic.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;
import "../../../dependencies/openzeppelin/contracts/SafeMath.sol";
import "../helpers/Errors.sol";
import "../types/DataTypes.sol";
library TokenConfigsLogic {
using SafeMath for uint256;
using TokenConfigsLogic for DataTypes.TokenConfigs;
function version() public pure returns (uint256) {
return 1;
}
function setTokenConfig(
DataTypes.TokenConfigs storage tokenConfigs,
address _token,
DataTypes.TokenConfig calldata tokenConfig
) public {
require(tokenConfig.isWhitelisted, Errors.TCL_TOKEN_NOT_WHITELISTED);
require(tokenConfig.weight > 0, Errors.TCL_INVALID_TOKEN_WEIGHT);
// increment token count for the first time
if (!tokenConfigs.tokenConfigs[_token].isWhitelisted) {
tokenConfigs.allWhitelistedTokens.push(_token);
}
uint256 _totalTokenWeights = tokenConfigs.totalTokenWeights.sub(
tokenConfigs.tokenConfigs[_token].weight
);
tokenConfigs.tokenConfigs[_token] = tokenConfig;
tokenConfigs.totalTokenWeights = _totalTokenWeights.add(
tokenConfig.weight
);
}
function clearTokenConfig(
DataTypes.TokenConfigs storage tokenConfigs,
address _token
) public {
require(
tokenConfigs.tokenConfigs[_token].isWhitelisted,
Errors.TCL_TOKEN_NOT_WHITELISTED
);
tokenConfigs.totalTokenWeights = tokenConfigs.totalTokenWeights.sub(
tokenConfigs.tokenConfigs[_token].weight
);
delete tokenConfigs.tokenConfigs[_token];
uint256 lengthMinus1 = tokenConfigs.allWhitelistedTokens.length - 1;
for (uint256 i = 0; i < lengthMinus1; i++) {
// no need to compare last element
// if no match, it's the target to be removed
// otherwise, its value will be saved in target's slot
if (tokenConfigs.allWhitelistedTokens[i] == _token) {
tokenConfigs.allWhitelistedTokens[i] = tokenConfigs
.allWhitelistedTokens[lengthMinus1];
break;
}
}
tokenConfigs.allWhitelistedTokens.pop();
}
function validateTokens(
DataTypes.TokenConfigs storage tokenConfigs,
address _collateralToken,
address _indexToken,
bool _isLong
) public view {
if (_isLong) {
require(
_collateralToken == _indexToken,
Errors.TCL_MISMATCHED_TOKENS
);
require(
tokenConfigs.tokenConfigs[_collateralToken].isWhitelisted,
Errors.TCL_COLLATERAL_TOKEN_NOT_WHITELISTED
);
require(
!tokenConfigs.tokenConfigs[_collateralToken].isStable,
Errors.TCL_COLLATERAL_TOKEN_MUST_NOT_BE_STABLE
);
return;
}
require(
tokenConfigs.tokenConfigs[_collateralToken].isWhitelisted,
Errors.TCL_COLLATERAL_TOKEN_NOT_WHITELISTED
);
require(
tokenConfigs.tokenConfigs[_collateralToken].isStable,
Errors.TCL_COLLATERAL_TOKEN_MUST_BE_STABLE
);
require(
!tokenConfigs.tokenConfigs[_indexToken].isStable,
Errors.TCL_INDEX_TOKEN_MUST_NOT_BE_STABLE
);
require(
tokenConfigs.tokenConfigs[_indexToken].isShortable,
Errors.TCL_INDEX_TOKEN_NOT_SHORTABLE
);
}
}
contracts/protocol/libraries/types/DataTypes.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.6;
library DataTypes {
struct VaultConfig {
bool isSwapEnabled;
bool isLeverageEnabled;
uint256 maxLeverage;
// fees
uint256 taxBps;
uint256 stableTaxBps;
uint256 mintBurnFeeBps;
uint256 swapFeeBps;
uint256 stableSwapFeeBps;
uint256 liquidationFeeUsd;
bool hasDynamicFees;
// staking
uint256 stakingBps;
}
struct TokenConfig {
// should always be true if token exists
bool isWhitelisted;
// basic parameters
uint256 decimals;
uint256 weight; // customisation of index composition
uint256 minProfitBps;
uint256 minProfitTime;
bool hasMinProfitTimeFloor;
uint256 minProfitTimeCoefBps; // coefficient for minProfitTime
uint256 maxUsdphAmount; // a max amount of USDPH debt for a token
bool isStable;
bool isShortable;
// risk parameters
// bufferAmount allows specification of an amount to exclude from swaps
// this can be used to ensure a certain amount of liquidity is available for leverage positions
uint256 bufferAmount;
uint256 maxGlobalLongSize;
uint256 maxGlobalShortSize;
uint256 maxPositionLongSize;
uint256 maxPositionShortSize;
// fees
uint256 longMarginFeeBps;
uint256 shortMarginFeeBps;
// funding rate
uint256 fundingInterval;
uint256 fundingRateFactor;
}
struct TokenConfigs {
uint256 totalTokenWeights;
address[] allWhitelistedTokens;
mapping(address => TokenConfig) tokenConfigs;
}
struct AccountTokenConfig {
bool isLongBlacklisted;
bool isShortBlacklisted;
uint256 maxPositionLongSizeOverride;
uint256 maxPositionShortSizeOverride;
}
}
contracts/protocol/libraries/utils/GeneralUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;
import "../../../dependencies/openzeppelin/contracts/IERC20.sol";
import "../../../dependencies/openzeppelin/contracts/SafeMath.sol";
import "../../../interfaces/IAddressesProvider.sol";
import "../../../interfaces/IVault.sol";
import "../../../interfaces/IVaultConfigurator.sol";
import "../../../interfaces/IPhamePriceOracle.sol";
library GeneralUtils {
function validate(bool condition, uint256 errorCode) public pure {
require(condition, _uint2str(errorCode));
}
function _uint2str(uint256 _i) private pure returns (string memory str) {
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length;
j = _i;
while (j != 0) {
bstr[--k] = bytes1(uint8(48 + (j % 10)));
j /= 10;
}
str = string(bstr);
}
}
contracts/protocol/libraries/utils/VaultUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;
import "../../../dependencies/openzeppelin/contracts/IERC20.sol";
import "../../../dependencies/openzeppelin/contracts/SafeMath.sol";
import "../../../interfaces/IAddressesProvider.sol";
import "../../../interfaces/IVault.sol";
import "../../../interfaces/IVaultConfigurator.sol";
import "../../../interfaces/IPhamePriceOracle.sol";
import "../helpers/Errors.sol";
library VaultUtils {
using SafeMath for uint256;
struct Position {
uint256 size;
uint256 collateral;
uint256 averagePrice;
uint256 entryFundingRate;
uint256 reserveAmount;
int256 realisedPnl;
uint256 lastIncreasedTime;
}
uint256 public constant BASIS_POINTS_DIVISOR = 10000;
uint256 public constant FUNDING_RATE_PRECISION = 1000000;
function validateLiquidation(
IAddressesProvider addressesProvider,
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
bool _raise
) public view returns (uint256, uint256) {
IVault vault = IVault(addressesProvider.getVault());
Position memory position = _getPosition(
vault,
_account,
_collateralToken,
_indexToken,
_isLong
);
(bool hasProfit, uint256 delta) = vault.getDelta(
_indexToken,
position.size,
position.averagePrice,
_isLong,
position.lastIncreasedTime
);
uint256 marginFees = getFundingFee(
vault,
_account,
_collateralToken,
_indexToken,
_isLong,
position.size,
position.entryFundingRate
).add(
getPositionFee(
addressesProvider,
_account,
_collateralToken,
_indexToken,
_isLong,
position.size
)
);
if (!hasProfit && position.collateral < delta) {
if (_raise) {
revert("VaultUtils: losses exceed collateral");
}
return (1, marginFees);
}
uint256 remainingCollateral = position.collateral;
if (!hasProfit) {
remainingCollateral = position.collateral.sub(delta);
}
if (remainingCollateral < marginFees) {
if (_raise) {
revert("VaultUtils: fees exceed collateral");
}
// cap the fees to the remainingCollateral
return (1, remainingCollateral);
}
DataTypes.VaultConfig memory vaultConfig = IVault(
addressesProvider.getVault()
).getVaultConfig();
if (
remainingCollateral < marginFees.add(vaultConfig.liquidationFeeUsd)
) {
if (_raise) {
revert("VaultUtils: liquidation fees exceed collateral");
}
return (1, marginFees);
}
if (
remainingCollateral.mul(vaultConfig.maxLeverage) <
position.size.mul(BASIS_POINTS_DIVISOR)
) {
if (_raise) {
revert("VaultUtils: maxLeverage exceeded");
}
return (2, marginFees);
}
return (0, marginFees);
}
function getNextFundingRate(
IAddressesProvider addressesProvider,
address _token
) public view returns (uint256) {
IVault vault = IVault(addressesProvider.getVault());
DataTypes.TokenConfig memory tokenConfig = vault.getTokenConfig(_token);
uint256 fundingInterval = tokenConfig.fundingInterval;
if (
vault.lastFundingTimes(_token).add(fundingInterval) >
block.timestamp
) {
return 0;
}
uint256 intervals = block
.timestamp
.sub(vault.lastFundingTimes(_token))
.div(fundingInterval);
uint256 poolAmount = vault.poolAmounts(_token);
if (poolAmount == 0) {
return 0;
}
return
tokenConfig
.fundingRateFactor
.mul(vault.reservedAmounts(_token))
.mul(intervals)
.div(poolAmount);
}
function getPositionFee(
IAddressesProvider addressesProvider,
address /* _account */,
address /* _collateralToken */,
address _indexToken,
bool _isLong,
uint256 _sizeDelta
) public view returns (uint256) {
if (_sizeDelta == 0) {
return 0;
}
DataTypes.TokenConfig memory tokenConfig = IVault(
addressesProvider.getVault()
).getTokenConfig(_indexToken);
uint256 afterFeeUsd = _sizeDelta
.mul(
BASIS_POINTS_DIVISOR.sub(
_isLong
? tokenConfig.longMarginFeeBps
: tokenConfig.shortMarginFeeBps
)
)
.div(BASIS_POINTS_DIVISOR);
return _sizeDelta.sub(afterFeeUsd);
}
function getFundingFee(
IVault vault,
address /* _account */,
address _collateralToken,
address /* _indexToken */,
bool /* _isLong */,
uint256 _size,
uint256 _entryFundingRate
) public view returns (uint256) {
if (_size == 0) {
return 0;
}
uint256 fundingRate = vault
.cumulativeFundingRates(_collateralToken)
.sub(_entryFundingRate);
if (fundingRate == 0) {
return 0;
}
return _size.mul(fundingRate).div(FUNDING_RATE_PRECISION);
}
function getBuyUsdphFeeBps(
IAddressesProvider addressesProvider,
address _token,
uint256 _usdphAmount
) public view returns (uint256) {
DataTypes.VaultConfig memory vaultConfig = IVault(
addressesProvider.getVault()
).getVaultConfig();
return
getFeeBps(
addressesProvider,
_token,
_usdphAmount,
vaultConfig.mintBurnFeeBps,
vaultConfig.taxBps,
true
);
}
function getSellUsdphFeeBps(
IAddressesProvider addressesProvider,
address _token,
uint256 _usdphAmount
) public view returns (uint256) {
DataTypes.VaultConfig memory vaultConfig = IVault(
addressesProvider.getVault()
).getVaultConfig();
return
getFeeBps(
addressesProvider,
_token,
_usdphAmount,
vaultConfig.mintBurnFeeBps,
vaultConfig.taxBps,
false
);
}
function getSwapFeeBps(
IAddressesProvider addressesProvider,
address _tokenIn,
address _tokenOut,
uint256 _usdphAmount
) public view returns (uint256) {
IVault vault = IVault(addressesProvider.getVault());
(uint256 baseBps, uint256 taxBps) = IVaultConfigurator(
addressesProvider.getVaultConfigurator()
).getSwapFeeAndTaxBps(
vault.getTokenConfig(_tokenIn).isStable &&
vault.getTokenConfig(_tokenOut).isStable
);
uint256 feesBps0 = getFeeBps(
addressesProvider,
_tokenIn,
_usdphAmount,
baseBps,
taxBps,
true
);
uint256 feesBps1 = getFeeBps(
addressesProvider,
_tokenOut,
_usdphAmount,
baseBps,
taxBps,
false
);
// use the higher of the two fee basis points
return feesBps0 > feesBps1 ? feesBps0 : feesBps1;
}
// cases to consider
// 1. initialAmount is far from targetAmount, action increases balance slightly => high rebate
// 2. initialAmount is far from targetAmount, action increases balance largely => high rebate
// 3. initialAmount is close to targetAmount, action increases balance slightly => low rebate
// 4. initialAmount is far from targetAmount, action reduces balance slightly => high tax
// 5. initialAmount is far from targetAmount, action reduces balance largely => high tax
// 6. initialAmount is close to targetAmount, action reduces balance largely => low tax
// 7. initialAmount is above targetAmount, nextAmount is below targetAmount and vice versa
// 8. a large swap should have similar fees as the same trade split into multiple smaller swaps
function getFeeBps(
IAddressesProvider addressesProvider,
address _token,
uint256 _usdphDelta,
uint256 _feeBps,
uint256 _taxBps,
bool _increment
) public view returns (uint256) {
if (
!IVault(addressesProvider.getVault())
.getVaultConfig()
.hasDynamicFees
) {
return _feeBps;
}
uint256 initialAmount = IVault(addressesProvider.getVault())
.usdphAmounts(_token);
uint256 nextAmount = initialAmount.add(_usdphDelta);
if (!_increment) {
nextAmount = _usdphDelta > initialAmount
? 0
: initialAmount.sub(_usdphDelta);
}
uint256 targetAmount = _getTargetUsdphAmount(addressesProvider, _token);
if (targetAmount == 0) {
return _feeBps;
}
uint256 initialDiff = initialAmount > targetAmount
? initialAmount.sub(targetAmount)
: targetAmount.sub(initialAmount);
uint256 nextDiff = nextAmount > targetAmount
? nextAmount.sub(targetAmount)
: targetAmount.sub(nextAmount);
// action improves relative asset balance
if (nextDiff < initialDiff) {
uint256 rebateBps = _taxBps.mul(initialDiff).div(targetAmount);
return rebateBps > _feeBps ? 0 : _feeBps.sub(rebateBps);
}
uint256 averageDiff = initialDiff.add(nextDiff).div(2);
if (averageDiff > targetAmount) {
averageDiff = targetAmount;
}
uint256 taxBps = _taxBps.mul(averageDiff).div(targetAmount);
return _feeBps.add(taxBps);
}
function _getMinProfitTime(
DataTypes.TokenConfig memory tokenConfig,
uint256 _size,
bool _isLong
) private pure returns (uint256) {
if (_size == 0 || tokenConfig.minProfitTime == 0) {
return 0;
}
uint256 baseSize = _isLong
? tokenConfig.maxPositionLongSize
: tokenConfig.maxPositionShortSize;
if (baseSize == 0) {
return tokenConfig.minProfitTime;
}
if (
(tokenConfig.hasMinProfitTimeFloor && _size < baseSize) ||
(_size == baseSize)
) {
return tokenConfig.minProfitTime;
}
if (_size < baseSize) {
return tokenConfig.minProfitTime.mul(_size).div(baseSize);
} else {
return
tokenConfig
.minProfitTime
.mul(
baseSize.mul(10000).add(
_size.sub(baseSize).mul(
tokenConfig.minProfitTimeCoefBps
)
)
)
.div(baseSize)
.div(10000);
}
}
function getMinProfitTime(
IAddressesProvider addressesProvider,
address _indexToken,
uint256 _size,
bool _isLong
) public view returns (uint256) {
DataTypes.TokenConfig memory tokenConfig = IVault(
addressesProvider.getVault()
).getTokenConfig(_indexToken);
return _getMinProfitTime(tokenConfig, _size, _isLong);
}
function getDelta(
IAddressesProvider addressesProvider,
address _indexToken,
uint256 _size,
uint256 _averagePrice,
bool _isLong,
uint256 _lastIncreasedTime
) public view returns (bool, uint256) {
require(_averagePrice > 0, Errors.VU_INVALID_AVERAGE_PRICE);
uint256 price = IPhamePriceOracle(addressesProvider.getPriceOracle())
.getPrice(_indexToken, !_isLong);
uint256 priceDelta = _averagePrice > price
? _averagePrice.sub(price)
: price.sub(_averagePrice);
uint256 delta = _size.mul(priceDelta).div(_averagePrice);
bool hasProfit;
if (_isLong) {
hasProfit = price > _averagePrice;
} else {
hasProfit = _averagePrice > price;
}
// if the minProfitTime has passed then there will be no min profit threshold
// the min profit threshold helps to prevent front-running issues
DataTypes.TokenConfig memory tokenConfig = IVault(
addressesProvider.getVault()
).getTokenConfig(_indexToken);
uint256 minBps = block.timestamp >
_lastIncreasedTime.add(
_getMinProfitTime(tokenConfig, _size, _isLong)
)
? 0
: tokenConfig.minProfitBps;
if (hasProfit && delta.mul(BASIS_POINTS_DIVISOR) <= _size.mul(minBps)) {
delta = 0;
}
return (hasProfit, delta);
}
function _getPosition(
IVault vault,
address _account,
address _collateralToken,
address _indexToken,
bool _isLong
) private view returns (Position memory) {
Position memory position;
{
(
uint256 size,
uint256 collateral,
uint256 averagePrice,
uint256 entryFundingRate /* reserveAmount */ /* realisedPnl */ /* hasProfit */,
,
,
,
uint256 lastIncreasedTime
) = vault.getPosition(
_account,
_collateralToken,
_indexToken,
_isLong
);
position.size = size;
position.collateral = collateral;
position.averagePrice = averagePrice;
position.entryFundingRate = entryFundingRate;
position.lastIncreasedTime = lastIncreasedTime;
}
return position;
}
function _getTargetUsdphAmount(
IAddressesProvider addressesProvider,
address _token
) private view returns (uint256) {
uint256 supply = IERC20(addressesProvider.getUsdph()).totalSupply();
if (supply == 0) {
return 0;
}
IVault vault = IVault(addressesProvider.getVault());
uint256 weight = vault.getTokenConfig(_token).weight;
return weight.mul(supply).div(vault.totalTokenWeights());
}
}
Compiler Settings
{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"libraries":{"contracts/protocol/libraries/utils/VaultUtils.sol":{"VaultUtils":"0xf525c7630f292ba26a7cf1019ff74d4176fa3eaf"},"contracts/protocol/libraries/utils/GeneralUtils.sol":{"GeneralUtils":"0xa60f20ac5322e698d15aeb169e72f8977d939da2"},"contracts/protocol/libraries/logic/TokenConfigsLogic.sol":{"TokenConfigsLogic":"0x5019828f87706c895c889577cdd272f4a3ac1823"}},"evmVersion":"istanbul"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"addressesProvider_","internalType":"contract IAddressesProvider"}]},{"type":"event","name":"BuyUSDPH","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false},{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"tokenAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"usdphAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"feeBps","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ClosePosition","inputs":[{"type":"bytes32","name":"key","internalType":"bytes32","indexed":false},{"type":"uint256","name":"size","internalType":"uint256","indexed":false},{"type":"uint256","name":"collateral","internalType":"uint256","indexed":false},{"type":"uint256","name":"averagePrice","internalType":"uint256","indexed":false},{"type":"uint256","name":"entryFundingRate","internalType":"uint256","indexed":false},{"type":"uint256","name":"reserveAmount","internalType":"uint256","indexed":false},{"type":"int256","name":"realisedPnl","internalType":"int256","indexed":false}],"anonymous":false},{"type":"event","name":"CollectMarginFees","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"feeUsd","internalType":"uint256","indexed":false},{"type":"uint256","name":"feeTokens","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"CollectSwapFees","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"feeUsd","internalType":"uint256","indexed":false},{"type":"uint256","name":"feeTokens","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DecreaseGuaranteedUsd","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DecreasePoolAmount","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DecreasePosition","inputs":[{"type":"bytes32","name":"key","internalType":"bytes32","indexed":false},{"type":"address","name":"account","internalType":"address","indexed":false},{"type":"address","name":"collateralToken","internalType":"address","indexed":false},{"type":"address","name":"indexToken","internalType":"address","indexed":false},{"type":"uint256","name":"collateralDelta","internalType":"uint256","indexed":false},{"type":"uint256","name":"sizeDelta","internalType":"uint256","indexed":false},{"type":"bool","name":"isLong","internalType":"bool","indexed":false},{"type":"uint256","name":"price","internalType":"uint256","indexed":false},{"type":"uint256","name":"fee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DecreaseReservedAmount","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DecreaseUsdphAmount","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DirectPoolDeposit","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"IncreaseGuaranteedUsd","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"IncreasePoolAmount","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"IncreasePosition","inputs":[{"type":"bytes32","name":"key","internalType":"bytes32","indexed":false},{"type":"address","name":"account","internalType":"address","indexed":false},{"type":"address","name":"collateralToken","internalType":"address","indexed":false},{"type":"address","name":"indexToken","internalType":"address","indexed":false},{"type":"uint256","name":"collateralDelta","internalType":"uint256","indexed":false},{"type":"uint256","name":"sizeDelta","internalType":"uint256","indexed":false},{"type":"bool","name":"isLong","internalType":"bool","indexed":false},{"type":"uint256","name":"price","internalType":"uint256","indexed":false},{"type":"uint256","name":"fee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"IncreaseReservedAmount","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"IncreaseUsdphAmount","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LiquidatePosition","inputs":[{"type":"bytes32","name":"key","internalType":"bytes32","indexed":false},{"type":"address","name":"account","internalType":"address","indexed":false},{"type":"address","name":"collateralToken","internalType":"address","indexed":false},{"type":"address","name":"indexToken","internalType":"address","indexed":false},{"type":"bool","name":"isLong","internalType":"bool","indexed":false},{"type":"uint256","name":"size","internalType":"uint256","indexed":false},{"type":"uint256","name":"collateral","internalType":"uint256","indexed":false},{"type":"uint256","name":"reserveAmount","internalType":"uint256","indexed":false},{"type":"int256","name":"realisedPnl","internalType":"int256","indexed":false},{"type":"uint256","name":"markPrice","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"PayLpFees","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"feeTokens","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SellUSDPH","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false},{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"usdphAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"tokenAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"feeBps","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Swap","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false},{"type":"address","name":"tokenIn","internalType":"address","indexed":false},{"type":"address","name":"tokenOut","internalType":"address","indexed":false},{"type":"uint256","name":"amountIn","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountOut","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountOutAfterFees","internalType":"uint256","indexed":false},{"type":"uint256","name":"feeBps","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdateFundingRate","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"fundingRate","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatePnl","inputs":[{"type":"bytes32","name":"key","internalType":"bytes32","indexed":false},{"type":"bool","name":"hasProfit","internalType":"bool","indexed":false},{"type":"uint256","name":"delta","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatePosition","inputs":[{"type":"bytes32","name":"key","internalType":"bytes32","indexed":false},{"type":"uint256","name":"size","internalType":"uint256","indexed":false},{"type":"uint256","name":"collateral","internalType":"uint256","indexed":false},{"type":"uint256","name":"averagePrice","internalType":"uint256","indexed":false},{"type":"uint256","name":"entryFundingRate","internalType":"uint256","indexed":false},{"type":"uint256","name":"reserveAmount","internalType":"uint256","indexed":false},{"type":"int256","name":"realisedPnl","internalType":"int256","indexed":false},{"type":"uint256","name":"markPrice","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BASIS_POINTS_DIVISOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"FUNDING_RATE_PRECISION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"PRICE_PRECISION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"USDPH_DECIMALS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IAddressesProvider"}],"name":"addressesProvider","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"allWhitelistedTokens","inputs":[{"type":"uint256","name":"i","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allWhitelistedTokensLength","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"buyUSDPH","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"address","name":"_receiver","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"buyUSDPHwithoutFee","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"clearTokenConfig","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"cumulativeFundingRates","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"decreasePosition","inputs":[{"type":"address","name":"_account","internalType":"address"},{"type":"address","name":"_collateralToken","internalType":"address"},{"type":"address","name":"_indexToken","internalType":"address"},{"type":"uint256","name":"_collateralDelta","internalType":"uint256"},{"type":"uint256","name":"_sizeDelta","internalType":"uint256"},{"type":"bool","name":"_isLong","internalType":"bool"},{"type":"address","name":"_receiver","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"directPoolDeposit","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"feeReserves","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct DataTypes.AccountTokenConfig","components":[{"type":"bool","name":"isLongBlacklisted","internalType":"bool"},{"type":"bool","name":"isShortBlacklisted","internalType":"bool"},{"type":"uint256","name":"maxPositionLongSizeOverride","internalType":"uint256"},{"type":"uint256","name":"maxPositionShortSizeOverride","internalType":"uint256"}]}],"name":"getAccountTokenConfig","inputs":[{"type":"address","name":"_account","internalType":"address"},{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAccountTokenPositionSizeLimit","inputs":[{"type":"address","name":"_account","internalType":"address"},{"type":"address","name":"_token","internalType":"address"},{"type":"bool","name":"_isLong","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getDelta","inputs":[{"type":"address","name":"_indexToken","internalType":"address"},{"type":"uint256","name":"_size","internalType":"uint256"},{"type":"uint256","name":"_averagePrice","internalType":"uint256"},{"type":"bool","name":"_isLong","internalType":"bool"},{"type":"uint256","name":"_lastIncreasedTime","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getFeeBps","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"uint256","name":"_usdphDelta","internalType":"uint256"},{"type":"uint256","name":"_feeBps","internalType":"uint256"},{"type":"uint256","name":"_taxBps","internalType":"uint256"},{"type":"bool","name":"_increment","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getFundingFee","inputs":[{"type":"address","name":"_account","internalType":"address"},{"type":"address","name":"_collateralToken","internalType":"address"},{"type":"address","name":"_indexToken","internalType":"address"},{"type":"bool","name":"_isLong","internalType":"bool"},{"type":"uint256","name":"_size","internalType":"uint256"},{"type":"uint256","name":"_entryFundingRate","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getMinProfitTime","inputs":[{"type":"address","name":"_indexToken","internalType":"address"},{"type":"uint256","name":"_size","internalType":"uint256"},{"type":"bool","name":"_isLong","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getNextFundingRate","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"bool","name":"","internalType":"bool"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getPosition","inputs":[{"type":"address","name":"_account","internalType":"address"},{"type":"address","name":"_collateralToken","internalType":"address"},{"type":"address","name":"_indexToken","internalType":"address"},{"type":"bool","name":"_isLong","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRedemptionAmount","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"uint256","name":"_usdphAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct DataTypes.TokenConfig","components":[{"type":"bool","name":"isWhitelisted","internalType":"bool"},{"type":"uint256","name":"decimals","internalType":"uint256"},{"type":"uint256","name":"weight","internalType":"uint256"},{"type":"uint256","name":"minProfitBps","internalType":"uint256"},{"type":"uint256","name":"minProfitTime","internalType":"uint256"},{"type":"bool","name":"hasMinProfitTimeFloor","internalType":"bool"},{"type":"uint256","name":"minProfitTimeCoefBps","internalType":"uint256"},{"type":"uint256","name":"maxUsdphAmount","internalType":"uint256"},{"type":"bool","name":"isStable","internalType":"bool"},{"type":"bool","name":"isShortable","internalType":"bool"},{"type":"uint256","name":"bufferAmount","internalType":"uint256"},{"type":"uint256","name":"maxGlobalLongSize","internalType":"uint256"},{"type":"uint256","name":"maxGlobalShortSize","internalType":"uint256"},{"type":"uint256","name":"maxPositionLongSize","internalType":"uint256"},{"type":"uint256","name":"maxPositionShortSize","internalType":"uint256"},{"type":"uint256","name":"longMarginFeeBps","internalType":"uint256"},{"type":"uint256","name":"shortMarginFeeBps","internalType":"uint256"},{"type":"uint256","name":"fundingInterval","internalType":"uint256"},{"type":"uint256","name":"fundingRateFactor","internalType":"uint256"}]}],"name":"getTokenConfig","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct DataTypes.VaultConfig","components":[{"type":"bool","name":"isSwapEnabled","internalType":"bool"},{"type":"bool","name":"isLeverageEnabled","internalType":"bool"},{"type":"uint256","name":"maxLeverage","internalType":"uint256"},{"type":"uint256","name":"taxBps","internalType":"uint256"},{"type":"uint256","name":"stableTaxBps","internalType":"uint256"},{"type":"uint256","name":"mintBurnFeeBps","internalType":"uint256"},{"type":"uint256","name":"swapFeeBps","internalType":"uint256"},{"type":"uint256","name":"stableSwapFeeBps","internalType":"uint256"},{"type":"uint256","name":"liquidationFeeUsd","internalType":"uint256"},{"type":"bool","name":"hasDynamicFees","internalType":"bool"},{"type":"uint256","name":"stakingBps","internalType":"uint256"}]}],"name":"getVaultConfig","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"globalShortSizes","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"guaranteedUsd","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"increasePosition","inputs":[{"type":"address","name":"_account","internalType":"address"},{"type":"address","name":"_collateralToken","internalType":"address"},{"type":"address","name":"_indexToken","internalType":"address"},{"type":"uint256","name":"_sizeDelta","internalType":"uint256"},{"type":"bool","name":"_isLong","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastFundingTimes","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"liquidatePosition","inputs":[{"type":"address","name":"_account","internalType":"address"},{"type":"address","name":"_collateralToken","internalType":"address"},{"type":"address","name":"_indexToken","internalType":"address"},{"type":"bool","name":"_isLong","internalType":"bool"},{"type":"address","name":"_feeReceiver","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"poolAmounts","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"size","internalType":"uint256"},{"type":"uint256","name":"collateral","internalType":"uint256"},{"type":"uint256","name":"averagePrice","internalType":"uint256"},{"type":"uint256","name":"entryFundingRate","internalType":"uint256"},{"type":"uint256","name":"reserveAmount","internalType":"uint256"},{"type":"int256","name":"realisedPnl","internalType":"int256"},{"type":"uint256","name":"lastIncreasedTime","internalType":"uint256"}],"name":"positions","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"reservedAmounts","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"sellUSDPH","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"address","name":"_receiver","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAccountTokenConfig","inputs":[{"type":"address","name":"_account","internalType":"address"},{"type":"address","name":"_token","internalType":"address"},{"type":"tuple","name":"accountTokenConfig","internalType":"struct DataTypes.AccountTokenConfig","components":[{"type":"bool","name":"isLongBlacklisted","internalType":"bool"},{"type":"bool","name":"isShortBlacklisted","internalType":"bool"},{"type":"uint256","name":"maxPositionLongSizeOverride","internalType":"uint256"},{"type":"uint256","name":"maxPositionShortSizeOverride","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTokenConfig","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"tuple","name":"tokenConfig","internalType":"struct DataTypes.TokenConfig","components":[{"type":"bool","name":"isWhitelisted","internalType":"bool"},{"type":"uint256","name":"decimals","internalType":"uint256"},{"type":"uint256","name":"weight","internalType":"uint256"},{"type":"uint256","name":"minProfitBps","internalType":"uint256"},{"type":"uint256","name":"minProfitTime","internalType":"uint256"},{"type":"bool","name":"hasMinProfitTimeFloor","internalType":"bool"},{"type":"uint256","name":"minProfitTimeCoefBps","internalType":"uint256"},{"type":"uint256","name":"maxUsdphAmount","internalType":"uint256"},{"type":"bool","name":"isStable","internalType":"bool"},{"type":"bool","name":"isShortable","internalType":"bool"},{"type":"uint256","name":"bufferAmount","internalType":"uint256"},{"type":"uint256","name":"maxGlobalLongSize","internalType":"uint256"},{"type":"uint256","name":"maxGlobalShortSize","internalType":"uint256"},{"type":"uint256","name":"maxPositionLongSize","internalType":"uint256"},{"type":"uint256","name":"maxPositionShortSize","internalType":"uint256"},{"type":"uint256","name":"longMarginFeeBps","internalType":"uint256"},{"type":"uint256","name":"shortMarginFeeBps","internalType":"uint256"},{"type":"uint256","name":"fundingInterval","internalType":"uint256"},{"type":"uint256","name":"fundingRateFactor","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setUsdphAmount","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setVaultConfig","inputs":[{"type":"tuple","name":"vaultConfig","internalType":"struct DataTypes.VaultConfig","components":[{"type":"bool","name":"isSwapEnabled","internalType":"bool"},{"type":"bool","name":"isLeverageEnabled","internalType":"bool"},{"type":"uint256","name":"maxLeverage","internalType":"uint256"},{"type":"uint256","name":"taxBps","internalType":"uint256"},{"type":"uint256","name":"stableTaxBps","internalType":"uint256"},{"type":"uint256","name":"mintBurnFeeBps","internalType":"uint256"},{"type":"uint256","name":"swapFeeBps","internalType":"uint256"},{"type":"uint256","name":"stableSwapFeeBps","internalType":"uint256"},{"type":"uint256","name":"liquidationFeeUsd","internalType":"uint256"},{"type":"bool","name":"hasDynamicFees","internalType":"bool"},{"type":"uint256","name":"stakingBps","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"swap","inputs":[{"type":"address","name":"_tokenIn","internalType":"address"},{"type":"address","name":"_tokenOut","internalType":"address"},{"type":"address","name":"_receiver","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tokenBalances","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tokenToUsdMin","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"uint256","name":"_tokenAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalTokenWeights","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateCumulativeFundingRate","inputs":[{"type":"address","name":"_collateralToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"usdphAmounts","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"validateLiquidation","inputs":[{"type":"address","name":"_account","internalType":"address"},{"type":"address","name":"_collateralToken","internalType":"address"},{"type":"address","name":"_indexToken","internalType":"address"},{"type":"bool","name":"_isLong","internalType":"bool"},{"type":"bool","name":"_raise","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"withdrawFees","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"address","name":"_receiver","internalType":"address"}]}]
Contract Creation Code
0x60a06040523480156200001157600080fd5b50604051620060413803806200604183398101604081905262000034916200004f565b600160005560601b6001600160601b0319166080526200007f565b60006020828403121562000061578081fd5b81516001600160a01b038116811462000078578182fd5b9392505050565b60805160601c615f136200012e600039806106f152806109f95280610ab25280610dd55280610f0852806110d45280611616528061182252806119445280611a665280611b1f5280611c995280611fab52806121b4528061238f528061244352806126325280612769528061281752806129635280612b0d5280612d5a5280613262528061360652806137aa52806139ff5280613ba55280613d165280614482528061465b5250615f136000f3fe608060405234801561001057600080fd5b506004361061028a5760003560e01c8063843d43991161015c578063c72c4d10116100ce578063de2ea94811610087578063de2ea948146105be578063e468baf0146105d1578063e4d661b4146105e4578063e67f59a7146105f7578063f07456ce1461060a578063f25552781461061d5761028a565b8063c72c4d101461053a578063cb67e3b11461054f578063d54d5a9f1461056f578063d8f897c314610590578063da76524c146105a3578063dc8f5fac146105b65761028a565b8063a65632e211610120578063a65632e2146104c6578063a93acac2146104d9578063b2a2353e146104ec578063b9b3fd2a14610501578063c3c7b9e914610514578063c65bc7b1146105275761028a565b8063843d43991461046557806388a2f635146104785780638a78daa81461049857806393316212146104ab57806395082d25146104be5761028a565b806342e85182116102005780635bae4263116101b95780635bae4263146103f05780635c07eaab146104035780635da69ed7146104245780635f7bc119146104375780636be6026b1461044a57806382a08490146104525761028a565b806342e851821461035757806348d91abf1461036a5780634a3f088d1461037d578063514ea4bf146103a4578063523fba7f146103ca57806352f55eed146103dd5761028a565b8063126082cf11610252578063126082cf146102f057806313f1e736146102f85780631ce9cb8f1461030b57806320355a2c1461031e5780632abfa9e4146103315780632c668ec1146103445761028a565b806303563e671461028f5780630754d4fb146102ad5780630842b076146102c257806308507fff146102ca5780630a48d5a9146102dd575b600080fd5b610297610630565b6040516102a49190615ce8565b60405180910390f35b6102c06102bb366004615424565b610635565b005b610297610690565b6102c06102d8366004615562565b610696565b6102976102eb366004615424565b6106ab565b610297610814565b6102c0610306366004615023565b61081a565b610297610319366004615023565b610967565b61029761032c366004615023565b610979565b6102c061033f3660046153e3565b610d58565b610297610352366004615424565b610dd0565b610297610365366004615023565b610fa6565b6102c0610378366004615277565b610fb8565b61039061038b3660046150dd565b61144b565b6040516102a4989796959493929190615cff565b6103b76103b236600461554a565b611535565b6040516102a4979695949392919061579c565b6102976103d8366004615023565b611572565b6102976103eb366004615023565b611584565b6102976103fe36600461505b565b611596565b610416610411366004615485565b611925565b6040516102a49291906156d4565b61029761043236600461505b565b6119e6565b6102c0610445366004615023565b611e25565b610297611efd565b6102976104603660046152d0565b611f04565b6102976104733660046154d6565b611f79565b61048b61048636600461505b565b612037565b6040516102a491906159b9565b6102976104a6366004615023565b6120a5565b6102976104b9366004615093565b6120b7565b6102976125b2565b6102c06104d4366004615392565b6125c2565b6102976104e7366004615023565b612602565b6104f46126b4565b6040516102a49190615c55565b61029761050f36600461544f565b612737565b610297610522366004615023565b6127f1565b610297610535366004615023565b612803565b610542612815565b6040516102a491906155f9565b61056261055d366004615023565b612839565b6040516102a491906159e8565b61058261057d3660046151a8565b612944565b6040516102a4929190615cf1565b61029761059e366004615023565b6129f7565b6102976105b136600461520a565b612a09565b610297612aaa565b6102c06105cc366004615138565b612ab0565b6105426105df36600461554a565b612f9b565b6102976105f2366004615353565b612fc8565b6102c0610605366004615023565b613187565b610297610618366004615023565b6131f5565b61029761062b36600461505b565b613207565b601281565b61063d61325d565b6001600160a01b03821660009081526010602052604090205480821115610677576106718361066c848461330e565b61336b565b5061068c565b61068a83610685838561330e565b613414565b505b5050565b600c5490565b61069e61325d565b80600161068a8282615da0565b6000816106ba5750600061080e565b61080b600b6002016000856001600160a01b03166001600160a01b0316815260200190815260200160002060010154600a0a6108057f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b15801561074857600080fd5b505afa15801561075c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610780919061503f565b6001600160a01b03166376d697608760006040518363ffffffff1660e01b81526004016107ae929190615698565b60206040518083038186803b1580156107c657600080fd5b505afa1580156107da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fe919061557a565b85906134e7565b90613540565b90505b92915050565b61271081565b6001600160a01b0381166000908152600d60209081526040808320601001546015909252909120546108795761085a816108544282613540565b906134e7565b6001600160a01b03831660009081526015602052604090205550610964565b6001600160a01b038216600090815260156020526040902054429061089e90836135a7565b11156108aa5750610964565b60006108b583612602565b6001600160a01b0384166000908152601460205260409020549091506108db90826135a7565b6001600160a01b038416600090815260146020526040902055610902826108544282613540565b6001600160a01b038416600090815260156020908152604080832093909355601490528190205490517fa146fc154e1913322e9817d49f0d5c37466c24326e15de10e739a948be815eab916109599186919061563e565b60405180910390a150505b50565b60176020526000908152604090205481565b6000600260005414156109c1576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b60026000556109ce613601565b6001600160a01b0382166000908152600d60205260409020546109f59060ff16600e6136ac565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b158015610a5057600080fd5b505afa158015610a64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a88919061503f565b90506000610a95846136e5565b9050610aa560008211600f6136ac565b610aae8461081a565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015610b0957600080fd5b505afa158015610b1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b41919061503f565b6001600160a01b03166376d697608660006040518363ffffffff1660e01b8152600401610b6f929190615698565b60206040518083038186803b158015610b8757600080fd5b505afa158015610b9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bbf919061557a565b90506000610bdd68327cb2734119d3b7a9601e1b61080585856134e7565b9050610bea8187866137a5565b9050610bfa6000821160126136ac565b6000610c1668327cb2734119d3b7a9601e1b61080586866134e7565b9050610c238188876137a5565b6001600160a01b038816600090815260106020526040902054909150610c4990826135a7565b6001600160a01b0388166000908152601060205260409081902091909155517ffbf914421322be849bbd116e701fe46b8e6b903e15192c1d479b8748a738452090610c97908990849061563e565b60405180910390a1610ca987856138da565b6040516340c10f1960e01b81526001600160a01b038616906340c10f1990610cd7903390859060040161563e565b600060405180830381600087803b158015610cf157600080fd5b505af1158015610d05573d6000803e3d6000fd5b505050507fc6c8b444f53d4e65122f6f9780fd7db1ff1a47d82ff0cbb2f450eee0c289e534338886846000604051610d4195949392919061560d565b60405180910390a160016000559695505050505050565b610d6061325d565b6040516326286a1760e21b8152735019828f87706c895c889577cdd272f4a3ac1823906398a1a85c90610d9c90600b9086908690600401615b1d565b60006040518083038186803b158015610db457600080fd5b505af4158015610dc8573d6000803e3d6000fd5b505050505050565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015610e2c57600080fd5b505afa158015610e40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e64919061503f565b6001600160a01b03166376d697608560016040518363ffffffff1660e01b8152600401610e92929190615698565b60206040518083038186803b158015610eaa57600080fd5b505afa158015610ebe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ee2919061557a565b90506000610f00826108058668327cb2734119d3b7a9601e1b6134e7565b9050610f9d817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f5f57600080fd5b505afa158015610f73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f97919061503f565b876137a5565b95945050505050565b60106020526000908152604090205481565b60026000541415610ffe576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b600260005561100b6139fa565b604051634755223960e01b8152735019828f87706c895c889577cdd272f4a3ac18239063475522399061104990600b90889088908790600401615af6565b60006040518083038186803b15801561106157600080fd5b505af4158015611075573d6000803e3d6000fd5b50506001546110909250610100900460ff169050601c6136ac565b6110998461081a565b60006110a786868685613aa9565b60008181526016602090815260408083208151631f94a27560e31b81529151949550936001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263fca513a89260048082019391829003018186803b15801561111657600080fd5b505afa15801561112a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114e919061503f565b6001600160a01b03166376d6976087866040518363ffffffff1660e01b815260040161117b929190615698565b60206040518083038186803b15801561119357600080fd5b505afa1580156111a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111cb919061557a565b905084156111f6576111f66111e1898887612fc8565b83546111ed90886135a7565b111560696136ac565b815461120457600282018190555b8154158015906112145750600085115b1561123a57611234868360000154846002015487858a8860060154613ae2565b60028301555b6000806112548a8a8a898b89600001548a60030154613b73565b9150915060006112638a6136e5565b905060006112718b836106ab565b600187015490915061128390826135a7565b6001870181905561129890851115601d6136ac565b60018601546112a7908561330e565b60018701556001600160a01b038b16600090815260146020526040902054600387015585546112d6908a6135a7565b8087554260068801556112ec901515601e6136ac565b6112fe86600001548760010154613cde565b61130c8c8c8c8b6001612944565b5050600061131c8c8b6001613d02565b600488015490915061132e90826135a7565b600488015561133d8c82613e4d565b881561137f5761135b8c6113518c886135a7565b8460008e11613ee2565b6113658c846138da565b61137a8c6113758e886000613d02565b61400c565b611389565b6113898b8b614093565b7f2fe68525253654c21998f35787a8d0f361905ef647c854092430ab65f2f15022888e8e8e868f8f8d8d6040516113c899989796959493929190615738565b60405180910390a17f20853733b590dce729d9f4628682ebd9a34d2354e72679e66f43a008fc03b77388886000015489600101548a600201548b600301548c600401548d600501548d6040516114259897969594939291906157cc565b60405180910390a16114378c85614115565b505060016000555050505050505050505050565b6000806000806000806000806000601660006114698f8f8f8f613aa9565b81526020019081526020016000206040518060e001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820154815260200160068201548152505090506000808260a00151136114e4578160a001516000036114ea565b8160a001515b9050816000015182602001518360400151846060015185608001518560008860a0015112158860c0015199509950995099509950995099509950505094995094995094999196509450565b6016602052600090815260409020805460018201546002830154600384015460048501546005860154600690960154949593949293919290919087565b600f6020526000908152604090205481565b60116020526000908152604090205481565b6000600260005414156115de576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b60026000556115eb613601565b6001600160a01b0383166000908152600d60205260409020546116129060ff16600e6136ac565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561166d57600080fd5b505afa158015611681573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a5919061503f565b905060006116b2826136e5565b90506116c26000821160126136ac565b6116cb8561081a565b60006116d78683610dd0565b90506116e76000821160156136ac565b6116f18683613414565b6116fb868261400c565b604051632770a7eb60e21b81526001600160a01b03841690639dc29fac90611729903090869060040161563e565b600060405180830381600087803b15801561174357600080fd5b505af1158015611757573d6000803e3d6000fd5b50506040516370a0823160e01b81526001600160a01b03861692506370a0823191506117879030906004016155f9565b60206040518083038186803b15801561179f57600080fd5b505afa1580156117b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d7919061557a565b6001600160a01b0384166000908152600f602052604080822092909255905163562b7e0760e11b815273f525c7630f292ba26a7cf1019ff74d4176fa3eaf9063ac56fc0e9061184e907f0000000000000000000000000000000000000000000000000000000000000000908b9088906004016158ba565b60206040518083038186803b15801561186657600080fd5b505af415801561187a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189e919061557a565b90506000806118ae8985856141e2565b915091506118c06000831160166136ac565b6118cb89838a61429d565b7ff0769e076d0b71b7205e1d6800e3ede98265f5a21bacbd6067947cbe7a3fa671888a87858760405161190295949392919061560d565b60405180910390a16119148982614115565b506001600055979650505050505050565b60008073f525c7630f292ba26a7cf1019ff74d4176fa3eaf63cfb90b207f000000000000000000000000000000000000000000000000000000000000000089898989896040518763ffffffff1660e01b815260040161198996959493929190615907565b604080518083038186803b1580156119a057600080fd5b505af41580156119b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d8919061551d565b915091509550959350505050565b600060026000541415611a2e576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b6002600055611a3b613601565b6001600160a01b0383166000908152600d6020526040902054611a629060ff16600e6136ac565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b158015611abd57600080fd5b505afa158015611ad1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af5919061503f565b90506000611b02856136e5565b9050611b1260008211600f6136ac565b611b1b8561081a565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015611b7657600080fd5b505afa158015611b8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bae919061503f565b6001600160a01b03166376d697608760006040518363ffffffff1660e01b8152600401611bdc929190615698565b60206040518083038186803b158015611bf457600080fd5b505afa158015611c08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c2c919061557a565b90506000611c4a68327cb2734119d3b7a9601e1b61080585856134e7565b9050611c578188866137a5565b9050611c676000821160126136ac565b6040516360d41f1560e11b815260009073f525c7630f292ba26a7cf1019ff74d4176fa3eaf9063c1a83e2a90611cc5907f0000000000000000000000000000000000000000000000000000000000000000908c9087906004016158ba565b60206040518083038186803b158015611cdd57600080fd5b505af4158015611cf1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d15919061557a565b9050600080611d258a87856141e2565b90925090506000611d4668327cb2734119d3b7a9601e1b61080585896134e7565b9050611d53818c8a6137a5565b9050611d5f8b8261336b565b611d698b846138da565b6040516340c10f1960e01b81526001600160a01b038916906340c10f1990611d97908d90859060040161563e565b600060405180830381600087803b158015611db157600080fd5b505af1158015611dc5573d6000803e3d6000fd5b505050507fc6c8b444f53d4e65122f6f9780fd7db1ff1a47d82ff0cbb2f450eee0c289e5348a8c898488604051611e0095949392919061560d565b60405180910390a1611e128b83614115565b60016000559a9950505050505050505050565b60026000541415611e6b576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b600260009081556001600160a01b0382168152600d6020526040902054611e969060ff16600e6136ac565b6000611ea1826136e5565b9050611eb160008211600f6136ac565b611ebb82826138da565b7fa5a389190ebf6170a133bda5c769b77f4d6715b8aa172ec0ddf8473d0b4944bd8282604051611eec92919061563e565b60405180910390a150506001600055565b620f424081565b600060026000541415611f4c576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b6002600055611f596139fa565b611f688888888888888861434e565b600160005598975050505050505050565b6040516308e83b0160e21b815260009073f525c7630f292ba26a7cf1019ff74d4176fa3eaf906323a0ec0490611fdd907f0000000000000000000000000000000000000000000000000000000000000000908a908a908a908a908a9060040161593f565b60206040518083038186803b158015611ff557600080fd5b505af4158015612009573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202d919061557a565b9695505050505050565b61203f614ef2565b506001600160a01b039182166000908152600e602090815260408083209390941682529182528290208251608081018452815460ff8082161515835261010090910416151592810192909252600181015492820192909252600290910154606082015290565b60186020526000908152604090205481565b6000600260005414156120ff576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b60026000556001546121159060ff1660176136ac565b6001600160a01b0384166000908152600d602052604090205461213c9060ff16600e6136ac565b6001600160a01b0383166000908152600d60205260409020546121639060ff16600e6136ac565b612183836001600160a01b0316856001600160a01b03161415601a6136ac565b61218c8461081a565b6121958361081a565b60006121a0856136e5565b90506121b060008211601b6136ac565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b15801561220b57600080fd5b505afa15801561221f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612243919061503f565b90506000816001600160a01b03166376d697608860006040518363ffffffff1660e01b8152600401612276929190615698565b60206040518083038186803b15801561228e57600080fd5b505afa1580156122a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c6919061557a565b90506000826001600160a01b03166376d697608860016040518363ffffffff1660e01b81526004016122f9929190615698565b60206040518083038186803b15801561231157600080fd5b505afa158015612325573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612349919061557a565b9050600061235b8261080587866134e7565b9050612368818a8a6137a5565b9050600061238668327cb2734119d3b7a9601e1b61080588876134e7565b9050612423818b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156123e657600080fd5b505afa1580156123fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061241e919061503f565b6137a5565b9050600073f525c7630f292ba26a7cf1019ff74d4176fa3eaf6387e35c2c7f00000000000000000000000000000000000000000000000000000000000000008d8d866040518563ffffffff1660e01b81526004016124849493929190615890565b60206040518083038186803b15801561249c57600080fd5b505af41580156124b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124d4919061557a565b90506000806124e48c86856141e2565b915091506124f28d8561336b565b6124fc8c85613414565b6125068d8a6138da565b6125108c8661400c565b6001600160a01b038c166000908152600d602090815260408083206009015460119092529091205461254591111560646136ac565b6125508c838d61429d565b7f0874b2d545cb271cdbda4e093020c452328b24af12382ed62c4d00f5c26709db8b8e8e8c89878960405161258b9796959493929190615657565b60405180910390a161259d8c82614115565b5060016000559b9a5050505050505050505050565b68327cb2734119d3b7a9601e1b81565b6125ca61325d565b6001600160a01b038084166000908152600e6020908152604080832093861683529290522081906125fb8282615d59565b5050505050565b6040516246b9f360e11b815260009073f525c7630f292ba26a7cf1019ff74d4176fa3eaf90628d73e69061265c907f0000000000000000000000000000000000000000000000000000000000000000908690600401615802565b60206040518083038186803b15801561267457600080fd5b505af4158015612688573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ac919061557a565b90505b919050565b6126bc614f1e565b50604080516101608101825260015460ff8082161515835261010091829004811615156020840152600254938301939093526003546060830152600454608083015260055460a083015260065460c083015260075460e0830152600854908201526009549091161515610120820152600a5461014082015290565b604051634732e96360e11b815260009073f525c7630f292ba26a7cf1019ff74d4176fa3eaf90638e65d2c690612797907f0000000000000000000000000000000000000000000000000000000000000000908890889088906004016158de565b60206040518083038186803b1580156127af57600080fd5b505af41580156127c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127e7919061557a565b90505b9392505050565b60126020526000908152604090205481565b60146020526000908152604090205481565b7f000000000000000000000000000000000000000000000000000000000000000081565b612841614f7e565b506001600160a01b03166000908152600d6020818152604092839020835161026081018552815460ff90811615158252600183015493820193909352600282015494810194909452600381015460608501526004810154608085015260058101548216151560a0850152600681015460c0850152600781015460e08501526008810154808316151561010080870191909152900490911615156101208401526009810154610140840152600a810154610160840152600b810154610180840152600c8101546101a0840152908101546101c0830152600e8101546101e0830152600f81015461020083015260108101546102208301526011015461024082015290565b60008073f525c7630f292ba26a7cf1019ff74d4176fa3eaf634e9c91757f000000000000000000000000000000000000000000000000000000000000000089898989896040518763ffffffff1660e01b81526004016129a89695949392919061581c565b604080518083038186803b1580156129bf57600080fd5b505af41580156129d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d89190615592565b60156020526000908152604090205481565b604051636df5632b60e11b815260009073f525c7630f292ba26a7cf1019ff74d4176fa3eaf9063dbeac65690612a4f9030908b908b908b908b908b908b90600401615975565b60206040518083038186803b158015612a6757600080fd5b505af4158015612a7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a9f919061557a565b979650505050505050565b600b5490565b60026000541415612af6576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b6002600081905550612bad336001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636a22dede6040518163ffffffff1660e01b815260040160206040518083038186803b158015612b6457600080fd5b505afa158015612b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9c919061503f565b6001600160a01b03161460226136ac565b612bb68461081a565b6000612bc486868686613aa9565b600081815260166020908152604091829020825160e08101845281548082526001830154938201939093526002820154938101939093526003810154606084015260048101546080840152600581015460a08401526006015460c083015291925090612c33901515601f6136ac565b600080612c44898989896000612944565b91509150612c57826000141560246136ac565b8160021415612c7e57612c74898989600087600001518b8f61434e565b5050505050612f8f565b6000612c8c89836000613d02565b6001600160a01b038a16600090815260176020526040902054909150612cb290826135a7565b6001600160a01b038a166000908152601760205260409081902091909155517f5d0c0019d3d45fadeb74eff9d2c9924d146d000ac6bcf3c28bf0ac3c9baa011a90612d02908b90859085906156b3565b60405180910390a1612d188985608001516148b5565b8615612d5657612d46896000612d3f8760200151886000015161330e90919063ffffffff16565b6000613ee2565b612d56896113758b856000613d02565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015612db157600080fd5b505afa158015612dc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612de9919061503f565b6001600160a01b03166376d697608a8a156040518363ffffffff1660e01b8152600401612e17929190615698565b60206040518083038186803b158015612e2f57600080fd5b505afa158015612e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e67919061557a565b90507f2e1f85a64a2f22cf2f0c42584e7c919ed4abe8d53675cff0f62bf1e95a1c676f868c8c8c8c8a600001518b602001518c608001518d60a001518a604051612eba9a999897969594939291906156e4565b60405180910390a187158015612ed35750846020015183105b15612f03576020850151600090612eea908561330e565b9050612f018b612efc8d846000613d02565b6138da565b505b87612f1657612f1689866000015161495e565b6000868152601660205260408120818155600181018290556002810182905560038101829055600481018290556005810182905560060181905560085490612f67908c906113759082908590613d02565b612f7d8b612f778d846000613d02565b8a61429d565b612f878b84614115565b505050505050505b50506001600055505050565b6000600b6001018281548110612fad57fe5b6000918252602090912001546001600160a01b031692915050565b6001600160a01b038084166000908152600e60208181526040808420948716808552948252808420815160808082018452825460ff80821615158452610100918290048116151584880152600180860154858801526002958601546060808701919091529a8a52600d808952878b20885161026081018a5281548516151581529281015499830199909952958801549681019690965260038701549986019990995260048601549185019190915260058501548816151560a0850152600685015460c0850152600785015460e085015260088501548089161515858301520490961615156101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a08301528201546101c0820152918101546101e0830152600f810154610200830152601081015461022083015260110154610240820152909190831561314f5781511561312c576000925050506127ea565b6040820151613143576101a0015191506127ea9050565b506040015190506127ea565b816020015115613164576000925050506127ea565b606082015161317b576101c0015191506127ea9050565b506060015190506127ea565b61318f61325d565b604051632e43d10160e01b8152735019828f87706c895c889577cdd272f4a3ac182390632e43d101906131c990600b908590600401615adf565b60006040518083038186803b1580156131e157600080fd5b505af41580156125fb573d6000803e3d6000fd5b60136020526000908152604090205481565b6000613211613601565b6001600160a01b0383166000908152601760205260409020548061323957600091505061080e565b6001600160a01b03841660009081526017602052604081205561080b84828561429d565b61330c7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d759932b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156132b957600080fd5b505afa1580156132cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f1919061503f565b6001600160a01b0316336001600160a01b03161460666136ac565b565b600082821115613365576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6001600160a01b03821660009081526010602052604090205461338e90826135a7565b6001600160a01b038316600090815260106020908152604080832093909355600d9052206007015480156133e3576001600160a01b0383166000908152601060205260409020546133e39082101560336136ac565b7ffbf914421322be849bbd116e701fe46b8e6b903e15192c1d479b8748a7384520838360405161095992919061563e565b6001600160a01b03821660009081526010602052604090205481811161348f576001600160a01b03831660009081526010602052604080822091909155517fda43d5cdf64be33afb6f3e9858581d78f3855a1f6fcb50e5eb86c0c5db312d1c90613481908590849061563e565b60405180910390a15061068c565b613499818361330e565b6001600160a01b0384166000908152601060205260409081902091909155517fda43d5cdf64be33afb6f3e9858581d78f3855a1f6fcb50e5eb86c0c5db312d1c90610959908590859061563e565b6000826134f65750600061080e565b8282028284828161350357fe5b041461080b5760405162461bcd60e51b8152600401808060200182810382526021815260200180615e936021913960400191505060405180910390fd5b6000808211613596576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161359f57fe5b049392505050565b60008282018381101561080b576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b61330c7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663028d27d86040518163ffffffff1660e01b815260040160206040518083038186803b15801561365d57600080fd5b505afa158015613671573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613695919061503f565b6001600160a01b0316336001600160a01b03161460365b60405163cda9d28760e01b815273a60f20ac5322e698d15aeb169e72f8977d939da29063cda9d28790610d9c90859085906004016156d4565b6001600160a01b0381166000818152600f60205260408082205490516370a0823160e01b8152919290918391906370a08231906137269030906004016155f9565b60206040518083038186803b15801561373e57600080fd5b505afa158015613752573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613776919061557a565b6001600160a01b0385166000908152600f60205260409020819055905061379d818361330e565b949350505050565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561380157600080fd5b505afa158015613815573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613839919061503f565b90506000816001600160a01b0316856001600160a01b031614613877576001600160a01b0385166000908152600d602052604090206001015461387a565b60125b90506000826001600160a01b0316856001600160a01b0316146138b8576001600160a01b0385166000908152600d60205260409020600101546138bb565b60125b9050612a9f82600a0a61080583600a0a8a6134e790919063ffffffff16565b6001600160a01b0382166000908152601160205260409020546138fd90826135a7565b6001600160a01b0383166000818152601160205260408082209390935591516370a0823160e01b81526370a082319061393a9030906004016155f9565b60206040518083038186803b15801561395257600080fd5b505afa158015613966573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061398a919061557a565b6001600160a01b0384166000908152601160205260409020549091508110156139c9576001600160a01b03831660009081526011602052604090208190555b7f976177fbe09a15e5e43f848844963a42b41ef919ef17ff21a17a5421de8f4737838360405161095992919061563e565b61330c7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b0f479a16040518163ffffffff1660e01b815260040160206040518083038186803b158015613a5657600080fd5b505afa158015613a6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a8e919061503f565b6001600160a01b0316336001600160a01b03161460676136ac565b600084848484604051602001613ac294939291906155bb565b604051602081830303815290604052805190602001209050949350505050565b6000806000613af48a8a8a8a88611925565b90925090506000613b058a876135a7565b905060008815613b345783613b2357613b1e828461330e565b613b2d565b613b2d82846135a7565b9050613b55565b83613b4857613b4382846135a7565b613b52565b613b52828461330e565b90505b613b63816108058a856134e7565b9c9b505050505050505050505050565b6000806000613c40613b898b8b8b8b8a8a612a09565b73f525c7630f292ba26a7cf1019ff74d4176fa3eaf63d4b9aa637f00000000000000000000000000000000000000000000000000000000000000008e8e8e8e8e6040518763ffffffff1660e01b8152600401613bea96959493929190615856565b60206040518083038186803b158015613c0257600080fd5b505af4158015613c16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c3a919061557a565b906135a7565b90506000613c508a836000613d02565b6001600160a01b038b16600090815260176020526040902054909150613c7690826135a7565b6001600160a01b038b166000908152601760205260409081902091909155517f5d0c0019d3d45fadeb74eff9d2c9924d146d000ac6bcf3c28bf0ac3c9baa011a90613cc6908c90859085906156b3565b60405180910390a1909a909950975050505050505050565b81613cf457613cef811560276136ac565b61068c565b61068c8183101560286136ac565b600082613d11575060006127ea565b6127e77f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015613d6d57600080fd5b505afa158015613d81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613da5919061503f565b6001600160a01b03166376d697608685156040518363ffffffff1660e01b8152600401613dd3929190615698565b60206040518083038186803b158015613deb57600080fd5b505afa158015613dff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e23919061557a565b6001600160a01b0386166000908152600d6020526040902060010154610805908690600a0a6134e7565b6001600160a01b038216600090815260126020526040902054613e7090826135a7565b6001600160a01b038316600090815260126020818152604080842085905560118252909220549152613ea591111560326136ac565b7faa5649d82f5462be9d19b0f2b31a59b2259950a6076550bac9f3a1c07db9f66d8282604051613ed692919061563e565b60405180910390a15050565b6001600160a01b038416600090815260136020526040902054613f11908390613f0b90866135a7565b9061330e565b6001600160a01b038516600090815260136020526040902055808015613f3657508183115b15613f86576001600160a01b0384166000908152600d60205260409020600a01548015613f84576001600160a01b038516600090815260136020526040902054613f849082101560686136ac565b505b8215613fc6577fd9d4761f75e0d0103b5cbeab941eeb443d7a56a35b5baf2a0787c03f03f4e4748484604051613fbd92919061563e565b60405180910390a15b8115614006577f34e07158b9db50df5613e591c44ea2ebc82834eff4a4dc3a46e000e608261d688483604051613ffd92919061563e565b60405180910390a15b50505050565b6001600160a01b03821660009081526011602052604090205461402f908261330e565b6001600160a01b0383166000908152601160209081526040808320849055601290915290205461406291101560326136ac565b7f112726233fbeaeed0f5b1dba5cb0b2b81883dee49fb35ff99fd98ed9f6d31eb08282604051613ed692919061563e565b8061409d5761068c565b6001600160a01b0382166000908152601860205260409020546140c090826135a7565b6001600160a01b038316600090815260186020908152604080832093909355600d905220600b0154801561068a576001600160a01b03831660009081526018602052604090205461068a9082101560656136ac565b600061413a6127106108056107fe60016009015461271061330e90919063ffffffff16565b9050801561068a576001600160a01b038316600090815260176020526040902054614165908261330e565b6001600160a01b03841660009081526017602090815260408083209390935560119052205461419490826135a7565b6001600160a01b0384166000908152601160205260409081902091909155517fe1f042ea063a41fdab4bcdfa4b5e9036c3444aead3f6e901aa5073a9f72e8abd90610959908590849061563e565b600080806142006127106108056141f9828861330e565b88906134e7565b9050600061420e868361330e565b6001600160a01b03881660009081526017602052604090205490915061423490826135a7565b6001600160a01b0388166000908152601760205260409020557f47cd9dda0e50ce30bcaaacd0488452b596221c07ac402a581cfae4d3933cac2b8761427981846106ab565b83604051614289939291906156b3565b60405180910390a190969095509350505050565b6142b16001600160a01b03841682846149c6565b6040516370a0823160e01b81526001600160a01b038416906370a08231906142dd9030906004016155f9565b60206040518083038186803b1580156142f557600080fd5b505afa158015614309573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061432d919061557a565b6001600160a01b039093166000908152600f60205260409020929092555050565b60006143598761081a565b600061436789898987613aa9565b6000818152601660205260409020805491925090614388901515601f6136ac565b61439a868260000154101560206136ac565b6143ac878260010154101560216136ac565b6001810154815460048301546000916143c991610805908b6134e7565b60048401549091506143db908261330e565b60048401556143ea8b826148b5565b506000806143fc8d8d8d8d8d8d614a18565b855491935091508914614644576001600160a01b038c1660009081526014602052604090205460038501558354614433908a61330e565b80855560018501546144459190613cde565b6144538d8d8d8b6001612944565b5050871561447e5761447e8c61447686600101548661330e90919063ffffffff16565b8b6001613ee2565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b1580156144d957600080fd5b505afa1580156144ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614511919061503f565b6001600160a01b03166376d697608d8b156040518363ffffffff1660e01b815260040161453f929190615698565b60206040518083038186803b15801561455757600080fd5b505afa15801561456b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061458f919061557a565b90507f93d75d64d1f84fc6f430a64fc578bdd4c1e090e90ea2d51773e626d19de56d30868f8f8f8f8f8f886145c48c8c61330e565b6040516145d999989796959493929190615738565b60405180910390a17f20853733b590dce729d9f4628682ebd9a34d2354e72679e66f43a008fc03b7738686600001548760010154886002015489600301548a600401548b60050154886040516146369897969594939291906157cc565b60405180910390a15061484f565b8715614657576146578c848b6001613ee2565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b1580156146b257600080fd5b505afa1580156146c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146ea919061503f565b6001600160a01b03166376d697608d8b156040518363ffffffff1660e01b8152600401614718929190615698565b60206040518083038186803b15801561473057600080fd5b505afa158015614744573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614768919061557a565b90507f93d75d64d1f84fc6f430a64fc578bdd4c1e090e90ea2d51773e626d19de56d30868f8f8f8f8f8f8861479d8c8c61330e565b6040516147b299989796959493929190615738565b60405180910390a17f73af1d417d82c240fdb6d319b34ad884487c6bf2845d98980cc52ad9171cb4558686600001548760010154886002015489600301548a600401548b6005015460405161480d979695949392919061579c565b60405180910390a15060008581526016602052604081208181556001810182905560028101829055600381018290556004810182905560058101829055600601555b8761485e5761485e8b8a61495e565b81156148a257871561487a5761487a8c6113758e856000613d02565b60006148888d836000613d02565b90506148958d828a61429d565b9550612a9f945050505050565b5060009c9b505050505050505050505050565b604080518082018252601b81527f5661756c743a20696e73756666696369656e74207265736572766500000000006020808301919091526001600160a01b038516600090815260129091529190912054614910918390614c24565b6001600160a01b0383166000908152601260205260409081902091909155517f533cb5ed32be6a90284e96b5747a1bfc2d38fdb5768a6b5f67ff7d62144ed67b90613ed6908490849061563e565b6001600160a01b0382166000908152601860205260409020548082111561499e57506001600160a01b03821660009081526018602052604081205561068c565b6149a8818361330e565b6001600160a01b038416600090815260186020526040902055505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261068a908490614cbb565b600080600080600080614a2d8c8c8c8a613aa9565b9050601660008281526020019081526020016000209350600080614a608c876000015488600201548c8a60060154611925565b87549196508693509150614a78906108058c846134e7565b93507f3ff41bdde87755b687ae83d0221a232b6be51a803330ed9661c1b5d0105e0d8a838686604051614aad93929190615786565b60405180910390a1505050600080614ad28d8d8d8b8d8a600001548b60030154613b73565b915091506000848015614ae55750600084115b15614b175750600585018054840190558288614b17576000614b098e866000613d02565b9050614b158e8261400c565b505b84158015614b255750600084115b15614b6b576001860154614b39908561330e565b600187015588614b5f576000614b518e866000613d02565b9050614b5d8e826138da565b505b60058601805485900390555b8a15614b9357614b7b818c6135a7565b6001870154909150614b8d908c61330e565b60018701555b85548a1415614bb6576001860154614bac9082906135a7565b6000600188015590505b8083811115614bd057614bc9828561330e565b9050614c06565b6001870154614bdf908561330e565b60018801558915614c06576000614bf88f866000613d02565b9050614c048f8261400c565b505b614c108e84614115565b909e909d509b505050505050505050505050565b60008184841115614cb35760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614c78578181015183820152602001614c60565b50505050905090810190601f168015614ca55780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000614d10826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d6c9092919063ffffffff16565b80519091501561068a57808060200190516020811015614d2f57600080fd5b505161068a5760405162461bcd60e51b815260040180806020018281038252602a815260200180615eb4602a913960400191505060405180910390fd5b60606127e7848460008585614d8085614e86565b614dd1576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b60208310614e0f5780518252601f199092019160209182019101614df0565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614e71576040519150601f19603f3d011682016040523d82523d6000602084013e614e76565b606091505b5091509150612a9f828286614e8c565b3b151590565b60608315614e9b5750816127ea565b825115614eab5782518084602001fd5b60405162461bcd60e51b8152602060048201818152845160248401528451859391928392604401919085019080838360008315614c78578181015183820152602001614c60565b604051806080016040528060001515815260200160001515815260200160008152602001600081525090565b60405180610160016040528060001515815260200160001515815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600015158152602001600081525090565b6040518061026001604052806000151581526020016000815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081526020016000151581526020016000151581526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b80356126af81615e64565b600060208284031215615034578081fd5b813561080b81615e4f565b600060208284031215615050578081fd5b815161080b81615e4f565b6000806040838503121561506d578081fd5b823561507881615e4f565b9150602083013561508881615e4f565b809150509250929050565b6000806000606084860312156150a7578081fd5b83356150b281615e4f565b925060208401356150c281615e4f565b915060408401356150d281615e4f565b809150509250925092565b600080600080608085870312156150f2578081fd5b84356150fd81615e4f565b9350602085013561510d81615e4f565b9250604085013561511d81615e4f565b9150606085013561512d81615e64565b939692955090935050565b600080600080600060a0868803121561514f578081fd5b853561515a81615e4f565b9450602086013561516a81615e4f565b9350604086013561517a81615e4f565b9250606086013561518a81615e64565b9150608086013561519a81615e4f565b809150509295509295909350565b600080600080600060a086880312156151bf578081fd5b85356151ca81615e4f565b945060208601356151da81615e4f565b935060408601356151ea81615e4f565b925060608601356151fa81615e64565b9150608086013561519a81615e64565b60008060008060008060c08789031215615222578081fd5b863561522d81615e4f565b9550602087013561523d81615e4f565b9450604087013561524d81615e4f565b9350606087013561525d81615e64565b9598949750929560808101359460a0909101359350915050565b600080600080600060a0868803121561528e578081fd5b853561529981615e4f565b945060208601356152a981615e4f565b935060408601356152b981615e4f565b925060608601359150608086013561519a81615e64565b600080600080600080600060e0888a0312156152ea578485fd5b87356152f581615e4f565b9650602088013561530581615e4f565b9550604088013561531581615e4f565b9450606088013593506080880135925060a088013561533381615e64565b915060c088013561534381615e4f565b8091505092959891949750929550565b600080600060608486031215615367578081fd5b833561537281615e4f565b9250602084013561538281615e4f565b915060408401356150d281615e64565b600080600083850360c08112156153a7578182fd5b84356153b281615e4f565b935060208501356153c281615e4f565b92506080603f19820112156153d5578182fd5b506040840190509250925092565b6000808284036102808112156153f7578283fd5b833561540281615e4f565b9250610260601f1982011215615416578182fd5b506020830190509250929050565b60008060408385031215615436578182fd5b823561544181615e4f565b946020939093013593505050565b600080600060608486031215615463578081fd5b833561546e81615e4f565b92506020840135915060408401356150d281615e64565b600080600080600060a0868803121561549c578283fd5b85356154a781615e4f565b9450602086013593506040860135925060608601356154c581615e64565b949793965091946080013592915050565b600080600080600060a086880312156154ed578283fd5b85356154f881615e4f565b9450602086013593506040860135925060608601359150608086013561519a81615e64565b6000806040838503121561552f578182fd5b825161553a81615e64565b6020939093015192949293505050565b60006020828403121561555b578081fd5b5035919050565b60006101608284031215615574578081fd5b50919050565b60006020828403121561558b578081fd5b5051919050565b600080604083850312156155a4578182fd5b505080516020909101519092909150565b15159052565b6bffffffffffffffffffffffff19606095861b8116825293851b841660148201529190931b909116602882015290151560f81b603c820152603d0190565b6001600160a01b0391909116815260200190565b6001600160a01b03958616815293909416602084015260408301919091526060820152608081019190915260a00190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b03978816815295871660208701529390951660408501526060840191909152608083015260a082019290925260c081019190915260e00190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b9115158252602082015260400190565b998a526001600160a01b0398891660208b015296881660408a0152949096166060880152911515608087015260a086015260c085015260e08401929092526101008301919091526101208201526101400190565b9889526001600160a01b0397881660208a01529587166040890152939095166060870152608086019190915260a085015291151560c084015260e08301919091526101008201526101200190565b9283529015156020830152604082015260600190565b968752602087019590955260408601939093526060850191909152608084015260a083015260c082015260e00190565b978852602088019690965260408701949094526060860192909252608085015260a084015260c083015260e08201526101000190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b0396871681529486166020860152928516604085015293166060830152911515608082015290151560a082015260c00190565b6001600160a01b0396871681529486166020860152928516604085015293166060830152911515608082015260a081019190915260c00190565b6001600160a01b039485168152928416602084015292166040820152606081019190915260800190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0394851681529290931660208301526040820152901515606082015260800190565b6001600160a01b039687168152949095166020850152604084019290925260608301521515608082015260a081019190915260c00190565b6001600160a01b03968716815294909516602085015260408401929092526060830152608082015290151560a082015260c00190565b6001600160a01b03978816815295871660208701529386166040860152919094166060840152921515608083015260a082019290925260c081019190915260e00190565b815115158152602080830151151590820152604080830151908201526060918201519181019190915260800190565b6000610260820190506159fc8284516155b5565b6020830151602083015260408301516040830152606083015160608301526080830151608083015260a0830151615a3660a08401826155b5565b5060c083015160c083015260e083015160e083015261010080840151615a5e828501826155b5565b505061012080840151615a73828501826155b5565b50506101408381015190830152610160808401519083015261018080840151908301526101a080840151908301526101c080840151908301526101e080840151908301526102008084015190830152610220808401519083015261024092830151929091019190915290565b9182526001600160a01b0316602082015260400190565b9384526001600160a01b039283166020850152911660408301521515606082015260800190565b8381526001600160a01b03831660208201526102a08101615b4960408301615b4485615018565b6155b5565b6020830135606083015260408301356080830152606083013560a0830152608083013560c0830152615b7d60a08401615018565b615b8a60e08401826155b5565b5061010060c08401358184015261012060e085013581850152615bae828601615018565b9150610140615bbf818601846155b5565b615bca828701615018565b92506101609150615bdd828601846155b5565b61018092508086013583860152506101a081860135818601526101c0915082860135828601526101e09250808601358386015250610200818601358186015261022091508286013582860152610240925080860135838601525080850135610260850152508084013561028084015250949350505050565b600061016082019050615c698284516155b5565b6020830151615c7b60208401826155b5565b5060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151615cd7828501826155b5565b505061014092830151919092015290565b90815260200190565b918252602082015260400190565b978852602088019690965260408701949094526060860192909252608085015260a0840152151560c083015260e08201526101000190565b6000813561080e81615e64565b60ff1981541660ff8315151681178255505050565b8135615d6481615e64565b615d6e8183615d44565b506020820135615d7d81615e64565b615d878183615e36565b5060408201356001820155606082013560028201555050565b615db2615dac83615d37565b82615d44565b615dc7615dc160208401615d37565b82615e36565b60408201356001820155606082013560028201556080820135600382015560a0820135600482015560c0820135600582015560e082013560068201556101008201356007820155615e27615e1e6101208401615d37565b60088301615d44565b61014082013560098201555050565b805461ff00191691151560081b61ff0016919091179055565b6001600160a01b038116811461096457600080fd5b801515811461096457600080fdfe5265656e7472616e637947756172643a207265656e7472616e742063616c6c00536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220780b8f363a9f82c9245e4b26355bbfbb483a3812be6dbec4b3cc68c42c98074864736f6c634300070600330000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f1
Deployed ByteCode
0x608060405234801561001057600080fd5b506004361061028a5760003560e01c8063843d43991161015c578063c72c4d10116100ce578063de2ea94811610087578063de2ea948146105be578063e468baf0146105d1578063e4d661b4146105e4578063e67f59a7146105f7578063f07456ce1461060a578063f25552781461061d5761028a565b8063c72c4d101461053a578063cb67e3b11461054f578063d54d5a9f1461056f578063d8f897c314610590578063da76524c146105a3578063dc8f5fac146105b65761028a565b8063a65632e211610120578063a65632e2146104c6578063a93acac2146104d9578063b2a2353e146104ec578063b9b3fd2a14610501578063c3c7b9e914610514578063c65bc7b1146105275761028a565b8063843d43991461046557806388a2f635146104785780638a78daa81461049857806393316212146104ab57806395082d25146104be5761028a565b806342e85182116102005780635bae4263116101b95780635bae4263146103f05780635c07eaab146104035780635da69ed7146104245780635f7bc119146104375780636be6026b1461044a57806382a08490146104525761028a565b806342e851821461035757806348d91abf1461036a5780634a3f088d1461037d578063514ea4bf146103a4578063523fba7f146103ca57806352f55eed146103dd5761028a565b8063126082cf11610252578063126082cf146102f057806313f1e736146102f85780631ce9cb8f1461030b57806320355a2c1461031e5780632abfa9e4146103315780632c668ec1146103445761028a565b806303563e671461028f5780630754d4fb146102ad5780630842b076146102c257806308507fff146102ca5780630a48d5a9146102dd575b600080fd5b610297610630565b6040516102a49190615ce8565b60405180910390f35b6102c06102bb366004615424565b610635565b005b610297610690565b6102c06102d8366004615562565b610696565b6102976102eb366004615424565b6106ab565b610297610814565b6102c0610306366004615023565b61081a565b610297610319366004615023565b610967565b61029761032c366004615023565b610979565b6102c061033f3660046153e3565b610d58565b610297610352366004615424565b610dd0565b610297610365366004615023565b610fa6565b6102c0610378366004615277565b610fb8565b61039061038b3660046150dd565b61144b565b6040516102a4989796959493929190615cff565b6103b76103b236600461554a565b611535565b6040516102a4979695949392919061579c565b6102976103d8366004615023565b611572565b6102976103eb366004615023565b611584565b6102976103fe36600461505b565b611596565b610416610411366004615485565b611925565b6040516102a49291906156d4565b61029761043236600461505b565b6119e6565b6102c0610445366004615023565b611e25565b610297611efd565b6102976104603660046152d0565b611f04565b6102976104733660046154d6565b611f79565b61048b61048636600461505b565b612037565b6040516102a491906159b9565b6102976104a6366004615023565b6120a5565b6102976104b9366004615093565b6120b7565b6102976125b2565b6102c06104d4366004615392565b6125c2565b6102976104e7366004615023565b612602565b6104f46126b4565b6040516102a49190615c55565b61029761050f36600461544f565b612737565b610297610522366004615023565b6127f1565b610297610535366004615023565b612803565b610542612815565b6040516102a491906155f9565b61056261055d366004615023565b612839565b6040516102a491906159e8565b61058261057d3660046151a8565b612944565b6040516102a4929190615cf1565b61029761059e366004615023565b6129f7565b6102976105b136600461520a565b612a09565b610297612aaa565b6102c06105cc366004615138565b612ab0565b6105426105df36600461554a565b612f9b565b6102976105f2366004615353565b612fc8565b6102c0610605366004615023565b613187565b610297610618366004615023565b6131f5565b61029761062b36600461505b565b613207565b601281565b61063d61325d565b6001600160a01b03821660009081526010602052604090205480821115610677576106718361066c848461330e565b61336b565b5061068c565b61068a83610685838561330e565b613414565b505b5050565b600c5490565b61069e61325d565b80600161068a8282615da0565b6000816106ba5750600061080e565b61080b600b6002016000856001600160a01b03166001600160a01b0316815260200190815260200160002060010154600a0a6108057f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b15801561074857600080fd5b505afa15801561075c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610780919061503f565b6001600160a01b03166376d697608760006040518363ffffffff1660e01b81526004016107ae929190615698565b60206040518083038186803b1580156107c657600080fd5b505afa1580156107da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fe919061557a565b85906134e7565b90613540565b90505b92915050565b61271081565b6001600160a01b0381166000908152600d60209081526040808320601001546015909252909120546108795761085a816108544282613540565b906134e7565b6001600160a01b03831660009081526015602052604090205550610964565b6001600160a01b038216600090815260156020526040902054429061089e90836135a7565b11156108aa5750610964565b60006108b583612602565b6001600160a01b0384166000908152601460205260409020549091506108db90826135a7565b6001600160a01b038416600090815260146020526040902055610902826108544282613540565b6001600160a01b038416600090815260156020908152604080832093909355601490528190205490517fa146fc154e1913322e9817d49f0d5c37466c24326e15de10e739a948be815eab916109599186919061563e565b60405180910390a150505b50565b60176020526000908152604090205481565b6000600260005414156109c1576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b60026000556109ce613601565b6001600160a01b0382166000908152600d60205260409020546109f59060ff16600e6136ac565b60007f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b158015610a5057600080fd5b505afa158015610a64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a88919061503f565b90506000610a95846136e5565b9050610aa560008211600f6136ac565b610aae8461081a565b60007f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015610b0957600080fd5b505afa158015610b1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b41919061503f565b6001600160a01b03166376d697608660006040518363ffffffff1660e01b8152600401610b6f929190615698565b60206040518083038186803b158015610b8757600080fd5b505afa158015610b9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bbf919061557a565b90506000610bdd68327cb2734119d3b7a9601e1b61080585856134e7565b9050610bea8187866137a5565b9050610bfa6000821160126136ac565b6000610c1668327cb2734119d3b7a9601e1b61080586866134e7565b9050610c238188876137a5565b6001600160a01b038816600090815260106020526040902054909150610c4990826135a7565b6001600160a01b0388166000908152601060205260409081902091909155517ffbf914421322be849bbd116e701fe46b8e6b903e15192c1d479b8748a738452090610c97908990849061563e565b60405180910390a1610ca987856138da565b6040516340c10f1960e01b81526001600160a01b038616906340c10f1990610cd7903390859060040161563e565b600060405180830381600087803b158015610cf157600080fd5b505af1158015610d05573d6000803e3d6000fd5b505050507fc6c8b444f53d4e65122f6f9780fd7db1ff1a47d82ff0cbb2f450eee0c289e534338886846000604051610d4195949392919061560d565b60405180910390a160016000559695505050505050565b610d6061325d565b6040516326286a1760e21b8152735019828f87706c895c889577cdd272f4a3ac1823906398a1a85c90610d9c90600b9086908690600401615b1d565b60006040518083038186803b158015610db457600080fd5b505af4158015610dc8573d6000803e3d6000fd5b505050505050565b6000807f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015610e2c57600080fd5b505afa158015610e40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e64919061503f565b6001600160a01b03166376d697608560016040518363ffffffff1660e01b8152600401610e92929190615698565b60206040518083038186803b158015610eaa57600080fd5b505afa158015610ebe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ee2919061557a565b90506000610f00826108058668327cb2734119d3b7a9601e1b6134e7565b9050610f9d817f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f5f57600080fd5b505afa158015610f73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f97919061503f565b876137a5565b95945050505050565b60106020526000908152604090205481565b60026000541415610ffe576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b600260005561100b6139fa565b604051634755223960e01b8152735019828f87706c895c889577cdd272f4a3ac18239063475522399061104990600b90889088908790600401615af6565b60006040518083038186803b15801561106157600080fd5b505af4158015611075573d6000803e3d6000fd5b50506001546110909250610100900460ff169050601c6136ac565b6110998461081a565b60006110a786868685613aa9565b60008181526016602090815260408083208151631f94a27560e31b81529151949550936001600160a01b037f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f1169263fca513a89260048082019391829003018186803b15801561111657600080fd5b505afa15801561112a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114e919061503f565b6001600160a01b03166376d6976087866040518363ffffffff1660e01b815260040161117b929190615698565b60206040518083038186803b15801561119357600080fd5b505afa1580156111a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111cb919061557a565b905084156111f6576111f66111e1898887612fc8565b83546111ed90886135a7565b111560696136ac565b815461120457600282018190555b8154158015906112145750600085115b1561123a57611234868360000154846002015487858a8860060154613ae2565b60028301555b6000806112548a8a8a898b89600001548a60030154613b73565b9150915060006112638a6136e5565b905060006112718b836106ab565b600187015490915061128390826135a7565b6001870181905561129890851115601d6136ac565b60018601546112a7908561330e565b60018701556001600160a01b038b16600090815260146020526040902054600387015585546112d6908a6135a7565b8087554260068801556112ec901515601e6136ac565b6112fe86600001548760010154613cde565b61130c8c8c8c8b6001612944565b5050600061131c8c8b6001613d02565b600488015490915061132e90826135a7565b600488015561133d8c82613e4d565b881561137f5761135b8c6113518c886135a7565b8460008e11613ee2565b6113658c846138da565b61137a8c6113758e886000613d02565b61400c565b611389565b6113898b8b614093565b7f2fe68525253654c21998f35787a8d0f361905ef647c854092430ab65f2f15022888e8e8e868f8f8d8d6040516113c899989796959493929190615738565b60405180910390a17f20853733b590dce729d9f4628682ebd9a34d2354e72679e66f43a008fc03b77388886000015489600101548a600201548b600301548c600401548d600501548d6040516114259897969594939291906157cc565b60405180910390a16114378c85614115565b505060016000555050505050505050505050565b6000806000806000806000806000601660006114698f8f8f8f613aa9565b81526020019081526020016000206040518060e001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820154815260200160068201548152505090506000808260a00151136114e4578160a001516000036114ea565b8160a001515b9050816000015182602001518360400151846060015185608001518560008860a0015112158860c0015199509950995099509950995099509950505094995094995094999196509450565b6016602052600090815260409020805460018201546002830154600384015460048501546005860154600690960154949593949293919290919087565b600f6020526000908152604090205481565b60116020526000908152604090205481565b6000600260005414156115de576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b60026000556115eb613601565b6001600160a01b0383166000908152600d60205260409020546116129060ff16600e6136ac565b60007f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561166d57600080fd5b505afa158015611681573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a5919061503f565b905060006116b2826136e5565b90506116c26000821160126136ac565b6116cb8561081a565b60006116d78683610dd0565b90506116e76000821160156136ac565b6116f18683613414565b6116fb868261400c565b604051632770a7eb60e21b81526001600160a01b03841690639dc29fac90611729903090869060040161563e565b600060405180830381600087803b15801561174357600080fd5b505af1158015611757573d6000803e3d6000fd5b50506040516370a0823160e01b81526001600160a01b03861692506370a0823191506117879030906004016155f9565b60206040518083038186803b15801561179f57600080fd5b505afa1580156117b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d7919061557a565b6001600160a01b0384166000908152600f602052604080822092909255905163562b7e0760e11b815273f525c7630f292ba26a7cf1019ff74d4176fa3eaf9063ac56fc0e9061184e907f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f1908b9088906004016158ba565b60206040518083038186803b15801561186657600080fd5b505af415801561187a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189e919061557a565b90506000806118ae8985856141e2565b915091506118c06000831160166136ac565b6118cb89838a61429d565b7ff0769e076d0b71b7205e1d6800e3ede98265f5a21bacbd6067947cbe7a3fa671888a87858760405161190295949392919061560d565b60405180910390a16119148982614115565b506001600055979650505050505050565b60008073f525c7630f292ba26a7cf1019ff74d4176fa3eaf63cfb90b207f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f189898989896040518763ffffffff1660e01b815260040161198996959493929190615907565b604080518083038186803b1580156119a057600080fd5b505af41580156119b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d8919061551d565b915091509550959350505050565b600060026000541415611a2e576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b6002600055611a3b613601565b6001600160a01b0383166000908152600d6020526040902054611a629060ff16600e6136ac565b60007f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b158015611abd57600080fd5b505afa158015611ad1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af5919061503f565b90506000611b02856136e5565b9050611b1260008211600f6136ac565b611b1b8561081a565b60007f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015611b7657600080fd5b505afa158015611b8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bae919061503f565b6001600160a01b03166376d697608760006040518363ffffffff1660e01b8152600401611bdc929190615698565b60206040518083038186803b158015611bf457600080fd5b505afa158015611c08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c2c919061557a565b90506000611c4a68327cb2734119d3b7a9601e1b61080585856134e7565b9050611c578188866137a5565b9050611c676000821160126136ac565b6040516360d41f1560e11b815260009073f525c7630f292ba26a7cf1019ff74d4176fa3eaf9063c1a83e2a90611cc5907f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f1908c9087906004016158ba565b60206040518083038186803b158015611cdd57600080fd5b505af4158015611cf1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d15919061557a565b9050600080611d258a87856141e2565b90925090506000611d4668327cb2734119d3b7a9601e1b61080585896134e7565b9050611d53818c8a6137a5565b9050611d5f8b8261336b565b611d698b846138da565b6040516340c10f1960e01b81526001600160a01b038916906340c10f1990611d97908d90859060040161563e565b600060405180830381600087803b158015611db157600080fd5b505af1158015611dc5573d6000803e3d6000fd5b505050507fc6c8b444f53d4e65122f6f9780fd7db1ff1a47d82ff0cbb2f450eee0c289e5348a8c898488604051611e0095949392919061560d565b60405180910390a1611e128b83614115565b60016000559a9950505050505050505050565b60026000541415611e6b576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b600260009081556001600160a01b0382168152600d6020526040902054611e969060ff16600e6136ac565b6000611ea1826136e5565b9050611eb160008211600f6136ac565b611ebb82826138da565b7fa5a389190ebf6170a133bda5c769b77f4d6715b8aa172ec0ddf8473d0b4944bd8282604051611eec92919061563e565b60405180910390a150506001600055565b620f424081565b600060026000541415611f4c576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b6002600055611f596139fa565b611f688888888888888861434e565b600160005598975050505050505050565b6040516308e83b0160e21b815260009073f525c7630f292ba26a7cf1019ff74d4176fa3eaf906323a0ec0490611fdd907f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f1908a908a908a908a908a9060040161593f565b60206040518083038186803b158015611ff557600080fd5b505af4158015612009573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202d919061557a565b9695505050505050565b61203f614ef2565b506001600160a01b039182166000908152600e602090815260408083209390941682529182528290208251608081018452815460ff8082161515835261010090910416151592810192909252600181015492820192909252600290910154606082015290565b60186020526000908152604090205481565b6000600260005414156120ff576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b60026000556001546121159060ff1660176136ac565b6001600160a01b0384166000908152600d602052604090205461213c9060ff16600e6136ac565b6001600160a01b0383166000908152600d60205260409020546121639060ff16600e6136ac565b612183836001600160a01b0316856001600160a01b03161415601a6136ac565b61218c8461081a565b6121958361081a565b60006121a0856136e5565b90506121b060008211601b6136ac565b60007f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b15801561220b57600080fd5b505afa15801561221f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612243919061503f565b90506000816001600160a01b03166376d697608860006040518363ffffffff1660e01b8152600401612276929190615698565b60206040518083038186803b15801561228e57600080fd5b505afa1580156122a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c6919061557a565b90506000826001600160a01b03166376d697608860016040518363ffffffff1660e01b81526004016122f9929190615698565b60206040518083038186803b15801561231157600080fd5b505afa158015612325573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612349919061557a565b9050600061235b8261080587866134e7565b9050612368818a8a6137a5565b9050600061238668327cb2734119d3b7a9601e1b61080588876134e7565b9050612423818b7f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156123e657600080fd5b505afa1580156123fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061241e919061503f565b6137a5565b9050600073f525c7630f292ba26a7cf1019ff74d4176fa3eaf6387e35c2c7f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f18d8d866040518563ffffffff1660e01b81526004016124849493929190615890565b60206040518083038186803b15801561249c57600080fd5b505af41580156124b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124d4919061557a565b90506000806124e48c86856141e2565b915091506124f28d8561336b565b6124fc8c85613414565b6125068d8a6138da565b6125108c8661400c565b6001600160a01b038c166000908152600d602090815260408083206009015460119092529091205461254591111560646136ac565b6125508c838d61429d565b7f0874b2d545cb271cdbda4e093020c452328b24af12382ed62c4d00f5c26709db8b8e8e8c89878960405161258b9796959493929190615657565b60405180910390a161259d8c82614115565b5060016000559b9a5050505050505050505050565b68327cb2734119d3b7a9601e1b81565b6125ca61325d565b6001600160a01b038084166000908152600e6020908152604080832093861683529290522081906125fb8282615d59565b5050505050565b6040516246b9f360e11b815260009073f525c7630f292ba26a7cf1019ff74d4176fa3eaf90628d73e69061265c907f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f1908690600401615802565b60206040518083038186803b15801561267457600080fd5b505af4158015612688573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ac919061557a565b90505b919050565b6126bc614f1e565b50604080516101608101825260015460ff8082161515835261010091829004811615156020840152600254938301939093526003546060830152600454608083015260055460a083015260065460c083015260075460e0830152600854908201526009549091161515610120820152600a5461014082015290565b604051634732e96360e11b815260009073f525c7630f292ba26a7cf1019ff74d4176fa3eaf90638e65d2c690612797907f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f1908890889088906004016158de565b60206040518083038186803b1580156127af57600080fd5b505af41580156127c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127e7919061557a565b90505b9392505050565b60126020526000908152604090205481565b60146020526000908152604090205481565b7f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f181565b612841614f7e565b506001600160a01b03166000908152600d6020818152604092839020835161026081018552815460ff90811615158252600183015493820193909352600282015494810194909452600381015460608501526004810154608085015260058101548216151560a0850152600681015460c0850152600781015460e08501526008810154808316151561010080870191909152900490911615156101208401526009810154610140840152600a810154610160840152600b810154610180840152600c8101546101a0840152908101546101c0830152600e8101546101e0830152600f81015461020083015260108101546102208301526011015461024082015290565b60008073f525c7630f292ba26a7cf1019ff74d4176fa3eaf634e9c91757f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f189898989896040518763ffffffff1660e01b81526004016129a89695949392919061581c565b604080518083038186803b1580156129bf57600080fd5b505af41580156129d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d89190615592565b60156020526000908152604090205481565b604051636df5632b60e11b815260009073f525c7630f292ba26a7cf1019ff74d4176fa3eaf9063dbeac65690612a4f9030908b908b908b908b908b908b90600401615975565b60206040518083038186803b158015612a6757600080fd5b505af4158015612a7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a9f919061557a565b979650505050505050565b600b5490565b60026000541415612af6576040805162461bcd60e51b815260206004820152601f6024820152600080516020615e73833981519152604482015290519081900360640190fd5b6002600081905550612bad336001600160a01b03167f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b0316636a22dede6040518163ffffffff1660e01b815260040160206040518083038186803b158015612b6457600080fd5b505afa158015612b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9c919061503f565b6001600160a01b03161460226136ac565b612bb68461081a565b6000612bc486868686613aa9565b600081815260166020908152604091829020825160e08101845281548082526001830154938201939093526002820154938101939093526003810154606084015260048101546080840152600581015460a08401526006015460c083015291925090612c33901515601f6136ac565b600080612c44898989896000612944565b91509150612c57826000141560246136ac565b8160021415612c7e57612c74898989600087600001518b8f61434e565b5050505050612f8f565b6000612c8c89836000613d02565b6001600160a01b038a16600090815260176020526040902054909150612cb290826135a7565b6001600160a01b038a166000908152601760205260409081902091909155517f5d0c0019d3d45fadeb74eff9d2c9924d146d000ac6bcf3c28bf0ac3c9baa011a90612d02908b90859085906156b3565b60405180910390a1612d188985608001516148b5565b8615612d5657612d46896000612d3f8760200151886000015161330e90919063ffffffff16565b6000613ee2565b612d56896113758b856000613d02565b60007f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015612db157600080fd5b505afa158015612dc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612de9919061503f565b6001600160a01b03166376d697608a8a156040518363ffffffff1660e01b8152600401612e17929190615698565b60206040518083038186803b158015612e2f57600080fd5b505afa158015612e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e67919061557a565b90507f2e1f85a64a2f22cf2f0c42584e7c919ed4abe8d53675cff0f62bf1e95a1c676f868c8c8c8c8a600001518b602001518c608001518d60a001518a604051612eba9a999897969594939291906156e4565b60405180910390a187158015612ed35750846020015183105b15612f03576020850151600090612eea908561330e565b9050612f018b612efc8d846000613d02565b6138da565b505b87612f1657612f1689866000015161495e565b6000868152601660205260408120818155600181018290556002810182905560038101829055600481018290556005810182905560060181905560085490612f67908c906113759082908590613d02565b612f7d8b612f778d846000613d02565b8a61429d565b612f878b84614115565b505050505050505b50506001600055505050565b6000600b6001018281548110612fad57fe5b6000918252602090912001546001600160a01b031692915050565b6001600160a01b038084166000908152600e60208181526040808420948716808552948252808420815160808082018452825460ff80821615158452610100918290048116151584880152600180860154858801526002958601546060808701919091529a8a52600d808952878b20885161026081018a5281548516151581529281015499830199909952958801549681019690965260038701549986019990995260048601549185019190915260058501548816151560a0850152600685015460c0850152600785015460e085015260088501548089161515858301520490961615156101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a08301528201546101c0820152918101546101e0830152600f810154610200830152601081015461022083015260110154610240820152909190831561314f5781511561312c576000925050506127ea565b6040820151613143576101a0015191506127ea9050565b506040015190506127ea565b816020015115613164576000925050506127ea565b606082015161317b576101c0015191506127ea9050565b506060015190506127ea565b61318f61325d565b604051632e43d10160e01b8152735019828f87706c895c889577cdd272f4a3ac182390632e43d101906131c990600b908590600401615adf565b60006040518083038186803b1580156131e157600080fd5b505af41580156125fb573d6000803e3d6000fd5b60136020526000908152604090205481565b6000613211613601565b6001600160a01b0383166000908152601760205260409020548061323957600091505061080e565b6001600160a01b03841660009081526017602052604081205561080b84828561429d565b61330c7f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663d759932b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156132b957600080fd5b505afa1580156132cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f1919061503f565b6001600160a01b0316336001600160a01b03161460666136ac565b565b600082821115613365576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6001600160a01b03821660009081526010602052604090205461338e90826135a7565b6001600160a01b038316600090815260106020908152604080832093909355600d9052206007015480156133e3576001600160a01b0383166000908152601060205260409020546133e39082101560336136ac565b7ffbf914421322be849bbd116e701fe46b8e6b903e15192c1d479b8748a7384520838360405161095992919061563e565b6001600160a01b03821660009081526010602052604090205481811161348f576001600160a01b03831660009081526010602052604080822091909155517fda43d5cdf64be33afb6f3e9858581d78f3855a1f6fcb50e5eb86c0c5db312d1c90613481908590849061563e565b60405180910390a15061068c565b613499818361330e565b6001600160a01b0384166000908152601060205260409081902091909155517fda43d5cdf64be33afb6f3e9858581d78f3855a1f6fcb50e5eb86c0c5db312d1c90610959908590859061563e565b6000826134f65750600061080e565b8282028284828161350357fe5b041461080b5760405162461bcd60e51b8152600401808060200182810382526021815260200180615e936021913960400191505060405180910390fd5b6000808211613596576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161359f57fe5b049392505050565b60008282018381101561080b576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b61330c7f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663028d27d86040518163ffffffff1660e01b815260040160206040518083038186803b15801561365d57600080fd5b505afa158015613671573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613695919061503f565b6001600160a01b0316336001600160a01b03161460365b60405163cda9d28760e01b815273a60f20ac5322e698d15aeb169e72f8977d939da29063cda9d28790610d9c90859085906004016156d4565b6001600160a01b0381166000818152600f60205260408082205490516370a0823160e01b8152919290918391906370a08231906137269030906004016155f9565b60206040518083038186803b15801561373e57600080fd5b505afa158015613752573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613776919061557a565b6001600160a01b0385166000908152600f60205260409020819055905061379d818361330e565b949350505050565b6000807f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561380157600080fd5b505afa158015613815573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613839919061503f565b90506000816001600160a01b0316856001600160a01b031614613877576001600160a01b0385166000908152600d602052604090206001015461387a565b60125b90506000826001600160a01b0316856001600160a01b0316146138b8576001600160a01b0385166000908152600d60205260409020600101546138bb565b60125b9050612a9f82600a0a61080583600a0a8a6134e790919063ffffffff16565b6001600160a01b0382166000908152601160205260409020546138fd90826135a7565b6001600160a01b0383166000818152601160205260408082209390935591516370a0823160e01b81526370a082319061393a9030906004016155f9565b60206040518083038186803b15801561395257600080fd5b505afa158015613966573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061398a919061557a565b6001600160a01b0384166000908152601160205260409020549091508110156139c9576001600160a01b03831660009081526011602052604090208190555b7f976177fbe09a15e5e43f848844963a42b41ef919ef17ff21a17a5421de8f4737838360405161095992919061563e565b61330c7f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663b0f479a16040518163ffffffff1660e01b815260040160206040518083038186803b158015613a5657600080fd5b505afa158015613a6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a8e919061503f565b6001600160a01b0316336001600160a01b03161460676136ac565b600084848484604051602001613ac294939291906155bb565b604051602081830303815290604052805190602001209050949350505050565b6000806000613af48a8a8a8a88611925565b90925090506000613b058a876135a7565b905060008815613b345783613b2357613b1e828461330e565b613b2d565b613b2d82846135a7565b9050613b55565b83613b4857613b4382846135a7565b613b52565b613b52828461330e565b90505b613b63816108058a856134e7565b9c9b505050505050505050505050565b6000806000613c40613b898b8b8b8b8a8a612a09565b73f525c7630f292ba26a7cf1019ff74d4176fa3eaf63d4b9aa637f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f18e8e8e8e8e6040518763ffffffff1660e01b8152600401613bea96959493929190615856565b60206040518083038186803b158015613c0257600080fd5b505af4158015613c16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c3a919061557a565b906135a7565b90506000613c508a836000613d02565b6001600160a01b038b16600090815260176020526040902054909150613c7690826135a7565b6001600160a01b038b166000908152601760205260409081902091909155517f5d0c0019d3d45fadeb74eff9d2c9924d146d000ac6bcf3c28bf0ac3c9baa011a90613cc6908c90859085906156b3565b60405180910390a1909a909950975050505050505050565b81613cf457613cef811560276136ac565b61068c565b61068c8183101560286136ac565b600082613d11575060006127ea565b6127e77f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015613d6d57600080fd5b505afa158015613d81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613da5919061503f565b6001600160a01b03166376d697608685156040518363ffffffff1660e01b8152600401613dd3929190615698565b60206040518083038186803b158015613deb57600080fd5b505afa158015613dff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e23919061557a565b6001600160a01b0386166000908152600d6020526040902060010154610805908690600a0a6134e7565b6001600160a01b038216600090815260126020526040902054613e7090826135a7565b6001600160a01b038316600090815260126020818152604080842085905560118252909220549152613ea591111560326136ac565b7faa5649d82f5462be9d19b0f2b31a59b2259950a6076550bac9f3a1c07db9f66d8282604051613ed692919061563e565b60405180910390a15050565b6001600160a01b038416600090815260136020526040902054613f11908390613f0b90866135a7565b9061330e565b6001600160a01b038516600090815260136020526040902055808015613f3657508183115b15613f86576001600160a01b0384166000908152600d60205260409020600a01548015613f84576001600160a01b038516600090815260136020526040902054613f849082101560686136ac565b505b8215613fc6577fd9d4761f75e0d0103b5cbeab941eeb443d7a56a35b5baf2a0787c03f03f4e4748484604051613fbd92919061563e565b60405180910390a15b8115614006577f34e07158b9db50df5613e591c44ea2ebc82834eff4a4dc3a46e000e608261d688483604051613ffd92919061563e565b60405180910390a15b50505050565b6001600160a01b03821660009081526011602052604090205461402f908261330e565b6001600160a01b0383166000908152601160209081526040808320849055601290915290205461406291101560326136ac565b7f112726233fbeaeed0f5b1dba5cb0b2b81883dee49fb35ff99fd98ed9f6d31eb08282604051613ed692919061563e565b8061409d5761068c565b6001600160a01b0382166000908152601860205260409020546140c090826135a7565b6001600160a01b038316600090815260186020908152604080832093909355600d905220600b0154801561068a576001600160a01b03831660009081526018602052604090205461068a9082101560656136ac565b600061413a6127106108056107fe60016009015461271061330e90919063ffffffff16565b9050801561068a576001600160a01b038316600090815260176020526040902054614165908261330e565b6001600160a01b03841660009081526017602090815260408083209390935560119052205461419490826135a7565b6001600160a01b0384166000908152601160205260409081902091909155517fe1f042ea063a41fdab4bcdfa4b5e9036c3444aead3f6e901aa5073a9f72e8abd90610959908590849061563e565b600080806142006127106108056141f9828861330e565b88906134e7565b9050600061420e868361330e565b6001600160a01b03881660009081526017602052604090205490915061423490826135a7565b6001600160a01b0388166000908152601760205260409020557f47cd9dda0e50ce30bcaaacd0488452b596221c07ac402a581cfae4d3933cac2b8761427981846106ab565b83604051614289939291906156b3565b60405180910390a190969095509350505050565b6142b16001600160a01b03841682846149c6565b6040516370a0823160e01b81526001600160a01b038416906370a08231906142dd9030906004016155f9565b60206040518083038186803b1580156142f557600080fd5b505afa158015614309573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061432d919061557a565b6001600160a01b039093166000908152600f60205260409020929092555050565b60006143598761081a565b600061436789898987613aa9565b6000818152601660205260409020805491925090614388901515601f6136ac565b61439a868260000154101560206136ac565b6143ac878260010154101560216136ac565b6001810154815460048301546000916143c991610805908b6134e7565b60048401549091506143db908261330e565b60048401556143ea8b826148b5565b506000806143fc8d8d8d8d8d8d614a18565b855491935091508914614644576001600160a01b038c1660009081526014602052604090205460038501558354614433908a61330e565b80855560018501546144459190613cde565b6144538d8d8d8b6001612944565b5050871561447e5761447e8c61447686600101548661330e90919063ffffffff16565b8b6001613ee2565b60007f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b1580156144d957600080fd5b505afa1580156144ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614511919061503f565b6001600160a01b03166376d697608d8b156040518363ffffffff1660e01b815260040161453f929190615698565b60206040518083038186803b15801561455757600080fd5b505afa15801561456b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061458f919061557a565b90507f93d75d64d1f84fc6f430a64fc578bdd4c1e090e90ea2d51773e626d19de56d30868f8f8f8f8f8f886145c48c8c61330e565b6040516145d999989796959493929190615738565b60405180910390a17f20853733b590dce729d9f4628682ebd9a34d2354e72679e66f43a008fc03b7738686600001548760010154886002015489600301548a600401548b60050154886040516146369897969594939291906157cc565b60405180910390a15061484f565b8715614657576146578c848b6001613ee2565b60007f0000000000000000000000003cf8f33789ab2c9ec6a8f6fd5f074f4a8a0547f16001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b1580156146b257600080fd5b505afa1580156146c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146ea919061503f565b6001600160a01b03166376d697608d8b156040518363ffffffff1660e01b8152600401614718929190615698565b60206040518083038186803b15801561473057600080fd5b505afa158015614744573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614768919061557a565b90507f93d75d64d1f84fc6f430a64fc578bdd4c1e090e90ea2d51773e626d19de56d30868f8f8f8f8f8f8861479d8c8c61330e565b6040516147b299989796959493929190615738565b60405180910390a17f73af1d417d82c240fdb6d319b34ad884487c6bf2845d98980cc52ad9171cb4558686600001548760010154886002015489600301548a600401548b6005015460405161480d979695949392919061579c565b60405180910390a15060008581526016602052604081208181556001810182905560028101829055600381018290556004810182905560058101829055600601555b8761485e5761485e8b8a61495e565b81156148a257871561487a5761487a8c6113758e856000613d02565b60006148888d836000613d02565b90506148958d828a61429d565b9550612a9f945050505050565b5060009c9b505050505050505050505050565b604080518082018252601b81527f5661756c743a20696e73756666696369656e74207265736572766500000000006020808301919091526001600160a01b038516600090815260129091529190912054614910918390614c24565b6001600160a01b0383166000908152601260205260409081902091909155517f533cb5ed32be6a90284e96b5747a1bfc2d38fdb5768a6b5f67ff7d62144ed67b90613ed6908490849061563e565b6001600160a01b0382166000908152601860205260409020548082111561499e57506001600160a01b03821660009081526018602052604081205561068c565b6149a8818361330e565b6001600160a01b038416600090815260186020526040902055505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261068a908490614cbb565b600080600080600080614a2d8c8c8c8a613aa9565b9050601660008281526020019081526020016000209350600080614a608c876000015488600201548c8a60060154611925565b87549196508693509150614a78906108058c846134e7565b93507f3ff41bdde87755b687ae83d0221a232b6be51a803330ed9661c1b5d0105e0d8a838686604051614aad93929190615786565b60405180910390a1505050600080614ad28d8d8d8b8d8a600001548b60030154613b73565b915091506000848015614ae55750600084115b15614b175750600585018054840190558288614b17576000614b098e866000613d02565b9050614b158e8261400c565b505b84158015614b255750600084115b15614b6b576001860154614b39908561330e565b600187015588614b5f576000614b518e866000613d02565b9050614b5d8e826138da565b505b60058601805485900390555b8a15614b9357614b7b818c6135a7565b6001870154909150614b8d908c61330e565b60018701555b85548a1415614bb6576001860154614bac9082906135a7565b6000600188015590505b8083811115614bd057614bc9828561330e565b9050614c06565b6001870154614bdf908561330e565b60018801558915614c06576000614bf88f866000613d02565b9050614c048f8261400c565b505b614c108e84614115565b909e909d509b505050505050505050505050565b60008184841115614cb35760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614c78578181015183820152602001614c60565b50505050905090810190601f168015614ca55780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000614d10826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d6c9092919063ffffffff16565b80519091501561068a57808060200190516020811015614d2f57600080fd5b505161068a5760405162461bcd60e51b815260040180806020018281038252602a815260200180615eb4602a913960400191505060405180910390fd5b60606127e7848460008585614d8085614e86565b614dd1576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b60208310614e0f5780518252601f199092019160209182019101614df0565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614e71576040519150601f19603f3d011682016040523d82523d6000602084013e614e76565b606091505b5091509150612a9f828286614e8c565b3b151590565b60608315614e9b5750816127ea565b825115614eab5782518084602001fd5b60405162461bcd60e51b8152602060048201818152845160248401528451859391928392604401919085019080838360008315614c78578181015183820152602001614c60565b604051806080016040528060001515815260200160001515815260200160008152602001600081525090565b60405180610160016040528060001515815260200160001515815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600015158152602001600081525090565b6040518061026001604052806000151581526020016000815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081526020016000151581526020016000151581526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b80356126af81615e64565b600060208284031215615034578081fd5b813561080b81615e4f565b600060208284031215615050578081fd5b815161080b81615e4f565b6000806040838503121561506d578081fd5b823561507881615e4f565b9150602083013561508881615e4f565b809150509250929050565b6000806000606084860312156150a7578081fd5b83356150b281615e4f565b925060208401356150c281615e4f565b915060408401356150d281615e4f565b809150509250925092565b600080600080608085870312156150f2578081fd5b84356150fd81615e4f565b9350602085013561510d81615e4f565b9250604085013561511d81615e4f565b9150606085013561512d81615e64565b939692955090935050565b600080600080600060a0868803121561514f578081fd5b853561515a81615e4f565b9450602086013561516a81615e4f565b9350604086013561517a81615e4f565b9250606086013561518a81615e64565b9150608086013561519a81615e4f565b809150509295509295909350565b600080600080600060a086880312156151bf578081fd5b85356151ca81615e4f565b945060208601356151da81615e4f565b935060408601356151ea81615e4f565b925060608601356151fa81615e64565b9150608086013561519a81615e64565b60008060008060008060c08789031215615222578081fd5b863561522d81615e4f565b9550602087013561523d81615e4f565b9450604087013561524d81615e4f565b9350606087013561525d81615e64565b9598949750929560808101359460a0909101359350915050565b600080600080600060a0868803121561528e578081fd5b853561529981615e4f565b945060208601356152a981615e4f565b935060408601356152b981615e4f565b925060608601359150608086013561519a81615e64565b600080600080600080600060e0888a0312156152ea578485fd5b87356152f581615e4f565b9650602088013561530581615e4f565b9550604088013561531581615e4f565b9450606088013593506080880135925060a088013561533381615e64565b915060c088013561534381615e4f565b8091505092959891949750929550565b600080600060608486031215615367578081fd5b833561537281615e4f565b9250602084013561538281615e4f565b915060408401356150d281615e64565b600080600083850360c08112156153a7578182fd5b84356153b281615e4f565b935060208501356153c281615e4f565b92506080603f19820112156153d5578182fd5b506040840190509250925092565b6000808284036102808112156153f7578283fd5b833561540281615e4f565b9250610260601f1982011215615416578182fd5b506020830190509250929050565b60008060408385031215615436578182fd5b823561544181615e4f565b946020939093013593505050565b600080600060608486031215615463578081fd5b833561546e81615e4f565b92506020840135915060408401356150d281615e64565b600080600080600060a0868803121561549c578283fd5b85356154a781615e4f565b9450602086013593506040860135925060608601356154c581615e64565b949793965091946080013592915050565b600080600080600060a086880312156154ed578283fd5b85356154f881615e4f565b9450602086013593506040860135925060608601359150608086013561519a81615e64565b6000806040838503121561552f578182fd5b825161553a81615e64565b6020939093015192949293505050565b60006020828403121561555b578081fd5b5035919050565b60006101608284031215615574578081fd5b50919050565b60006020828403121561558b578081fd5b5051919050565b600080604083850312156155a4578182fd5b505080516020909101519092909150565b15159052565b6bffffffffffffffffffffffff19606095861b8116825293851b841660148201529190931b909116602882015290151560f81b603c820152603d0190565b6001600160a01b0391909116815260200190565b6001600160a01b03958616815293909416602084015260408301919091526060820152608081019190915260a00190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b03978816815295871660208701529390951660408501526060840191909152608083015260a082019290925260c081019190915260e00190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b9115158252602082015260400190565b998a526001600160a01b0398891660208b015296881660408a0152949096166060880152911515608087015260a086015260c085015260e08401929092526101008301919091526101208201526101400190565b9889526001600160a01b0397881660208a01529587166040890152939095166060870152608086019190915260a085015291151560c084015260e08301919091526101008201526101200190565b9283529015156020830152604082015260600190565b968752602087019590955260408601939093526060850191909152608084015260a083015260c082015260e00190565b978852602088019690965260408701949094526060860192909252608085015260a084015260c083015260e08201526101000190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b0396871681529486166020860152928516604085015293166060830152911515608082015290151560a082015260c00190565b6001600160a01b0396871681529486166020860152928516604085015293166060830152911515608082015260a081019190915260c00190565b6001600160a01b039485168152928416602084015292166040820152606081019190915260800190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0394851681529290931660208301526040820152901515606082015260800190565b6001600160a01b039687168152949095166020850152604084019290925260608301521515608082015260a081019190915260c00190565b6001600160a01b03968716815294909516602085015260408401929092526060830152608082015290151560a082015260c00190565b6001600160a01b03978816815295871660208701529386166040860152919094166060840152921515608083015260a082019290925260c081019190915260e00190565b815115158152602080830151151590820152604080830151908201526060918201519181019190915260800190565b6000610260820190506159fc8284516155b5565b6020830151602083015260408301516040830152606083015160608301526080830151608083015260a0830151615a3660a08401826155b5565b5060c083015160c083015260e083015160e083015261010080840151615a5e828501826155b5565b505061012080840151615a73828501826155b5565b50506101408381015190830152610160808401519083015261018080840151908301526101a080840151908301526101c080840151908301526101e080840151908301526102008084015190830152610220808401519083015261024092830151929091019190915290565b9182526001600160a01b0316602082015260400190565b9384526001600160a01b039283166020850152911660408301521515606082015260800190565b8381526001600160a01b03831660208201526102a08101615b4960408301615b4485615018565b6155b5565b6020830135606083015260408301356080830152606083013560a0830152608083013560c0830152615b7d60a08401615018565b615b8a60e08401826155b5565b5061010060c08401358184015261012060e085013581850152615bae828601615018565b9150610140615bbf818601846155b5565b615bca828701615018565b92506101609150615bdd828601846155b5565b61018092508086013583860152506101a081860135818601526101c0915082860135828601526101e09250808601358386015250610200818601358186015261022091508286013582860152610240925080860135838601525080850135610260850152508084013561028084015250949350505050565b600061016082019050615c698284516155b5565b6020830151615c7b60208401826155b5565b5060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151615cd7828501826155b5565b505061014092830151919092015290565b90815260200190565b918252602082015260400190565b978852602088019690965260408701949094526060860192909252608085015260a0840152151560c083015260e08201526101000190565b6000813561080e81615e64565b60ff1981541660ff8315151681178255505050565b8135615d6481615e64565b615d6e8183615d44565b506020820135615d7d81615e64565b615d878183615e36565b5060408201356001820155606082013560028201555050565b615db2615dac83615d37565b82615d44565b615dc7615dc160208401615d37565b82615e36565b60408201356001820155606082013560028201556080820135600382015560a0820135600482015560c0820135600582015560e082013560068201556101008201356007820155615e27615e1e6101208401615d37565b60088301615d44565b61014082013560098201555050565b805461ff00191691151560081b61ff0016919091179055565b6001600160a01b038116811461096457600080fd5b801515811461096457600080fdfe5265656e7472616e637947756172643a207265656e7472616e742063616c6c00536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220780b8f363a9f82c9245e4b26355bbfbb483a3812be6dbec4b3cc68c42c98074864736f6c63430007060033
External libraries
GeneralUtils : 0xa60f20ac5322e698d15aeb169e72f8977d939da2
TokenConfigsLogic : 0x5019828f87706c895c889577cdd272f4a3ac1823
VaultUtils : 0xf525c7630f292ba26a7cf1019ff74d4176fa3eaf