Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- VaultUtils
- Optimization enabled
- true
- Compiler version
- v0.7.6+commit.7338295f
- Optimization runs
- 200
- EVM Version
- istanbul
- Verified at
- 2024-06-07T07:01:41.100333Z
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());
}
}
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/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/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/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/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;
}
}
Compiler Settings
{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"libraries":{},"evmVersion":"istanbul"}
Contract ABI
[{"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":"getBuyUsdphFeeBps","inputs":[{"type":"IAddressesProvider","name":"addressesProvider","internalType":"contract IAddressesProvider"},{"type":"address","name":"_token","internalType":"address"},{"type":"uint256","name":"_usdphAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getDelta","inputs":[{"type":"IAddressesProvider","name":"addressesProvider","internalType":"contract IAddressesProvider"},{"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":"IAddressesProvider","name":"addressesProvider","internalType":"contract IAddressesProvider"},{"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":"IVault","name":"vault","internalType":"contract IVault"},{"type":"address","name":"","internalType":"address"},{"type":"address","name":"_collateralToken","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"bool","name":"","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":"IAddressesProvider","name":"addressesProvider","internalType":"contract IAddressesProvider"},{"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":"IAddressesProvider","name":"addressesProvider","internalType":"contract IAddressesProvider"},{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getPositionFee","inputs":[{"type":"IAddressesProvider","name":"addressesProvider","internalType":"contract IAddressesProvider"},{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"address","name":"_indexToken","internalType":"address"},{"type":"bool","name":"_isLong","internalType":"bool"},{"type":"uint256","name":"_sizeDelta","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getSellUsdphFeeBps","inputs":[{"type":"IAddressesProvider","name":"addressesProvider","internalType":"contract IAddressesProvider"},{"type":"address","name":"_token","internalType":"address"},{"type":"uint256","name":"_usdphAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getSwapFeeBps","inputs":[{"type":"IAddressesProvider","name":"addressesProvider","internalType":"contract IAddressesProvider"},{"type":"address","name":"_tokenIn","internalType":"address"},{"type":"address","name":"_tokenOut","internalType":"address"},{"type":"uint256","name":"_usdphAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"validateLiquidation","inputs":[{"type":"IAddressesProvider","name":"addressesProvider","internalType":"contract IAddressesProvider"},{"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"}]}]
Contract Creation Code
0x6124e7610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106100bd5760003560e01c80638e65d2c61161007b5780638e65d2c614610142578063ac56fc0e14610155578063c1a83e2a14610168578063cfb90b201461017b578063d4b9aa631461019c578063dbeac656146101af576100bd565b80628d73e6146100c2578063126082cf146100eb57806323a0ec04146100f35780634e9c9175146101065780636be6026b1461012757806387e35c2c1461012f575b600080fd5b6100d56100d0366004611c9f565b6101c2565b6040516100e2919061242f565b60405180910390f35b6100d561052a565b6100d5610101366004611f03565b610530565b610119610114366004611cd7565b610837565b6040516100e2929190612438565b6100d5610bc8565b6100d561013d366004611dd0565b610bcf565b6100d5610150366004611e60565b610e85565b6100d5610163366004611e20565b610f82565b6100d5610176366004611e20565b61108b565b61018e610189366004611eb2565b611189565b6040516100e29291906122c3565b6100d56101aa366004611d58565b611465565b6100d56101bd366004611f5b565b6115b3565b600080836001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b1580156101fe57600080fd5b505afa158015610212573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102369190611c56565b90506000816001600160a01b031663cb67e3b1856040518263ffffffff1660e01b8152600401610266919061222f565b6102606040518083038186803b15801561027f57600080fd5b505afa158015610293573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102b79190611fdd565b9050600081610220015190504261034a82856001600160a01b031663d8f897c3896040518263ffffffff1660e01b81526004016102f4919061222f565b60206040518083038186803b15801561030c57600080fd5b505afa158015610320573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610344919061218b565b9061167b565b111561035c5760009350505050610524565b60006103ee826103e8866001600160a01b031663d8f897c38a6040518263ffffffff1660e01b8152600401610391919061222f565b60206040518083038186803b1580156103a957600080fd5b505afa1580156103bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103e1919061218b565b42906116d5565b90611732565b90506000846001600160a01b03166352f55eed886040518263ffffffff1660e01b815260040161041e919061222f565b60206040518083038186803b15801561043657600080fd5b505afa15801561044a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061046e919061218b565b90508061048357600095505050505050610524565b61051c816103e884610516896001600160a01b031663c3c7b9e98d6040518263ffffffff1660e01b81526004016104ba919061222f565b60206040518083038186803b1580156104d257600080fd5b505afa1580156104e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050a919061218b565b6102408a015190611799565b90611799565b955050505050505b92915050565b61271081565b6000866001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561056b57600080fd5b505afa15801561057f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a39190611c56565b6001600160a01b031663b2a2353e6040518163ffffffff1660e01b81526004016101606040518083038186803b1580156105dc57600080fd5b505afa1580156105f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061491906120e3565b610120015161062457508261082d565b6000876001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561065f57600080fd5b505afa158015610673573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106979190611c56565b6001600160a01b03166342e85182886040518263ffffffff1660e01b81526004016106c2919061222f565b60206040518083038186803b1580156106da57600080fd5b505afa1580156106ee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610712919061218b565b90506000610720828861167b565b9050836107435781871161073d5761073882886116d5565b610740565b60005b90505b600061074f8a8a6117f2565b9050806107615786935050505061082d565b60008184116107795761077482856116d5565b610783565b61078384836116d5565b9050600082841161079d5761079883856116d5565b6107a7565b6107a784846116d5565b9050818110156107e95760006107c1846103e88b86611799565b90508981116107d9576107d48a826116d5565b6107dc565b60005b965050505050505061082d565b60006107fa60026103e8858561167b565b9050838111156108075750825b6000610817856103e88c85611799565b90506108238b8261167b565b9750505050505050505b9695505050505050565b6000806000886001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561087557600080fd5b505afa158015610889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ad9190611c56565b905060006108be828a8a8a8a611a5f565b805160408083015160c08401519151635c07eaab60e01b815293945060009384936001600160a01b03881693635c07eaab93610901938f93928f91600401612288565b604080518083038186803b15801561091857600080fd5b505afa15801561092c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109509190611c72565b91509150600061098261096b8e8e8e8e8e8a60000151611465565b610344878f8f8f8f8b600001518c606001516115b3565b9050821580156109955750818460200151105b156109d15787156109c15760405162461bcd60e51b81526004016109b890612326565b60405180910390fd5b600196509450610bbd9350505050565b6020840151836109ed5760208501516109ea90846116d5565b90505b81811015610a24578815610a135760405162461bcd60e51b81526004016109b8906123ed565b600197509550610bbd945050505050565b60008e6001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b158015610a5f57600080fd5b505afa158015610a73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a979190611c56565b6001600160a01b031663b2a2353e6040518163ffffffff1660e01b81526004016101606040518083038186803b158015610ad057600080fd5b505afa158015610ae4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0891906120e3565b9050610b228161010001518461167b90919063ffffffff16565b821015610b5a578915610b475760405162461bcd60e51b81526004016109b89061239f565b6001839850985050505050505050610bbd565b8551610b6890612710611799565b6040820151610b78908490611799565b1015610baf578915610b9c5760405162461bcd60e51b81526004016109b89061236a565b6002839850985050505050505050610bbd565b506000975090955050505050505b965096945050505050565b620f424081565b600080856001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b158015610c0b57600080fd5b505afa158015610c1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c439190611c56565b9050600080876001600160a01b031663d759932b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610c8157600080fd5b505afa158015610c95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb99190611c56565b6001600160a01b031663eab4d7a9846001600160a01b031663cb67e3b18a6040518263ffffffff1660e01b8152600401610cf3919061222f565b6102606040518083038186803b158015610d0c57600080fd5b505afa158015610d20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d449190611fdd565b61010001518015610dd3575060405163cb67e3b160e01b81526001600160a01b0386169063cb67e3b190610d7c908b9060040161222f565b6102606040518083038186803b158015610d9557600080fd5b505afa158015610da9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dcd9190611fdd565b61010001515b6040518263ffffffff1660e01b8152600401610def91906122b8565b604080518083038186803b158015610e0657600080fd5b505afa158015610e1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e3e91906121a3565b915091506000610e5389898886866001610530565b90506000610e668a898987876000610530565b9050808211610e755780610e77565b815b9a9950505050505050505050565b600080856001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b158015610ec157600080fd5b505afa158015610ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef99190611c56565b6001600160a01b031663cb67e3b1866040518263ffffffff1660e01b8152600401610f24919061222f565b6102606040518083038186803b158015610f3d57600080fd5b505afa158015610f51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f759190611fdd565b905061082d818585611b27565b600080846001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b158015610fbe57600080fd5b505afa158015610fd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff69190611c56565b6001600160a01b031663b2a2353e6040518163ffffffff1660e01b81526004016101606040518083038186803b15801561102f57600080fd5b505afa158015611043573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106791906120e3565b90506110808585858460a0015185606001516000610530565b9150505b9392505050565b600080846001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b1580156110c757600080fd5b505afa1580156110db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ff9190611c56565b6001600160a01b031663b2a2353e6040518163ffffffff1660e01b81526004016101606040518083038186803b15801561113857600080fd5b505afa15801561114c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117091906120e3565b90506110808585858460a0015185606001516001610530565b6000806000851160405180604001604052806002815260200161066760f31b815250906111c95760405162461bcd60e51b81526004016109b891906122d3565b506000886001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b15801561120557600080fd5b505afa158015611219573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061123d9190611c56565b6001600160a01b03166376d697608987156040518363ffffffff1660e01b815260040161126b92919061226d565b60206040518083038186803b15801561128357600080fd5b505afa158015611297573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bb919061218b565b905060008187116112d5576112d082886116d5565b6112df565b6112df87836116d5565b905060006112f1886103e88b85611799565b9050600087156113045750878311611309565b508288115b60008c6001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561134457600080fd5b505afa158015611358573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137c9190611c56565b6001600160a01b031663cb67e3b18d6040518263ffffffff1660e01b81526004016113a7919061222f565b6102606040518083038186803b1580156113c057600080fd5b505afa1580156113d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f89190611fdd565b9050600061141161140a838e8d611b27565b8a9061167b565b4211611421578160600151611424565b60005b905082801561144757506114388c82611799565b61144485612710611799565b11155b1561145157600093505b50909c919b50909950505050505050505050565b6000816114745750600061082d565b6000876001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b1580156114af57600080fd5b505afa1580156114c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e79190611c56565b6001600160a01b031663cb67e3b1866040518263ffffffff1660e01b8152600401611512919061222f565b6102606040518083038186803b15801561152b57600080fd5b505afa15801561153f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115639190611fdd565b9050600061159a6127106103e8611593886115835785610200015161158a565b856101e001515b612710906116d5565b8790611799565b90506115a684826116d5565b9998505050505050505050565b6000826115c257506000611670565b600061164a838a6001600160a01b031663c65bc7b18a6040518263ffffffff1660e01b81526004016115f4919061222f565b60206040518083038186803b15801561160c57600080fd5b505afa158015611620573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611644919061218b565b906116d5565b90508061165b576000915050611670565b61166c620f42406103e88684611799565b9150505b979650505050505050565b600082820183811015611084576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60008282111561172c576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6000808211611788576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161179157fe5b049392505050565b6000826117a857506000610524565b828202828482816117b557fe5b04146110845760405162461bcd60e51b81526004018080602001828103825260218152602001806124916021913960400191505060405180910390fd5b600080836001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561182e57600080fd5b505afa158015611842573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118669190611c56565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561189e57600080fd5b505afa1580156118b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d6919061218b565b9050806118e7576000915050610524565b6000846001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561192257600080fd5b505afa158015611936573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061195a9190611c56565b90506000816001600160a01b031663cb67e3b1866040518263ffffffff1660e01b815260040161198a919061222f565b6102606040518083038186803b1580156119a357600080fd5b505afa1580156119b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119db9190611fdd565b60400151905061082d826001600160a01b031663dc8f5fac6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a1d57600080fd5b505afa158015611a31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a55919061218b565b6103e88386611799565b611a67611c09565b611a6f611c09565b60008060008060008b6001600160a01b0316634a3f088d8c8c8c8c6040518563ffffffff1660e01b8152600401611aa99493929190612243565b6101006040518083038186803b158015611ac257600080fd5b505afa158015611ad6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611afa91906121c6565b968d5250505060208a01929092526040890152606088015260c087015250939a9950505050505050505050565b6000821580611b3857506080840151155b15611b4557506000611084565b600082611b5757846101c00151611b5e565b846101a001515b905080611b715750506080830151611084565b8460a001518015611b8157508084105b80611b8b57508084145b15611b9c5750506080830151611084565b80841015611bc757611bbf816103e886886080015161179990919063ffffffff16565b915050611084565b611bbf6127106103e8836103e8611bfe611bf28b60c00151610516898d6116d590919063ffffffff16565b61034488612710611799565b60808b015190611799565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8051611c5181612482565b919050565b600060208284031215611c67578081fd5b81516110848161246a565b60008060408385031215611c84578081fd5b8251611c8f81612482565b6020939093015192949293505050565b60008060408385031215611cb1578182fd5b8235611cbc8161246a565b91506020830135611ccc8161246a565b809150509250929050565b60008060008060008060c08789031215611cef578182fd5b8635611cfa8161246a565b95506020870135611d0a8161246a565b94506040870135611d1a8161246a565b93506060870135611d2a8161246a565b92506080870135611d3a81612482565b915060a0870135611d4a81612482565b809150509295509295509295565b60008060008060008060c08789031215611d70578182fd5b8635611d7b8161246a565b95506020870135611d8b8161246a565b94506040870135611d9b8161246a565b93506060870135611dab8161246a565b92506080870135611dbb81612482565b8092505060a087013590509295509295509295565b60008060008060808587031215611de5578384fd5b8435611df08161246a565b93506020850135611e008161246a565b92506040850135611e108161246a565b9396929550929360600135925050565b600080600060608486031215611e34578081fd5b8335611e3f8161246a565b92506020840135611e4f8161246a565b929592945050506040919091013590565b60008060008060808587031215611e75578182fd5b8435611e808161246a565b93506020850135611e908161246a565b9250604085013591506060850135611ea781612482565b939692955090935050565b60008060008060008060c08789031215611eca578384fd5b8635611ed58161246a565b95506020870135611ee58161246a565b945060408701359350606087013592506080870135611dbb81612482565b60008060008060008060c08789031215611f1b578384fd5b8635611f268161246a565b95506020870135611f368161246a565b945060408701359350606087013592506080870135915060a0870135611d4a81612482565b600080600080600080600060e0888a031215611f75578485fd5b8735611f808161246a565b96506020880135611f908161246a565b95506040880135611fa08161246a565b94506060880135611fb08161246a565b93506080880135611fc081612482565b9699959850939692959460a0840135945060c09093013592915050565b6000610260808385031215611ff0578182fd5b611ff981612446565b905061200483611c46565b81526020830151602082015260408301516040820152606083015160608201526080830151608082015261203a60a08401611c46565b60a082015260c083015160c082015260e083015160e0820152610100612061818501611c46565b90820152610120612073848201611c46565b908201526101408381015190820152610160808401519082015261018080840151908201526101a080840151908201526101c080840151908201526101e0808401519082015261020080840151908201526102208084015190820152610240928301519281019290925250919050565b60006101608083850312156120f6578182fd5b6120ff81612446565b905061210a83611c46565b815261211860208401611c46565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e0820152610100808401518183015250610120612173818501611c46565b90820152610140928301519281019290925250919050565b60006020828403121561219c578081fd5b5051919050565b600080604083850312156121b5578182fd5b505080516020909101519092909150565b600080600080600080600080610100898b0312156121e2578182fd5b885197506020890151965060408901519550606089015194506080890151935060a0890151925060c089015161221781612482565b8092505060e089015190509295985092959890939650565b6001600160a01b0391909116815260200190565b6001600160a01b039485168152928416602084015292166040820152901515606082015260800190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b039590951685526020850193909352604084019190915215156060830152608082015260a00190565b901515815260200190565b9115158252602082015260400190565b6000602080835283518082850152825b818110156122ff578581018301518582016040015282016122e3565b818111156123105783604083870101525b50601f01601f1916929092016040019392505050565b60208082526024908201527f5661756c745574696c733a206c6f737365732065786365656420636f6c6c6174604082015263195c985b60e21b606082015260800190565b6020808252818101527f5661756c745574696c733a206d61784c65766572616765206578636565646564604082015260600190565b6020808252602e908201527f5661756c745574696c733a206c69717569646174696f6e20666565732065786360408201526d1959590818dbdb1b185d195c985b60921b606082015260800190565b60208082526022908201527f5661756c745574696c733a20666565732065786365656420636f6c6c61746572604082015261185b60f21b606082015260800190565b90815260200190565b918252602082015260400190565b60405181810167ffffffffffffffff8111828210171561246257fe5b604052919050565b6001600160a01b038116811461247f57600080fd5b50565b801515811461247f57600080fdfe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a264697066735822122050b0025f9b44f4e5dfa35dc924767108eaa667baa2870b32255d3d374c8c923564736f6c63430007060033
Deployed ByteCode
0x73f525c7630f292ba26a7cf1019ff74d4176fa3eaf30146080604052600436106100bd5760003560e01c80638e65d2c61161007b5780638e65d2c614610142578063ac56fc0e14610155578063c1a83e2a14610168578063cfb90b201461017b578063d4b9aa631461019c578063dbeac656146101af576100bd565b80628d73e6146100c2578063126082cf146100eb57806323a0ec04146100f35780634e9c9175146101065780636be6026b1461012757806387e35c2c1461012f575b600080fd5b6100d56100d0366004611c9f565b6101c2565b6040516100e2919061242f565b60405180910390f35b6100d561052a565b6100d5610101366004611f03565b610530565b610119610114366004611cd7565b610837565b6040516100e2929190612438565b6100d5610bc8565b6100d561013d366004611dd0565b610bcf565b6100d5610150366004611e60565b610e85565b6100d5610163366004611e20565b610f82565b6100d5610176366004611e20565b61108b565b61018e610189366004611eb2565b611189565b6040516100e29291906122c3565b6100d56101aa366004611d58565b611465565b6100d56101bd366004611f5b565b6115b3565b600080836001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b1580156101fe57600080fd5b505afa158015610212573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102369190611c56565b90506000816001600160a01b031663cb67e3b1856040518263ffffffff1660e01b8152600401610266919061222f565b6102606040518083038186803b15801561027f57600080fd5b505afa158015610293573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102b79190611fdd565b9050600081610220015190504261034a82856001600160a01b031663d8f897c3896040518263ffffffff1660e01b81526004016102f4919061222f565b60206040518083038186803b15801561030c57600080fd5b505afa158015610320573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610344919061218b565b9061167b565b111561035c5760009350505050610524565b60006103ee826103e8866001600160a01b031663d8f897c38a6040518263ffffffff1660e01b8152600401610391919061222f565b60206040518083038186803b1580156103a957600080fd5b505afa1580156103bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103e1919061218b565b42906116d5565b90611732565b90506000846001600160a01b03166352f55eed886040518263ffffffff1660e01b815260040161041e919061222f565b60206040518083038186803b15801561043657600080fd5b505afa15801561044a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061046e919061218b565b90508061048357600095505050505050610524565b61051c816103e884610516896001600160a01b031663c3c7b9e98d6040518263ffffffff1660e01b81526004016104ba919061222f565b60206040518083038186803b1580156104d257600080fd5b505afa1580156104e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050a919061218b565b6102408a015190611799565b90611799565b955050505050505b92915050565b61271081565b6000866001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561056b57600080fd5b505afa15801561057f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a39190611c56565b6001600160a01b031663b2a2353e6040518163ffffffff1660e01b81526004016101606040518083038186803b1580156105dc57600080fd5b505afa1580156105f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061491906120e3565b610120015161062457508261082d565b6000876001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561065f57600080fd5b505afa158015610673573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106979190611c56565b6001600160a01b03166342e85182886040518263ffffffff1660e01b81526004016106c2919061222f565b60206040518083038186803b1580156106da57600080fd5b505afa1580156106ee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610712919061218b565b90506000610720828861167b565b9050836107435781871161073d5761073882886116d5565b610740565b60005b90505b600061074f8a8a6117f2565b9050806107615786935050505061082d565b60008184116107795761077482856116d5565b610783565b61078384836116d5565b9050600082841161079d5761079883856116d5565b6107a7565b6107a784846116d5565b9050818110156107e95760006107c1846103e88b86611799565b90508981116107d9576107d48a826116d5565b6107dc565b60005b965050505050505061082d565b60006107fa60026103e8858561167b565b9050838111156108075750825b6000610817856103e88c85611799565b90506108238b8261167b565b9750505050505050505b9695505050505050565b6000806000886001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561087557600080fd5b505afa158015610889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ad9190611c56565b905060006108be828a8a8a8a611a5f565b805160408083015160c08401519151635c07eaab60e01b815293945060009384936001600160a01b03881693635c07eaab93610901938f93928f91600401612288565b604080518083038186803b15801561091857600080fd5b505afa15801561092c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109509190611c72565b91509150600061098261096b8e8e8e8e8e8a60000151611465565b610344878f8f8f8f8b600001518c606001516115b3565b9050821580156109955750818460200151105b156109d15787156109c15760405162461bcd60e51b81526004016109b890612326565b60405180910390fd5b600196509450610bbd9350505050565b6020840151836109ed5760208501516109ea90846116d5565b90505b81811015610a24578815610a135760405162461bcd60e51b81526004016109b8906123ed565b600197509550610bbd945050505050565b60008e6001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b158015610a5f57600080fd5b505afa158015610a73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a979190611c56565b6001600160a01b031663b2a2353e6040518163ffffffff1660e01b81526004016101606040518083038186803b158015610ad057600080fd5b505afa158015610ae4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0891906120e3565b9050610b228161010001518461167b90919063ffffffff16565b821015610b5a578915610b475760405162461bcd60e51b81526004016109b89061239f565b6001839850985050505050505050610bbd565b8551610b6890612710611799565b6040820151610b78908490611799565b1015610baf578915610b9c5760405162461bcd60e51b81526004016109b89061236a565b6002839850985050505050505050610bbd565b506000975090955050505050505b965096945050505050565b620f424081565b600080856001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b158015610c0b57600080fd5b505afa158015610c1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c439190611c56565b9050600080876001600160a01b031663d759932b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610c8157600080fd5b505afa158015610c95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb99190611c56565b6001600160a01b031663eab4d7a9846001600160a01b031663cb67e3b18a6040518263ffffffff1660e01b8152600401610cf3919061222f565b6102606040518083038186803b158015610d0c57600080fd5b505afa158015610d20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d449190611fdd565b61010001518015610dd3575060405163cb67e3b160e01b81526001600160a01b0386169063cb67e3b190610d7c908b9060040161222f565b6102606040518083038186803b158015610d9557600080fd5b505afa158015610da9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dcd9190611fdd565b61010001515b6040518263ffffffff1660e01b8152600401610def91906122b8565b604080518083038186803b158015610e0657600080fd5b505afa158015610e1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e3e91906121a3565b915091506000610e5389898886866001610530565b90506000610e668a898987876000610530565b9050808211610e755780610e77565b815b9a9950505050505050505050565b600080856001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b158015610ec157600080fd5b505afa158015610ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef99190611c56565b6001600160a01b031663cb67e3b1866040518263ffffffff1660e01b8152600401610f24919061222f565b6102606040518083038186803b158015610f3d57600080fd5b505afa158015610f51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f759190611fdd565b905061082d818585611b27565b600080846001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b158015610fbe57600080fd5b505afa158015610fd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff69190611c56565b6001600160a01b031663b2a2353e6040518163ffffffff1660e01b81526004016101606040518083038186803b15801561102f57600080fd5b505afa158015611043573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106791906120e3565b90506110808585858460a0015185606001516000610530565b9150505b9392505050565b600080846001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b1580156110c757600080fd5b505afa1580156110db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ff9190611c56565b6001600160a01b031663b2a2353e6040518163ffffffff1660e01b81526004016101606040518083038186803b15801561113857600080fd5b505afa15801561114c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117091906120e3565b90506110808585858460a0015185606001516001610530565b6000806000851160405180604001604052806002815260200161066760f31b815250906111c95760405162461bcd60e51b81526004016109b891906122d3565b506000886001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b15801561120557600080fd5b505afa158015611219573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061123d9190611c56565b6001600160a01b03166376d697608987156040518363ffffffff1660e01b815260040161126b92919061226d565b60206040518083038186803b15801561128357600080fd5b505afa158015611297573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bb919061218b565b905060008187116112d5576112d082886116d5565b6112df565b6112df87836116d5565b905060006112f1886103e88b85611799565b9050600087156113045750878311611309565b508288115b60008c6001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561134457600080fd5b505afa158015611358573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137c9190611c56565b6001600160a01b031663cb67e3b18d6040518263ffffffff1660e01b81526004016113a7919061222f565b6102606040518083038186803b1580156113c057600080fd5b505afa1580156113d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f89190611fdd565b9050600061141161140a838e8d611b27565b8a9061167b565b4211611421578160600151611424565b60005b905082801561144757506114388c82611799565b61144485612710611799565b11155b1561145157600093505b50909c919b50909950505050505050505050565b6000816114745750600061082d565b6000876001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b1580156114af57600080fd5b505afa1580156114c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e79190611c56565b6001600160a01b031663cb67e3b1866040518263ffffffff1660e01b8152600401611512919061222f565b6102606040518083038186803b15801561152b57600080fd5b505afa15801561153f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115639190611fdd565b9050600061159a6127106103e8611593886115835785610200015161158a565b856101e001515b612710906116d5565b8790611799565b90506115a684826116d5565b9998505050505050505050565b6000826115c257506000611670565b600061164a838a6001600160a01b031663c65bc7b18a6040518263ffffffff1660e01b81526004016115f4919061222f565b60206040518083038186803b15801561160c57600080fd5b505afa158015611620573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611644919061218b565b906116d5565b90508061165b576000915050611670565b61166c620f42406103e88684611799565b9150505b979650505050505050565b600082820183811015611084576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60008282111561172c576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6000808211611788576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161179157fe5b049392505050565b6000826117a857506000610524565b828202828482816117b557fe5b04146110845760405162461bcd60e51b81526004018080602001828103825260218152602001806124916021913960400191505060405180910390fd5b600080836001600160a01b0316638ad6de4e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561182e57600080fd5b505afa158015611842573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118669190611c56565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561189e57600080fd5b505afa1580156118b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d6919061218b565b9050806118e7576000915050610524565b6000846001600160a01b0316638d928af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561192257600080fd5b505afa158015611936573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061195a9190611c56565b90506000816001600160a01b031663cb67e3b1866040518263ffffffff1660e01b815260040161198a919061222f565b6102606040518083038186803b1580156119a357600080fd5b505afa1580156119b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119db9190611fdd565b60400151905061082d826001600160a01b031663dc8f5fac6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a1d57600080fd5b505afa158015611a31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a55919061218b565b6103e88386611799565b611a67611c09565b611a6f611c09565b60008060008060008b6001600160a01b0316634a3f088d8c8c8c8c6040518563ffffffff1660e01b8152600401611aa99493929190612243565b6101006040518083038186803b158015611ac257600080fd5b505afa158015611ad6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611afa91906121c6565b968d5250505060208a01929092526040890152606088015260c087015250939a9950505050505050505050565b6000821580611b3857506080840151155b15611b4557506000611084565b600082611b5757846101c00151611b5e565b846101a001515b905080611b715750506080830151611084565b8460a001518015611b8157508084105b80611b8b57508084145b15611b9c5750506080830151611084565b80841015611bc757611bbf816103e886886080015161179990919063ffffffff16565b915050611084565b611bbf6127106103e8836103e8611bfe611bf28b60c00151610516898d6116d590919063ffffffff16565b61034488612710611799565b60808b015190611799565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8051611c5181612482565b919050565b600060208284031215611c67578081fd5b81516110848161246a565b60008060408385031215611c84578081fd5b8251611c8f81612482565b6020939093015192949293505050565b60008060408385031215611cb1578182fd5b8235611cbc8161246a565b91506020830135611ccc8161246a565b809150509250929050565b60008060008060008060c08789031215611cef578182fd5b8635611cfa8161246a565b95506020870135611d0a8161246a565b94506040870135611d1a8161246a565b93506060870135611d2a8161246a565b92506080870135611d3a81612482565b915060a0870135611d4a81612482565b809150509295509295509295565b60008060008060008060c08789031215611d70578182fd5b8635611d7b8161246a565b95506020870135611d8b8161246a565b94506040870135611d9b8161246a565b93506060870135611dab8161246a565b92506080870135611dbb81612482565b8092505060a087013590509295509295509295565b60008060008060808587031215611de5578384fd5b8435611df08161246a565b93506020850135611e008161246a565b92506040850135611e108161246a565b9396929550929360600135925050565b600080600060608486031215611e34578081fd5b8335611e3f8161246a565b92506020840135611e4f8161246a565b929592945050506040919091013590565b60008060008060808587031215611e75578182fd5b8435611e808161246a565b93506020850135611e908161246a565b9250604085013591506060850135611ea781612482565b939692955090935050565b60008060008060008060c08789031215611eca578384fd5b8635611ed58161246a565b95506020870135611ee58161246a565b945060408701359350606087013592506080870135611dbb81612482565b60008060008060008060c08789031215611f1b578384fd5b8635611f268161246a565b95506020870135611f368161246a565b945060408701359350606087013592506080870135915060a0870135611d4a81612482565b600080600080600080600060e0888a031215611f75578485fd5b8735611f808161246a565b96506020880135611f908161246a565b95506040880135611fa08161246a565b94506060880135611fb08161246a565b93506080880135611fc081612482565b9699959850939692959460a0840135945060c09093013592915050565b6000610260808385031215611ff0578182fd5b611ff981612446565b905061200483611c46565b81526020830151602082015260408301516040820152606083015160608201526080830151608082015261203a60a08401611c46565b60a082015260c083015160c082015260e083015160e0820152610100612061818501611c46565b90820152610120612073848201611c46565b908201526101408381015190820152610160808401519082015261018080840151908201526101a080840151908201526101c080840151908201526101e0808401519082015261020080840151908201526102208084015190820152610240928301519281019290925250919050565b60006101608083850312156120f6578182fd5b6120ff81612446565b905061210a83611c46565b815261211860208401611c46565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e0820152610100808401518183015250610120612173818501611c46565b90820152610140928301519281019290925250919050565b60006020828403121561219c578081fd5b5051919050565b600080604083850312156121b5578182fd5b505080516020909101519092909150565b600080600080600080600080610100898b0312156121e2578182fd5b885197506020890151965060408901519550606089015194506080890151935060a0890151925060c089015161221781612482565b8092505060e089015190509295985092959890939650565b6001600160a01b0391909116815260200190565b6001600160a01b039485168152928416602084015292166040820152901515606082015260800190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b039590951685526020850193909352604084019190915215156060830152608082015260a00190565b901515815260200190565b9115158252602082015260400190565b6000602080835283518082850152825b818110156122ff578581018301518582016040015282016122e3565b818111156123105783604083870101525b50601f01601f1916929092016040019392505050565b60208082526024908201527f5661756c745574696c733a206c6f737365732065786365656420636f6c6c6174604082015263195c985b60e21b606082015260800190565b6020808252818101527f5661756c745574696c733a206d61784c65766572616765206578636565646564604082015260600190565b6020808252602e908201527f5661756c745574696c733a206c69717569646174696f6e20666565732065786360408201526d1959590818dbdb1b185d195c985b60921b606082015260800190565b60208082526022908201527f5661756c745574696c733a20666565732065786365656420636f6c6c61746572604082015261185b60f21b606082015260800190565b90815260200190565b918252602082015260400190565b60405181810167ffffffffffffffff8111828210171561246257fe5b604052919050565b6001600160a01b038116811461247f57600080fd5b50565b801515811461247f57600080fdfe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a264697066735822122050b0025f9b44f4e5dfa35dc924767108eaa667baa2870b32255d3d374c8c923564736f6c63430007060033