上一篇文章:
这周我们详细解析一下上次GMX文章的几个关键合约:Vault、VaultPriceFeed和FastPriceFeed。
虽然Notion没有一键三连)
Vault合约是GMX最核心的合约,管理了资金池(Pool)、仓位(Position)等,部署在:GMX: Vault | Address 0x489ee077994B6658eAfA855C308275EAd8097C4A | Arbiscan(Arbitrum)
increasePosition首先我们来详细分析下增加仓位的代码:(为了让代码更简洁,我把源码改写为Solidity 8.0+的版本,即将SafeMath的函数改回运算符)
function increasePosition(address _account, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong) external nonReentrant {
_validate(isLeverageEnabled, 28);
_validateGasPrice();
_validateRouter(_account);
_validateTokens(_collateralToken, _indexToken, _isLong);
vaultUtils.validateIncreasePosition(_account, _collateralToken, _indexToken, _sizeDelta, _isLong);
updateCumulativeFundingRate(_collateralToken, _indexToken);
bytes32 key = getPositionKey(_account, _collateralToken, _indexToken, _isLong);
Position storage position = positions[key];
uint256 price = _isLong ? vault().getMaxPrice(_indexToken) : vault().getMinPrice(_indexToken);
if (position.size == 0) {
position.averagePrice = price;
}
if (position.size > 0 && _sizeDelta > 0) {
position.averagePrice = getNextAveragePrice(_indexToken, position.size, position.averagePrice, _isLong, price, _sizeDelta, position.lastIncreasedTime);
}
uint256 fee = _collectMarginFees(_account, _collateralToken, _indexToken, _isLong, _sizeDelta, position.size, position.entryFundingRate);
uint256 collateralDelta = _transferIn(_collateralToken);
uint256 collateralDeltaUsd = tokenToUsdMin(_collateralToken, collateralDelta);
position.collateral = position.collateral + collateralDeltaUsd;
_validate(position.collateral >= fee, 29);
position.collateral = position.collateral - fee;
position.entryFundingRate = getEntryFundingRate(_collateralToken, _indexToken, _isLong);
position.size = position.size + _sizeDelta;
position.lastIncreasedTime = block.timestamp;
_validate(position.size > 0, 30);
_validatePosition(position.size, position.collateral);
validateLiquidation(_account, _collateralToken, _indexToken, _isLong, true);
// reserve tokens to pay profits on the position
uint256 reserveDelta = usdToTokenMax(_collateralToken, _sizeDelta);
position.reserveAmount = position.reserveAmount + reserveDelta;
_increaseReservedAmount(_collateralToken, reserveDelta);
if (_isLong) {
// guaranteedUsd stores the sum of (position.size - position.collateral) for all positions
// if a fee is charged on the collateral then guaranteedUsd should be increased by that fee amount
// since (position.size - position.collateral) would have increased by `fee`
_increaseGuaranteedUsd(_collateralToken, _sizeDelta + fee);
_decreaseGuaranteedUsd(_collateralToken, collateralDeltaUsd);
// treat the deposited collateral as part of the pool
_increasePoolAmount(_collateralToken, collateralDelta);
// fees need to be deducted from the pool since fees are deducted from position.collateral
// and collateral is treated as part of the pool
_decreasePoolAmount(_collateralToken, usdToTokenMin(_collateralToken, fee));
} else {
if (globalShortSizes[_indexToken] == 0) {
globalShortAveragePrices[_indexToken] = price;
} else {
globalShortAveragePrices[_indexToken] = getNextGlobalShortAveragePrice(_indexToken, price, _sizeDelta);
}
_increaseGlobalShortSize(_indexToken, _sizeDelta);
}
emit IncreasePosition(key, _account, _collateralToken, _indexToken, collateralDeltaUsd, _sizeDelta, _isLong, price, fee);
emit UpdatePosition(key, position.size, position.collateral, position.averagePrice, position.entryFundingRate, position.reserveAmount, position.realisedPnl, price);
}
该函数可以分为三个部分:
至此,increasePosition函数解析完毕。
decreasePosition接下来是减少仓位的代码:
function decreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver) external nonReentrant returns (uint256) {
_validateGasPrice();
_validateRouter(_account);
return _decreasePosition(_account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, _receiver);
}
function _decreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver) private returns (uint256) {
vaultUtils.validateDecreasePosition(_account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, _receiver);
updateCumulativeFundingRate(_collateralToken, _indexToken);
bytes32 key = getPositionKey(_account, _collateralToken, _indexToken, _isLong);
Position storage position = positions[key];
_validate(position.size > 0, 31);
_validate(position.size >= _sizeDelta, 32);
_validate(position.collateral >= _collateralDelta, 33);
uint256 collateral = position.collateral;
// scrop variables to avoid stack too deep errors
{
uint256 reserveDelta = position.reserveAmount * _sizeDelta / position.size;
position.reserveAmount = position.reserveAmount - reserveDelta;
_decreaseReservedAmount(_collateralToken, reserveDelta);
}
(uint256 usdOut, uint256 usdOutAfterFee) = _reduceCollateral(_account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong);
if (position.size != _sizeDelta) {
position.entryFundingRate = getEntryFundingRate(_collateralToken, _indexToken, _isLong);
position.size = position.size - _sizeDelta;
_validatePosition(position.size, position.collateral);
validateLiquidation(_account, _collateralToken, _indexToken, _isLong, true);
if (_isLong) {
_increaseGuaranteedUsd(_collateralToken, collateral - position.collateral);
_decreaseGuaranteedUsd(_collateralToken, _sizeDelta);
}
uint256 price = _isLong ? vault().getMinPrice(_indexToken) : vault().getMaxPrice(_indexToken);
emit DecreasePosition(key, _account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, price, usdOut - usdOutAfterFee);
emit UpdatePosition(key, position.size, position.collateral, position.averagePrice, position.entryFundingRate, position.reserveAmount, position.realisedPnl, price);
} else {
if (_isLong) {
_increaseGuaranteedUsd(_collateralToken, collateral);
_decreaseGuaranteedUsd(_collateralToken, _sizeDelta);
}
uint256 price = _isLong ? vault().getMinPrice(_indexToken) : vault().getMaxPrice(_indexToken);
emit DecreasePosition(key, _account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, price, usdOut - usdOutAfterFee);
emit ClosePosition(key, position.size, position.collateral, position.averagePrice, position.entryFundingRate, position.reserveAmount, position.realisedPnl);
delete positions[key];
}
if (!_isLong) {
_decreaseGlobalShortSize(_indexToken, _sizeDelta);
}
if (usdOut > 0) {
if (_isLong) {
_decreasePoolAmount(_collateralToken, usdToTokenMin(_collateralToken, usdOut));
}
uint256 amountOutAfterFees = usdToTokenMin(_collateralToken, usdOutAfterFee);
_transferOut(_collateralToken, amountOutAfterFees, _receiver);
return amountOutAfterFees;
}
return 0;
}