深入 GMX V2 - LP 的质押与提取
2025-06-17 10:53
Coset
2025-06-17 10:53
订阅此专栏
收藏此文章

在永续 Dex 有两大流派,订单簿和 AMM。而 GMX 属于中间路线,使用流动性作为永续交易的保障,但价格从预言机获取。为了吸引用户提供流动性,GMX 为 LP 提供了以下收益:

  1. 手续费收入:GMX V2 中所有交易,包括 Swap,永续,以及 LP 都会产生交易手续费,这些手续费会有一部分奖励给 LP,形成旱涝保收的收入。
  2. PNL:永续 trader 和 LP 形成对手方。trader 亏的钱,会成为 LP 的收益,由所有 LP 均分,但如果 trader 赚钱,所有 LP 要共同承担这些损失。

为 GMX 提供流动性有很多好处。首先,相比为 AMM 提供流动性,LP 的资产被对半分为了 Long 和 short token,价值也随着价格线性变化,并不会有无常损失。而且 LP 的手续费的收益也很丰厚:虽然 GMX 的手续费率低,但低也意味着高交易量;而且对于永续交易,手续费是按照杠杆后的金额收取的,这就让 LP 的收入更为丰厚。最后,PNL 这块的收益也是正负都有可能,当价格出现异常波动的时候,trader 受损的概率较大,而这正是 LP 盈利的机会。

GMX 的 Pool 页面[1]列出了所有的池的 APY( 年化收益率 ),看起来这些收益率比 DEFI 的 LP 高很多,而且如果翻到最后一页,APY 看不到负值。但从理论上看,作为 trader 的对手方,LP 实际承担了被动交易的风险,因此尽管 LP 有手续费的补贴,LP 还是有亏损的可能。我们的研究也证明了这一点。

要成为 LP 非常简单,购买 GM token 即可。向交易池转入 token 后,交易池会 mint 对应 GM token 并发放,这个过程叫 Deposit。持仓期间,GM token 的数量不变,而 GM token 本身价格则会随着市场情况变化,因此仓位的净值会随着 GM 的价格变化。如果 GM token 的价格升高,那么 LP 就获得了盈利,反之 LP 会亏损。撤出流动性 (Withdraw) 的时候, GMX 会根据撤出 GM token 的数量以及价格,返还给用户对应的 long/short token。

Deposit 的时候,用户可以支付 long token 或者 short token,或者同时支付两种。而 Withdraw 后,会按照交易池中 long 和 short 的比例,同时得到的 long 和 short token。另外如果在 Withdraw 的时候选择以其它 token 支付,会触发 swap 交易。


Deposit 交易

Deposit 交易分为两步。第一步是创建 Deposit 订单,入口在 contracts/router/ExchangeRouter.sol 的 createDeposit() 函数;核心逻辑在 contracts/deposit/DepositUtils.sol 的 createDeposit() 函数。这里的操作与 swap 类似,主要是创建 Deposit 订单对象并保存。因此不再赘述,如果有需要可以参考上一篇的 swap 详解。

创建订单时,订单的一些属性是从交易的 input 参数带进来的。而执行订单时,这些值会决定订单的执行流程,比如 longTokenSwapPath。


Deposit 的流程

Deposit 请求创建后,几秒之内,Keeper 就会发现这个请求,并调用 Handler 合约。调用的入口在 contracts/exchange/DepositHandler.sol 的 executeDeposit() 函数。随后会调用 contracts/deposit/ExecuteDepositUtils.sol 的 executeDeposit() 函数。

Deposit 的主要逻辑都在 executeOrder() 中。内容包括:

  1. 从存储中删除这个订单
  2. 进行一些校验:
    • 用户地址不能为空
    • 验证是否超时
  3. 如果这个池是首次接受质押,进行一些特殊验证,防止首次抵押时,价格被操纵。
  4. 从缓存中获取价格。
  5. 更新池的 Position price impact,Funding fee,Borrowing fee
  6. 校验 pnlToPoolFactor 是否超过了最大限制,保证 market token 的价格不会太低
  7. 进行对 long token 进行 swap 操作,由于输入中的 swapPathMarkets 为空,因此这个操作相当于把资金从 Deposit vault 转到交易池中
  8. 对 short token 进行同样操作
  9. 获取质押 token 的价值,并计算 swap price impact
  10. 调用 _executeDeposit(),对 long token 进行 deposit 操作
  11. 调用_executeDeposit(),对 short token 进行 deposit 操作
  12. 验证交易池中 GM token 的数量,不能超过永续抵押的数量,也不能超过 claimableFundingFee 的数量
  13. 弹出 Deposit 执行完毕的 log
  14. 获取新的 pool value,弹出 MarketPoolValueUpdated
  15. 处理 keeper 的 gas fee

