GMX是一个衍生品交易所,可以进行做空和做多,其使用质押的方式提供流动性,流动行提供方和合约交易者是对手方,合约交易者进行杠杆操作时实际上是向流动性提供方借钱,所以合约交易者赚的越多,流动性提供者亏的越多,反之亦然。由于不是采用AMA的方式来获取交易对的价格而是直接从ChainLink和其他交易所获取的价格当作标记价格,所以交易不会存在滑点的问题。
// ...
contract Vault is ReentrancyGuard, IVault {
// ...
function buyUSDG(address _token, address _receiver) external override nonReentrant returns (uint256) {
_validateManager();
_validate(whitelistedTokens[_token], 16);
useSwapPricing = true;
uint256 tokenAmount = _transferIn(_token); // 获取转进来的token数量
_validate(tokenAmount > 0, 17);
updateCumulativeFundingRate(_token, _token); // 更新token的累计FundingRate
uint256 price = getMinPrice(_token); // 获取各个价格提供方中最小的价格
// 将token的数量转成usdg数量
uint256 usdgAmount = tokenAmount.mul(price).div(PRICE_PRECISION);
usdgAmount = adjustForDecimals(usdgAmount, _token, usdg);
_validate(usdgAmount > 0, 18);
uint256 feeBasisPoints = vaultUtils.getBuyUsdgFeeBasisPoints(_token, usdgAmount); // 获取手续费率,其会根据数量和余额有所不同
uint256 amountAfterFees = _collectSwapFees(_token, tokenAmount, feeBasisPoints); // 扣除手续费后的token数量
uint256 mintAmount = amountAfterFees.mul(price).div(PRICE_PRECISION); // 获取最终获得的最小usdg数量
mintAmount = adjustForDecimals(mintAmount, _token, usdg);
// 增加usdg的总数量和token池子中的数量
_increaseUsdgAmount(_token, mintAmount);
_increasePoolAmount(_token, amountAfterFees);
// mint出usdg给交易接受者
IUSDG(usdg).mint(_receiver, mintAmount);
emit BuyUSDG(_receiver, _token, tokenAmount, mintAmount, feeBasisPoints);
useSwapPricing = false;
return mintAmount;
}
function sellUSDG(address _token, address _receiver) external override nonReentrant returns (uint256) {
_validateManager();
_validate(whitelistedTokens[_token], 19);
useSwapPricing = true;
uint256 usdgAmount = _transferIn(usdg); // 获取转进来的usdg数量
_validate(usdgAmount > 0, 20);
updateCumulativeFundingRate(_token, _token); // 更新token的累计FundingRate
uint256 redemptionAmount = getRedemptionAmount(_token, usdgAmount); // 获取可以赎回的金额
_validate(redemptionAmount > 0, 21);
// 更新token对应置换成的usdg数量和token的余额
_decreaseUsdgAmount(_token, usdgAmount);
_decreasePoolAmount(_token, redemptionAmount);
// burn掉usdg
IUSDG(usdg).burn(address(this), usdgAmount);
// the _transferIn call increased the value of tokenBalances[usdg]
// usually decreases in token balances are synced by calling _transferOut
// however, for usdg, the tokens are burnt, so _updateTokenBalance should
// be manually called to record the decrease in tokens
_updateTokenBalance(usdg);
uint256 feeBasisPoints = vaultUtils.getSellUsdgFeeBasisPoints(_token, usdgAmount);
uint256 amountOut = _collectSwapFees(_token, redemptionAmount, feeBasisPoints); // 扣除交易手续费后的数量
_validate(amountOut > 0, 22);
_transferOut(_token, amountOut, _receiver);
emit SellUSDG(_receiver, _token, usdgAmount, amountOut, feeBasisPoints);
useSwapPricing = false;
return amountOut;
}
function swap(address _tokenIn, address _tokenOut, address _receiver) external override nonReentrant returns (uint256) {
_validate(isSwapEnabled, 23);
_validate(whitelistedTokens[_tokenIn], 24);
_validate(whitelistedTokens[_tokenOut], 25);
_validate(_tokenIn != _tokenOut, 26);
useSwapPricing = true;
// 更新累计资金费率
updateCumulativeFundingRate(_tokenIn, _tokenIn);
updateCumulativeFundingRate(_tokenOut, _tokenOut);
uint256 amountIn = _transferIn(_tokenIn); // 转入数量
_validate(amountIn > 0, 27);
uint256 priceIn = getMinPrice(_tokenIn); // 转入token的最小价格
uint256 priceOut = getMaxPrice(_tokenOut); // 转出token的最大价格
uint256 amountOut = amountIn.mul(priceIn).div(priceOut); // 计算转出token的数量
amountOut = adjustForDecimals(amountOut, _tokenIn, _tokenOut);
// adjust usdgAmounts by the same usdgAmount as debt is shifted between the assets
uint256 usdgAmount = amountIn.mul(priceIn).div(PRICE_PRECISION); // 转入token转成usdg数量
usdgAmount = adjustForDecimals(usdgAmount, _tokenIn, usdg);
uint256 feeBasisPoints = vaultUtils.getSwapFeeBasisPoints(_tokenIn, _tokenOut, usdgAmount);
uint256 amountOutAfterFees = _collectSwapFees(_tokenOut, amountOut, feeBasisPoints); // 扣除手续费后的转出token数量
// 更新tokenIn和tokenOut余额对应的usdg数量
_increaseUsdgAmount(_tokenIn, usdgAmount);
_decreaseUsdgAmount(_tokenOut, usdgAmount);
// 更新池子中tokenIn和tokenOut的数量
_increasePoolAmount(_tokenIn, amountIn);
_decreasePoolAmount(_tokenOut, amountOut);
_validateBufferAmount(_tokenOut);
_transferOut(_tokenOut, amountOutAfterFees, _receiver);
emit Swap(_receiver, _tokenIn, _tokenOut, amountIn, amountOut, amountOutAfterFees, feeBasisPoints);
useSwapPricing = false;
return amountOutAfterFees;
}
// ...
function getRedemptionAmount(address _token, uint256 _usdgAmount) public override view returns (uint256) {
uint256 price = getMaxPrice(_token); // 获取各个市场上token的最大值(及最终获取到token的数量小的方案)
// redemptionAmount = _usdgAmount / tokenPrice
uint256 redemptionAmount = _usdgAmount.mul(PRICE_PRECISION).div(price);
return adjustForDecimals(redemptionAmount, usdg, _token);
}
// ...
function updateCumulativeFundingRate(address _collateralToken, address _indexToken) public {
bool shouldUpdate = vaultUtils.updateCumulativeFundingRate(_collateralToken, _indexToken);
if (!shouldUpdate) {
return;
}
if (lastFundingTimes[_collateralToken] == 0) {
lastFundingTimes[_collateralToken] = block.timestamp.div(fundingInterval).mul(fundingInterval);
return;
}
// 距离上次更新间隔大于fundingInterval才更新
if (lastFundingTimes[_collateralToken].add(fundingInterval) > block.timestamp) {
return;
}
uint256 fundingRate = getNextFundingRate(_collateralToken);
cumulativeFundingRates[_collateralToken] = cumulativeFundingRates[_collateralToken].add(fundingRate);
lastFundingTimes[_collateralToken] = block.timestamp.div(fundingInterval).mul(fundingInterval);
emit UpdateFundingRate(_collateralToken, cumulativeFundingRates[_collateralToken]);
}
function getNextFundingRate(address _token) public override view returns (uint256) {
if (lastFundingTimes[_token].add(fundingInterval) > block.timestamp) { return 0; }
// intervals = (now - lastTime) / fundingInterval
uint256 intervals = block.timestamp.sub(lastFundingTimes[_token]).div(fundingInterval);
uint256 poolAmount = poolAmounts[_token];
if (poolAmount == 0) { return 0; }
uint256 _fundingRateFactor = stableTokens[_token] ? stableFundingRateFactor : fundingRateFactor;
// FundingRate = _fundingRateFactor * intervals * reservedAmounts/poolAmount
// 从这里可以看出,reservedAmounts/poolAmount越大,FundingRate越大
return _fundingRateFactor.mul(reservedAmounts[_token]).mul(intervals).div(poolAmount);
}
// ...
}
GitHub - gmx-io/gmx-contracts at v1
Dapp-Learning/defi/GMX at main · Dapp-Learning-DAO/Dapp-Learning