Contract Name:
ShortsTracker
Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
contract Governable {
address public gov;
constructor() public {
gov = msg.sender;
}
modifier onlyGov() {
require(msg.sender == gov, "Governable: forbidden");
_;
}
function setGov(address _gov) external onlyGov {
gov = _gov;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
interface IShortsTracker {
function isGlobalShortDataReady() external view returns (bool);
function globalShortAveragePrices(address _token) external view returns (uint256);
function getNextGlobalShortData(
address _account,
address _collateralToken,
address _indexToken,
uint256 _nextPrice,
uint256 _sizeDelta,
bool _isIncrease
) external view returns (uint256, uint256);
function updateGlobalShortData(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
uint256 _sizeDelta,
uint256 _markPrice,
bool _isIncrease
) external;
function setIsGlobalShortDataReady(bool value) external;
function setInitData(address[] calldata _tokens, uint256[] calldata _averagePrices) external;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "./IVaultUtils.sol";
interface IVault {
function isInitialized() external view returns (bool);
function isSwapEnabled() external view returns (bool);
function isLeverageEnabled() external view returns (bool);
function setVaultUtils(IVaultUtils _vaultUtils) external;
function setError(uint256 _errorCode, string calldata _error) external;
function router() external view returns (address);
function usdg() external view returns (address);
function gov() external view returns (address);
function whitelistedTokenCount() external view returns (uint256);
function maxLeverage() external view returns (uint256);
function minProfitTime() external view returns (uint256);
function hasDynamicFees() external view returns (bool);
function fundingInterval() external view returns (uint256);
function totalTokenWeights() external view returns (uint256);
function getTargetUsdgAmount(address _token) external view returns (uint256);
function inManagerMode() external view returns (bool);
function inPrivateLiquidationMode() external view returns (bool);
function maxGasPrice() external view returns (uint256);
function approvedRouters(address _account, address _router) external view returns (bool);
function isLiquidator(address _account) external view returns (bool);
function isManager(address _account) external view returns (bool);
function minProfitBasisPoints(address _token) external view returns (uint256);
function tokenBalances(address _token) external view returns (uint256);
function lastFundingTimes(address _token) external view returns (uint256);
function setMaxLeverage(uint256 _maxLeverage) external;
function setInManagerMode(bool _inManagerMode) external;
function setManager(address _manager, bool _isManager) external;
function setIsSwapEnabled(bool _isSwapEnabled) external;
function setIsLeverageEnabled(bool _isLeverageEnabled) external;
function setMaxGasPrice(uint256 _maxGasPrice) external;
function setUsdgAmount(address _token, uint256 _amount) external;
function setBufferAmount(address _token, uint256 _amount) external;
function setMaxGlobalShortSize(address _token, uint256 _amount) external;
function setInPrivateLiquidationMode(bool _inPrivateLiquidationMode) external;
function setLiquidator(address _liquidator, bool _isActive) external;
function setFundingRate(uint256 _fundingInterval, uint256 _fundingRateFactor, uint256 _stableFundingRateFactor) external;
function setFees(
uint256 _taxBasisPoints,
uint256 _stableTaxBasisPoints,
uint256 _mintBurnFeeBasisPoints,
uint256 _swapFeeBasisPoints,
uint256 _stableSwapFeeBasisPoints,
uint256 _marginFeeBasisPoints,
uint256 _liquidationFeeUsd,
uint256 _minProfitTime,
bool _hasDynamicFees
) external;
function setTokenConfig(
address _token,
uint256 _tokenDecimals,
uint256 _redemptionBps,
uint256 _minProfitBps,
uint256 _maxUsdgAmount,
bool _isStable,
bool _isShortable
) external;
function setPriceFeed(address _priceFeed) external;
function withdrawFees(address _token, address _receiver) external returns (uint256);
function directPoolDeposit(address _token) external;
function buyUSDG(address _token, address _receiver) external returns (uint256);
function sellUSDG(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 priceFeed() external view returns (address);
function fundingRateFactor() external view returns (uint256);
function stableFundingRateFactor() external view returns (uint256);
function cumulativeFundingRates(address _token) external view returns (uint256);
function getNextFundingRate(address _token) external view returns (uint256);
function getFeeBasisPoints(address _token, uint256 _usdgDelta, uint256 _feeBasisPoints, uint256 _taxBasisPoints, bool _increment) external view returns (uint256);
function liquidationFeeUsd() external view returns (uint256);
function taxBasisPoints() external view returns (uint256);
function stableTaxBasisPoints() external view returns (uint256);
function mintBurnFeeBasisPoints() external view returns (uint256);
function swapFeeBasisPoints() external view returns (uint256);
function stableSwapFeeBasisPoints() external view returns (uint256);
function marginFeeBasisPoints() external view returns (uint256);
function allWhitelistedTokensLength() external view returns (uint256);
function allWhitelistedTokens(uint256) external view returns (address);
function whitelistedTokens(address _token) external view returns (bool);
function stableTokens(address _token) external view returns (bool);
function shortableTokens(address _token) external view returns (bool);
function feeReserves(address _token) external view returns (uint256);
function globalShortSizes(address _token) external view returns (uint256);
function globalShortAveragePrices(address _token) external view returns (uint256);
function maxGlobalShortSizes(address _token) external view returns (uint256);
function tokenDecimals(address _token) external view returns (uint256);
function tokenWeights(address _token) external view returns (uint256);
function guaranteedUsd(address _token) external view returns (uint256);
function poolAmounts(address _token) external view returns (uint256);
function bufferAmounts(address _token) external view returns (uint256);
function reservedAmounts(address _token) external view returns (uint256);
function usdgAmounts(address _token) external view returns (uint256);
function maxUsdgAmounts(address _token) external view returns (uint256);
function getRedemptionAmount(address _token, uint256 _usdgAmount) external view returns (uint256);
function getMaxPrice(address _token) external view returns (uint256);
function getMinPrice(address _token) 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);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
interface IVaultUtils {
function updateCumulativeFundingRate(address _collateralToken, address _indexToken) external returns (bool);
function validateIncreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong) external view;
function validateDecreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver) external view;
function validateLiquidation(address _account, address _collateralToken, address _indexToken, bool _isLong, bool _raise) external view returns (uint256, uint256);
function getEntryFundingRate(address _collateralToken, address _indexToken, bool _isLong) external view returns (uint256);
function getPositionFee(address _account, address _collateralToken, address _indexToken, bool _isLong, uint256 _sizeDelta) external view returns (uint256);
function getFundingFee(address _account, address _collateralToken, address _indexToken, bool _isLong, uint256 _size, uint256 _entryFundingRate) external view returns (uint256);
function getBuyUsdgFeeBasisPoints(address _token, uint256 _usdgAmount) external view returns (uint256);
function getSellUsdgFeeBasisPoints(address _token, uint256 _usdgAmount) external view returns (uint256);
function getSwapFeeBasisPoints(address _tokenIn, address _tokenOut, uint256 _usdgAmount) external view returns (uint256);
function getFeeBasisPoints(address _token, uint256 _usdgDelta, uint256 _feeBasisPoints, uint256 _taxBasisPoints, bool _increment) external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "../libraries/math/SafeMath.sol";
import "../access/Governable.sol";
import "./interfaces/IShortsTracker.sol";
import "./interfaces/IVault.sol";
contract ShortsTracker is Governable, IShortsTracker {
using SafeMath for uint256;
event GlobalShortDataUpdated(address indexed token, uint256 globalShortSize, uint256 globalShortAveragePrice);
uint256 public constant MAX_INT256 = uint256(type(int256).max);
IVault public vault;
mapping (address => bool) public isHandler;
mapping (bytes32 => bytes32) public data;
mapping (address => uint256) override public globalShortAveragePrices;
bool override public isGlobalShortDataReady;
modifier onlyHandler() {
require(isHandler[msg.sender], "ShortsTracker: forbidden");
_;
}
constructor(address _vault) public {
vault = IVault(_vault);
}
function setHandler(address _handler, bool _isActive) external onlyGov {
require(_handler != address(0), "ShortsTracker: invalid _handler");
isHandler[_handler] = _isActive;
}
function _setGlobalShortAveragePrice(address _token, uint256 _averagePrice) internal {
globalShortAveragePrices[_token] = _averagePrice;
}
function setIsGlobalShortDataReady(bool value) override external onlyGov {
isGlobalShortDataReady = value;
}
function updateGlobalShortData(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
uint256 _sizeDelta,
uint256 _markPrice,
bool _isIncrease
) override external onlyHandler {
if (_isLong || _sizeDelta == 0) {
return;
}
if (!isGlobalShortDataReady) {
return;
}
(uint256 globalShortSize, uint256 globalShortAveragePrice) = getNextGlobalShortData(
_account,
_collateralToken,
_indexToken,
_markPrice,
_sizeDelta,
_isIncrease
);
_setGlobalShortAveragePrice(_indexToken, globalShortAveragePrice);
emit GlobalShortDataUpdated(_indexToken, globalShortSize, globalShortAveragePrice);
}
function getGlobalShortDelta(address _token) public view returns (bool, uint256) {
uint256 size = vault.globalShortSizes(_token);
uint256 averagePrice = globalShortAveragePrices[_token];
if (size == 0) { return (false, 0); }
uint256 nextPrice = IVault(vault).getMaxPrice(_token);
uint256 priceDelta = averagePrice > nextPrice ? averagePrice.sub(nextPrice) : nextPrice.sub(averagePrice);
uint256 delta = size.mul(priceDelta).div(averagePrice);
bool hasProfit = averagePrice > nextPrice;
return (hasProfit, delta);
}
function setInitData(address[] calldata _tokens, uint256[] calldata _averagePrices) override external onlyGov {
require(!isGlobalShortDataReady, "ShortsTracker: already migrated");
for (uint256 i = 0; i < _tokens.length; i++) {
globalShortAveragePrices[_tokens[i]] = _averagePrices[i];
}
isGlobalShortDataReady = true;
}
function getNextGlobalShortData(
address _account,
address _collateralToken,
address _indexToken,
uint256 _nextPrice,
uint256 _sizeDelta,
bool _isIncrease
) override public view returns (uint256, uint256) {
int256 realisedPnl = getRealisedPnl(_account,_collateralToken, _indexToken, _sizeDelta, _isIncrease);
uint256 averagePrice = globalShortAveragePrices[_indexToken];
uint256 priceDelta = averagePrice > _nextPrice ? averagePrice.sub(_nextPrice) : _nextPrice.sub(averagePrice);
uint256 nextSize;
uint256 delta;
// avoid stack to deep
{
uint256 size = vault.globalShortSizes(_indexToken);
nextSize = _isIncrease ? size.add(_sizeDelta) : size.sub(_sizeDelta);
if (nextSize == 0) {
return (0, 0);
}
if (averagePrice == 0) {
return (nextSize, _nextPrice);
}
delta = size.mul(priceDelta).div(averagePrice);
}
uint256 nextAveragePrice = _getNextGlobalAveragePrice(
averagePrice,
_nextPrice,
nextSize,
delta,
realisedPnl
);
return (nextSize, nextAveragePrice);
}
function getRealisedPnl(
address _account,
address _collateralToken,
address _indexToken,
uint256 _sizeDelta,
bool _isIncrease
) public view returns (int256) {
if (_isIncrease) {
return 0;
}
IVault _vault = vault;
(uint256 size, /*uint256 collateral*/, uint256 averagePrice, , , , , uint256 lastIncreasedTime) = _vault.getPosition(_account, _collateralToken, _indexToken, false);
(bool hasProfit, uint256 delta) = _vault.getDelta(_indexToken, size, averagePrice, false, lastIncreasedTime);
// get the proportional change in pnl
uint256 adjustedDelta = _sizeDelta.mul(delta).div(size);
require(adjustedDelta < MAX_INT256, "ShortsTracker: overflow");
return hasProfit ? int256(adjustedDelta) : -int256(adjustedDelta);
}
function _getNextGlobalAveragePrice(
uint256 _averagePrice,
uint256 _nextPrice,
uint256 _nextSize,
uint256 _delta,
int256 _realisedPnl
) public pure returns (uint256) {
(bool hasProfit, uint256 nextDelta) = _getNextDelta(_delta, _averagePrice, _nextPrice, _realisedPnl);
uint256 nextAveragePrice = _nextPrice
.mul(_nextSize)
.div(hasProfit ? _nextSize.sub(nextDelta) : _nextSize.add(nextDelta));
return nextAveragePrice;
}
function _getNextDelta(
uint256 _delta,
uint256 _averagePrice,
uint256 _nextPrice,
int256 _realisedPnl
) internal pure returns (bool, uint256) {
// global delta 10000, realised pnl 1000 => new pnl 9000
// global delta 10000, realised pnl -1000 => new pnl 11000
// global delta -10000, realised pnl 1000 => new pnl -11000
// global delta -10000, realised pnl -1000 => new pnl -9000
// global delta 10000, realised pnl 11000 => new pnl -1000 (flips sign)
// global delta -10000, realised pnl -11000 => new pnl 1000 (flips sign)
bool hasProfit = _averagePrice > _nextPrice;
if (hasProfit) {
// global shorts pnl is positive
if (_realisedPnl > 0) {
if (uint256(_realisedPnl) > _delta) {
_delta = uint256(_realisedPnl).sub(_delta);
hasProfit = false;
} else {
_delta = _delta.sub(uint256(_realisedPnl));
}
} else {
_delta = _delta.add(uint256(-_realisedPnl));
}
return (hasProfit, _delta);
}
if (_realisedPnl > 0) {
_delta = _delta.add(uint256(_realisedPnl));
} else {
if (uint256(-_realisedPnl) > _delta) {
_delta = uint256(-_realisedPnl).sub(_delta);
hasProfit = true;
} else {
_delta = _delta.sub(uint256(-_realisedPnl));
}
}
return (hasProfit, _delta);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
/**
* @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, 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) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* 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);
uint256 c = a - b;
return c;
}
/**
* @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) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}