在这里 _executeDeposit()函数是 Deposit 最核心的部分。对于 long token 和 short token,这个函数会分别执行。也就意味着,如果抵押了 long 和 short,相关的 event 会抛出两次。从另一个角度说,如果只抵押 long,资产不会先 swap 成一半 long 和一半 short,而是直接兑换成 GM token。

_executeDeposit() 执行了如下操作:

  1. 计算交易手续费
  2. 根据手续费计算结果,增加池的 ClaimableFeeAmount,并抛出 ClaimableFeeAmountUpdated log
  3. 增加池的 claimableUiFeeAmount,并抛出 ClaimableUiFeeAmountUpdated log
  4. 抛出 SwapFeesCollected log,展示手续费的计算结果
  5. 获取 pool value。pool value 的含义会在后面解释。
  6. 获取池 GM token 的数量
  7. 抛出 MarketPoolValueInfo log,展示池的净值,以及净值是如何构成的
  8. 处理 price impact。Deposit 交易所操作的金额在 pool amount 中,因此这个交易会产生 swap price impact。如果交易的 price impact 是正,用户会受到奖励:
    1. 从池的 SwapImpactPool 扣除对应的 impactAmount。
    2. 根据 Pool GM token 的价格,将 price Impact 折算 GM token 数量,并添加到 mint 数量上。
    3. 将 impactAmount 对应的 long/short token 添加到 Pool Amount 中。因此这个过程相当于把 price impact 从 SwapImpactPool 取出来,然后作为 deposit 添加到 Pool amount 中,并让用户得到对应的 GM token。
    4. 验证 pool amount。
  9. 如果 price impact 是负,则用户要支付一定的代价:
    1. 向池的 SwapImpactPool 添加对应的 impactAmount。
    2. 从用户的 amountAfterFees( 扣掉手续费后的金额 ) 扣除这部分资金。也就意味着用户用来 deposit 的资金少了。
  10. 根据最终的 amountAfterFees,计算 mint 数量,如果刚才 priceImpact > 0,此时 mint 数量应该对应 amountAfterFees + price Impact。
  11. 修改 Pool amount,把 amountAfterFees(用户资产扣除手续费剩下的部分)和 feeAmountForPool( 手续费中,LP 分到的部分 ) 添加到 Pool amount 上。对应抛出 VirtualSwapInventoryUpdated 和 PoolAmountUpdated。
  12. 验证 pool value 和 pool amount。
  13. 铸造 GM token 并发放给用户。

在 Deposit 过程中,手续费是通过 SwapPricingUtils.getSwapFees() 计算的,这个函数也负责计算 swap 和 withdraw 交易的手续费。因此 Swap/Deposit/Withdraw 的手续费组成相同,都包括交易手续费和 UI fee,不同点只在于手续费费率。

可兑换 GM 的数量,是根据 deposit 的价值和 GM token 的价格计算的。用公式表示为:

公式中的分子表示存入 token 的价值,因此兑换的 GM 的数量可以理解为存入资产的价值除以 GM 的价格。从某些角度来说,GM 的数量和 long/short token 本身的价格是无关的。


PoolValue

在这里我们接触到了 PoolValue。这是一个很重要的概念。在 LP 做市期间,用户持有 GM 的数量不变。因此用户净值只受到 GM 价格影响,同时 GM 价格 = PoolValue/GM_supply。因此 PoolValue 决定了用户的净值.

GM_supply 的影响可以忽略,因为 GM_supply 的变化源于 Deposit 和 Withdraw,这两种情况下,PoolValue 都会对应的增减,因此 GM 价格不会因此改变。

再次提醒 Pool Value 仅仅是与 LP 有关,与交易池中 long/short token 的余额无关。详细解释参见本系列第一篇。

Pool value 由以下几个部分组成,

mindmap
 root((Pool value))
   pool amount
    Deposits
    fee amount
    PNL
    position price impact
   Pending borrowing fee
   Pending PNL
   Pending position price impact

用公式表示为:

Pool amount

这是构成 Pool value 中最大的部分。Pool amount 有四个来源:

  • Deposits: LP 质押的资金,这也是 Pool amount 最大的组成部分
  • fee amount: 交易池的手续费会集中到 Pool amount 中。手续费包括 swap 交易手续费,永续交易手续费,借款费。
  • PNL: 作为 trader 的对手方,每次永续交易关闭的时候,都会结算这个头寸的浮动盈亏,然后在 Pool amount 中增加或者扣除对应的金额。
  • position price impact: 永续交易的 position price impact 也会写入到 Pool amount 中。

Pending PNL

PNL 是指永续 trader 的盈亏,加上 Pending 就是指浮动盈亏。比如,Alice 投入了 10 元,加了 10 倍杠杆。因此她的未平仓合约(Open Interest)是 100 元。 如果价格上涨了 5%,那么 Alice 的未平仓合约变为 105 元,她得到了 5 元的收益。这 5 元就是 Alice 的浮盈,由于 Alice 继续持仓,这 5 元并没有真正到 Alice 的口袋里。此时对于 L P 来说,Pool amount 也不会变化。如果价格变动,PNL 会继续变化。如果 Alice 选择退出。这 5 元才真正进入她的口袋,变成 Realized PNL。而这 5 元由所有的 LP 共同支付,从 Pool amount 中扣掉 5 元。

计算 Pool value 的时候,必须将浮动盈亏计入。如果不计算的话,Pool value 会出现较大的偏差,无法体现最新的盈亏。LP 也可以利用这一点,在盈利的头寸退出之前撤出流动性以避免损失。

Pendings borrowing fee

GMX 中有三种手续费

  • 交易手续费: 只有交易时才产生,并立即转入 pool amount
  • 借款手续费: 根据时间持续增长,关闭头寸时才写入 pool amount
  • funding fee: 只在永续交易中存在,放在后面讨论。

借款手续费中,如果永续的头寸没有关闭,产生的借款手续费属于 pending borrowing fee。计算 Pool value 时要将其考虑在内。

Pending position price impact

Position price impact 是永续交易的 price impact。它是根据 long/short Open Interest 的价值差计算的 ( 而 swap price impact 是用 long/short pool amount 计算的 )。


交易解析

我们通过一个 Deposit 交易[2]来查看一下在 Deposit 过程中,金额是如何变化的。这个交易向 ETH/USDC 池 Deposit 了 0.06 ETH,得到 101.942196064798996903 GM。


更新价格

分别获取了 index,long,short token 的价格。ETH 和 USDC 价格是 2732.6860625,0.99995610343558155

OraclePriceUpdate
{
   'token': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'provider': '0xf4122df7be4ccd46d7397daf2387b3a14e53d967',
   'minPrice': 2732686062500000,
   'maxPrice': 2732686062500000,
   'timestamp': 1740042638
}
OraclePriceUpdate
{
   'token': '0xaf88d065e77c8cc2239327c5edb3a432268e5831',
   'provider': '0xf4122df7be4ccd46d7397daf2387b3a14e53d967',
   'minPrice': 999956103435581550000000,
   'maxPrice': 999956103435581550000000,
   'timestamp': 1740042638
}


更新 Pool 状态

FundingFeeAmountPerSizeUpdated
{
   'collateralToken': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'delta': 433963531506389639458865,
   'value': 135719683026404779711004323389,
   'isLong': True
}
FundingFeeAmountPerSizeUpdated
{
   'collateralToken': '0xaf88d065e77c8cc2239327c5edb3a432268e5831',
   'delta': 1185938152791311,
   'value': 383043342388995069376,
   'isLong': True
}
ClaimableFundingAmountPerSizeUpdated
{
   'collateralToken': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'delta': 159767419623047268237320,
   'value': 45337328069736428646952055190,
   'isLong': False
}
ClaimableFundingAmountPerSizeUpdated
{
   'collateralToken': '0xaf88d065e77c8cc2239327c5edb3a432268e5831',
   'delta': 854879983115699,
   'value': 426661705227560011375,
   'isLong': False
}
CumulativeBorrowingFactorUpdated
{
   'delta': 678560556517506690012486,
   'nextValue': 210027292043445526192691154857,
   'isLong': True
}
CumulativeBorrowingFactorUpdated
{'delta': 0,'nextValue': 75908042181578765082622272895,'isLong': False}

更新手续费

从这里开始执行的是 _executeDeposit()的逻辑,由于这笔交易只抵押了 ETH,所以这个函数只调用了一次。_executeDeposit() 会首先计算手续费,然后更新 ClaimableFeeAmount。我们投入了 0.06 ETH,而 Deposit 手续费是 0.007%,因此总手续费是 0.000042 ETH,而 keeper 拿走的比例是 37%,所以 ClaimableFeeAmount 会增加 0.00001554 ETH,而 Pool amount 可以获得的手续费是 0.00002646 ETH。扣除手续费后,用户的资金 (amountAfterFees) 变成了 0.059958 ETH。

ClaimableFeeAmountUpdated
{
   'token': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'delta': 15540000000000,
   'nextValue': 66651236878974250,
   'feeType': '39226eb4fed85317aa310fa53f734c7af59274c49325ab568f9c4592250e8cc5'
}
SwapFeesCollected
{
   'uiFeeReceiver': '0xff00000000000000000000000000000000000001',
   'token': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'tokenPrice': 2732686062500000,
   'feeReceiverAmount': 15540000000000,
   'feeAmountForPool': 26460000000000,
   'amountAfterFees': 59958000000000000,
   'uiFeeReceiverFactor': 0,
   'uiFeeAmount': 0,
   'tradeKey': 'c5bbdd00efe9cfd005c0378a632db5b462fb9ac2738383f214b47a43a69dd867',
   'swapFeeType': '39226eb4fed85317aa310fa53f734c7af59274c49325ab568f9c4592250e8cc5'
}

获取 PoolValue

这个 event 列出了 Pool value 的组成

MarketPoolValueInfo
{
   'longTokenAmount': 14581198436766408509216// Pool amount(long token)
   'shortTokenAmount': 38723042330186// Pool amount(short token)
   'longTokenUsd': 39845837742698352101315966002000000000// 对应的 value(单位是 usd)
   'shortTokenUsd': 38721342521663874624056029668300000000,
   'totalBorrowingFees': 164585447661688504765945136126227828// pending borrowing fee
   'borrowingFeePoolFactor': 630000000000000000000000000000// borrowing fee 中,属于 LP 的分成
   'impactPoolAmount': 1513877822894491761468// position price impact
   'marketTokensSupply': 46335546856513040887008845// 交易池中, GM 的 supply
   'poolValue': 74438257384492383196123272286561465605// pool value
   'longPnl': -1359170844035244179078080015350033186// 做多方的浮动盈亏
   'shortPnl': 1454829728980330421744670695098091112// 做空方的浮动盈亏
   'netPnl': 95658884945086242666590679748057926// 总浮动盈亏,netPnl = longPnl + shortPnl
   'tradeKey': 'c5bbdd00efe9cfd005c0378a632db5b462fb9ac2738383f214b47a43a69dd867'
}

处理 Price Impact

这笔交易的 Price impact 是负的,因此 SwapImpactPoolAmount 被加上了 0.000027741916663722 ETH。

SwapImpactPoolAmountUpdated
{
   'token': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'nextValue': 27741916663722,
   'delta': 27741916663722
}

更新 Pool amount

amountAfterFees(0.059958 ETH) + feeAmountForPool(0.00002646 ETH) = 0.05998446ETH,由于 priceImpact 是负,所以还要减去 priceImpact(0.000027741916663722 ETH),最终 Pool amount 增加的值是 0.059956718083336278 ETH。

VirtualSwapInventoryUpdated
{
   'nextValue': 16831165930803339890351,
   'delta': 59956718083336278,
   'isLongToken': True,
   'virtualMarketId': 'f5134a0a1379cd7f246d7a04d2463c57aa177bf09a34e93dafc5e768c05cea63'
}
PoolAmountUpdated
{
   'token': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'nextValue': 14581258393484491845494,
   'delta': 59956718083336278
}

结束 Deposit

根据 PoolValue 和 GM TotalSupply,GM 的价格是 1.607213638554。因此可计算 GM 的数量为 0.059956718083336278 * 2732.686062500000 / 1.607213638554 = 101.942196064798996903

DepositExecuted
{
   'account': '0x784f8b525d652a83e4ae85e573c31f17f2dbca0d',
   'longTokenAmount': 60000000000000000,
   'shortTokenAmount': 0,
   'receivedMarketTokens': 101942196064798996903,
   'swapPricingType': 3,
   'key': 'c5bbdd00efe9cfd005c0378a632db5b462fb9ac2738383f214b47a43a69dd867'
}
MarketPoolValueUpdated
{
   'longTokenAmount': 14581258393484491845494,
   'shortTokenAmount': 38723042330186,
   'longTokenUsd': 39846001585586211676076357227375000000,
   'shortTokenUsd': 38721342521663874624056029668300000000,
   'totalBorrowingFees': 164585447661688504765945136126227828,
   'borrowingFeePoolFactor': 630000000000000000000000000000,
   'impactPoolAmount': 1513877822894491761468,
   'marketTokensSupply': 46335648798709105686005748,
   'poolValue': 74438421227380242770883663511936465605,
   'longPnl': -1359170844035244179078080015350033186,
   'shortPnl': 1454829728980330421744670695098091112,
   'netPnl': 95658884945086242666590679748057926,
   'actionType': '607991fc5963e264f1a94faa126c63482fdc5af14a656f08751dc8b0c5d47630',
   'tradeKey': 'c5bbdd00efe9cfd005c0378a632db5b462fb9ac2738383f214b47a43a69dd867'
}

处理 Keeper 的 gas fee

KeeperExecutionFee
{
   'keeper': '0x8e66ee36f2c7b9461f50aa0b53ef0e4e47f4abbf',
   'executionFeeAmount': 29738540000000
}
ExecutionFeeRefund
{
   'receiver': '0x784f8b525d652a83e4ae85e573c31f17f2dbca0d',
   'refundFeeAmount': 10256311000000
}

Withdraw 交易

Withdraw 交易要经过 CreateWithdraw,ExecuteWithdraw 的过程,这里从 ExecuteWithdraw 开始介绍。ExecuteWithdraw 的核心逻辑在 contracts/withdrawal/ExecuteWithdrawalUtils.sol 的 executeWithdrawal() 函数。内容包括:

  1. 从存储中删除这个订单。
  2. 校验 Withdraw 任务。
    1. 地址是否是空。
    2. 提取的 GM token 数量是否为 0。
    3. 获取价格的时间是否大于订单时间。
    4. 是否超时。
  3. 更新池的 Position price impact,Funding fee,Borrowing fee。
  4. 获取池的 GM token supply,并校验提取 GM 数量不超过总量。
  5. 调用 _executeWithdrawal(),执行核心逻辑。
  6. 弹出 WithdrawalExecuted log。
  7. 调用 Withdraw 的回调函数。
  8. 支付 Keeper 的 gas fee。

_executeWithdrawal()是核心逻辑,内容包括

  1. 获取输出 long/short token 的数量。
  2. 计算手续费。
  3. 对于 long token:
    1. 增加池的 ClaimableFeeAmount,并抛出 ClaimableFeeAmountUpdated log。
    2. 增加池的 claimableUiFeeAmount,并抛出 ClaimableUiFeeAmountUpdated log。
  4. 对于 short token:
    1. 增加池的 ClaimableFeeAmount,并抛出 ClaimableFeeAmountUpdated log。
    2. 增加池的 claimableUiFeeAmount,并抛出 ClaimableUiFeeAmountUpdated log。
  5. 修改 long/short pool amount,扣除用户提走的数量,加上交易手续费。
  6. 校验。
  7. Burn 掉对应的 GM token。
  8. 如果需要提取成另外的 token,进行 swap(withdraw 的时候用户可以在界面上选择提取成其它 token)。
  9. 弹出 SwapFeesCollected(long/short 各一次 )。
  10. 验证 GM token。
  11. 弹出 MarketPoolValueUpdated。

相比 Deposit,withdraw 的逻辑更简单。首先它输入输出都是确定的,是从 GM token 到 long/short token,而且 long/short token 的比例也是固定的。另一方面,Withdraw 过程不涉及 price impact。

Withdraw 的一个核心问题是输出 token 数量的计算。主要代码如下:

uint256 longTokenPoolAmount = MarketUtils.getPoolAmount(params.dataStore,market,market.longToken);
uint256 shortTokenPoolAmount = MarketUtils.getPoolAmount(params.dataStore,market,market.shortToken);

uint256 longTokenPoolUsd = longTokenPoolAmount * prices.longTokenPrice.max;
uint256 shortTokenPoolUsd = shortTokenPoolAmount * prices.shortTokenPrice.max;

uint256 totalPoolUsd = longTokenPoolUsd + shortTokenPoolUsd;

uint256 marketTokensUsd = MarketUtils.marketTokenAmountToUsd(marketTokenAmount,poolValue,marketTokensSupply);

uint256 longTokenOutputUsd = Precision.mulDiv(marketTokensUsd,longTokenPoolUsd,totalPoolUsd);
uint256 shortTokenOutputUsd = Precision.mulDiv(marketTokensUsd,shortTokenPoolUsd,totalPoolUsd);

return (longTokenOutputUsd / prices.longTokenPrice.max,shortTokenOutputUsd / prices.shortTokenPrice.max);

看起来很复杂,其实用三句话就可以概括:

  • 计算 Withdraw amount 对应的 usd 价值 (withdraw value)。
  • withdraw value 按照 long/short pool amount 的比例分为两份,注意这里只取了 Pool amount,和 pool value 以及那些 pending pnl 无关。
  • 对于 long 或者 short,用价值除以价格,得到数量。

另一个需要关注的问题是手续费,Withdraw 的手续费是对 long/short 分别计算的。依据的数量是要提取的 long/short token 数量。因此手续费相关的 log 会弹出两次。


交易解析

这次的交易[3]是刚才 Deposit 例子的反向操作,将之前提供的流动性全部撤出。


获取价格

ETH 价格是 2757.646069115846,USDC 价格是 0.9999491847023574

OraclePriceUpdate
{
   'token': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'provider': '0xf4122df7be4ccd46d7397daf2387b3a14e53d967',
   'minPrice': 2757646069115846,
   'maxPrice': 2757646069115846,
   'timestamp': 1740367285
}
OraclePriceUpdate
{
   'token': '0xaf88d065e77c8cc2239327c5edb3a432268e5831',
   'provider': '0xf4122df7be4ccd46d7397daf2387b3a14e53d967',
   'minPrice': 999949184702357400000000,
   'maxPrice': 999949184702357400000000,
   'timestamp': 1740367285
}

更新 pool 状态

FundingFeeAmountPerSizeUpdated
{
   'collateralToken': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'delta': 315147308308354845922569,
   'value': 136158557938725131078937604204,
   'isLong': True
}
FundingFeeAmountPerSizeUpdated
{
   'collateralToken': '0xaf88d065e77c8cc2239327c5edb3a432268e5831',
   'delta': 869108899976411,
   'value': 384251356311993812405,
   'isLong': True
}
ClaimableFundingAmountPerSizeUpdated
{
   'collateralToken': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'delta': 146079052316155554786912,
   'value': 45475736545527286670278070823,
   'isLong': False
}
ClaimableFundingAmountPerSizeUpdated
{
   'collateralToken': '0xaf88d065e77c8cc2239327c5edb3a432268e5831',
   'delta': 541344442245701,
   'value': 427508916331792735890,
   'isLong': False
}
CumulativeBorrowingFactorUpdated
{
   'delta': 1332349347452612744596560,
   'nextValue': 210776588514536258327498291339,
   'isLong': True
}
CumulativeBorrowingFactorUpdated
{'delta': 0,'nextValue': 76449792294630294845444791584,'isLong': False}

计算 OutputAmount

这里对应 _getOutputAmounts()函数中的 MarketEventUtils.emitMarketPoolValueInfo,

这里可以顺便计算提走 Token 的数量。通过 PoolValue/imarketTokensSupply,可以求出 GM 的价格是 1.613566649968。而 withdraw 的数量是 101.942196064798996903 GM,那么 withdraw value 是 164.49052779465875 USD,而 long token 的价值比例是 longRate = longTokenUsd/(longTokenUsd+shortTokenUsd)  = 0.4917655517939715624,因此 long token 的数量就是 withdrawValue * longRate / longPrice = 0.029333269440111450 ETH。同理,可以求出 USDC 的数量是 83.604000 USDC。

MarketPoolValueInfo
{
   'longTokenAmount': 14713315747123213021818,
   'shortTokenAmount': 41935048079640,
   'longTokenUsd': 40574117333714605224307254669167528028,
   'shortTokenUsd': 41932917137690176351850943336000000000,
   'totalBorrowingFees': 177022803925263014518653812715415229,
   'borrowingFeePoolFactor': 630000000000000000000000000000,
   'impactPoolAmount': 1623742999781266174555,
   'marketTokensSupply': 48302001888095982887001991,
   'poolValue': 77938499373339749897351929186940519018,
   'longPnl': -1773290622442868312144474004895477993,
   'shortPnl': 1975641586379635032440570589580700067,
   'netPnl': 202350963936766720296096584685222074,
   'tradeKey': '88995479b95a3584123b5e47e10c4642483a763b136016c94a7709a858ce777b'
}

更新 keeper 手续费

对于 long/short token,各抛出一次。

其中 long 手续费: 0.029333269440111450 * 0.0007 * 0.37 = 0.000007597316784988 ETH

short 手续费: 83.604000 USDC * 0.0007 * 0.37 = 0.021653 USDC

ClaimableFeeAmountUpdated
{
   'token': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'delta': 7597316784988,
   'nextValue': 4232585045842441550,
   'feeType': 'da1ac8fcb4f900f8ab7c364d553e5b6b8bdc58f74160df840be80995056f3838'
}
ClaimableFeeAmountUpdated
{
   'token': '0xaf88d065e77c8cc2239327c5edb3a432268e5831',
   'delta': 21653,
   'nextValue': 54884604417,
   'feeType': 'da1ac8fcb4f900f8ab7c364d553e5b6b8bdc58f74160df840be80995056f3838'
}

更新 pool amount

pool amount 的变化量 = - LP 提走的数量 + 手续费收入

  • longTokenOutputAmount (0.029333269440111450 ETH) - longTokenFees.feeAmountForPool(0.000012935971823090 ETH) = -0.02932033346828836 ETH,
  • shortTokenOutputAmount(83.604000 USDC) - shortTokenFees.feeAmountForPool(0.036869 USDC) = -83.567131 USDC
VirtualSwapInventoryUpdated
{
   'nextValue': 16782607705924140082566,
   'delta': -29320333468288360,
   'isLongToken': True,
   'virtualMarketId': 'f5134a0a1379cd7f246d7a04d2463c57aa177bf09a34e93dafc5e768c05cea63'
}
PoolAmountUpdated
{
   'token': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'nextValue': 14713286426789744733458,
   'delta': -29320333468288360
}
VirtualSwapInventoryUpdated
{
   'nextValue': 47608956594685,
   'delta': -83567131,
   'isLongToken': False,
   'virtualMarketId': 'f5134a0a1379cd7f246d7a04d2463c57aa177bf09a34e93dafc5e768c05cea63'
}
PoolAmountUpdated
{
   'token': '0xaf88d065e77c8cc2239327c5edb3a432268e5831',
   'nextValue': 41934964512509,
   'delta': -83567131
}

手续费计算结果

SwapFeesCollected
{
   'uiFeeReceiver': '0xff00000000000000000000000000000000000001',
   'token': '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
   'tokenPrice': 2757646069115846,
   'feeReceiverAmount': 7597316784988,
   'feeAmountForPool': 12935971823090,
   'amountAfterFees': 29312736151503372,
   'uiFeeReceiverFactor': 0,
   'uiFeeAmount': 0,
   'tradeKey': '88995479b95a3584123b5e47e10c4642483a763b136016c94a7709a858ce777b',
   'swapFeeType': 'da1ac8fcb4f900f8ab7c364d553e5b6b8bdc58f74160df840be80995056f3838'
}
SwapFeesCollected
{
   'uiFeeReceiver': '0xff00000000000000000000000000000000000001',
   'token': '0xaf88d065e77c8cc2239327c5edb3a432268e5831',
   'tokenPrice': 999949184702357400000000,
   'feeReceiverAmount': 21653,
   'feeAmountForPool': 36869,
   'amountAfterFees': 83545478,
   'uiFeeReceiverFactor': 0,
   'uiFeeAmount': 0,
   'tradeKey': '88995479b95a3584123b5e47e10c4642483a763b136016c94a7709a858ce777b',
   'swapFeeType': 'da1ac8fcb4f900f8ab7c364d553e5b6b8bdc58f74160df840be80995056f3838'
}

完成

这里弹出新的 PoolValue,并宣告交易完成:

MarketPoolValueUpdated
{
   'longTokenAmount': 14713286426789744733458,
   'shortTokenAmount': 41934964512509,
   'longTokenUsd': 40574036478612271233131791244994175468,
   'shortTokenUsd': 41932833574805664986754088716600000000,
   'totalBorrowingFees': 177022803925263014518653812715415229,
   'borrowingFeePoolFactor': 630000000000000000000000000000,
   'impactPoolAmount': 1623742999781266174555,
   'marketTokensSupply': 48301899945899918088005088,
   'poolValue': 77938334955352904541079611143367166458,
   'longPnl': -1773290622442868312144474004895477993,
   'shortPnl': 1975641586379635032440570589580700067,
   'netPnl': 202350963936766720296096584685222074,
   'actionType': '04aaf2da749cf1350350f3e1b892fb52d87fb0ed285b814e5f37599977fceb42',
   'tradeKey': '88995479b95a3584123b5e47e10c4642483a763b136016c94a7709a858ce777b'
}
WithdrawalExecuted
{
   'account': '0x784f8b525d652a83e4ae85e573c31f17f2dbca0d',
   'swapPricingType': 4,
   'key': '88995479b95a3584123b5e47e10c4642483a763b136016c94a7709a858ce777b'
}

处理 keeper 的 gas

KeeperExecutionFee
{
   'keeper': '0x6a2b3a13be0c723674bcfd722d4e133b3f356e05',
   'executionFeeAmount': 29939450000000
}
ExecutionFeeRefund
{
   'receiver': '0x784f8b525d652a83e4ae85e573c31f17f2dbca0d',
   'refundFeeAmount': 4592151000000
}


总结

本文详细介绍了 Deposit 和 Withdraw 交易,以及 Pool Value 的计算。至此,LP 的主要交易类型就介绍完了 ( 这里漏掉了 shift 交易,它是 withdraw 和 deposit 的组合,所以跳过 )。对于 GMX 来说,永续交易才是最重要的内容。下一篇就会开始介绍永续的 Increaes Position 交易。


往期阅读

Uniswap 系列研究

Zelos 小组产出


参考资料
[1] 

Pool 页面: https://app.gmx.io/#/pools

[2] 

Deposit 交易: https://arbiscan.io/tx/0x6e099d65153758cb01927b9c5a667c7dca0752b546546800ad6026a04b6094a7

[3] 

这次的交易: https://arbiscan.io/tx/0x98aafbaa46659b5bb9f049807c94b8ec3ec55f9a3eb1855e2d02650793338a9e#eventlog


Coset 

致力于促进不同个体之间有效的、深度的交流与协作,激发更多创新和创造。

关注我们的社交媒体,了解更多动态:

Website:https://coset.io/ 

Twitter:https://twitter.com/coset_io

Telegram:https://t.me/coset_io

Youtube:www.youtube.com/@coset_io
Contact:emily@coset.io


【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

Coset
数据请求中
查看更多

推荐专栏

数据请求中
在 App 打开