在GMX v2中 发起一笔添加流动性交易背后会发生什么(上)

  • zero
  • 更新于 2023-10-19 22:40
  • 阅读 2018

GMX V2版本上线之后短短两个月TVL做到5千万,让我们逐步解析他背后的技术

GMX协议是什么

GMX 是一种去中心化的现货和永续交易所,支持低swap费用和零价格影响交易。 交易由独特的多资产池支持,该池通过做市、swap费、杠杆交易(点差、融资费和清算)和资产再平衡赚取流动性提供者费用。V2版本上线之后短短两个月TVL做到5千万,GMX也是Arbitrum 网络 TVL 最高的Dapp ,让我们逐步解析他背后的技术。

发起一笔添加流动性的交易

这是我发起交易的交易 hash , 这笔交易通过muticall将3步操作合成一步,感兴趣的可以通过tenderly打开查看交易细节

  1. 扣除本笔交易的执行费

    通过 ExchangeRouter中的 sendWnt 方法将本笔交易的执行费先从ETH兑换成WETH 并转入 DespositVault

 // @dev Wraps the specified amount of native tokens into WNT then sends the WNT to the specified address
    function sendWnt(address receiver, uint256 amount) external payable nonReentrant {
        //校验receiver不能为零地址
        AccountUtils.validateReceiver(receiver);
        //将
        TokenUtils.depositAndSendWrappedNativeToken(dataStore, receiver, amount);
    }
  1. 将要添加流动性的金额转入指定的账户

    通过ExchangeRouter中的 sendTokens 方法将要铸流资金转入 DespositVault

    function sendTokens(address token, address receiver, uint256 amount) external payable nonReentrant {
        AccountUtils.validateReceiver(receiver);
        address account = msg.sender;
        router.pluginTransfer(token, account, receiver, amount);
    }
  1. 创建流入流动性的的订单

    通过ExchangeRouter中的createDeposit的方法经过DepositHandler的createDeposit方法最终会调到DepositUtils的createDeposit的方法。 DepositUtils的createDeposit的方法是创建订单的核心函数,接下来我们看下createDeposit方法得详细信息

    // @dev creates a deposit
    //
    // @param dataStore DataStore
    // @param eventEmitter EventEmitter
    // @param depositVault DepositVault
    // @param account the depositing account
    // @param params CreateDepositParams
    function createDeposit(
        DataStore dataStore,
        EventEmitter eventEmitter,
        DepositVault depositVault,
        address account,
        CreateDepositParams memory params
    ) external returns (bytes32) {
        //验证account地址不能为0
        AccountUtils.validateAccount(account);
        //验证这个市场是否开启
        Market.Props memory market = MarketUtils.getEnabledMarket(dataStore, params.market);
        //校验swap路径是否正确
        MarketUtils.validateSwapPath(dataStore, params.longTokenSwapPath);
        MarketUtils.validateSwapPath(dataStore, params.shortTokenSwapPath);

        // if the initialLongToken and initialShortToken are the same, only the initialLongTokenAmount would
        // be non-zero, the initialShortTokenAmount would be zero
        //如果initialLongToken和initialShortToken相同的话,initialLongTokenAmount 不为0,initialShortTokenAmount应该为0
        //recordTransferIn计算一下本次交易转进来多少代币
        uint256 initialLongTokenAmount = depositVault.recordTransferIn(params.initialLongToken);
        uint256 initialShortTokenAmount = depositVault.recordTransferIn(params.initialShortToken);

        address wnt = TokenUtils.wnt(dataStore);
        //如果是该链的治理代币的Wrap应该扣除执行费
        //检查initialLongToken和initialShortToken是否为治理代币
        //如果是就扣除执行费
        //如果不是,查看一下本笔交易转入的治理代币的金额是否够支付执行费
        //如果不够 则终止交易
        if (params.initialLongToken == wnt) {
            initialLongTokenAmount -= params.executionFee;
        } else if (params.initialShortToken == wnt) {
            initialShortTokenAmount -= params.executionFee;
        } else {
            //应该在muticall 函数将执行费计算好,进行扣除
            uint256 wntAmount = depositVault.recordTransferIn(wnt);
            if (wntAmount < params.executionFee) {
                revert Errors.InsufficientWntAmountForExecutionFee(wntAmount, params.executionFee);
            }

            params.executionFee = wntAmount;
        }
        //如果initialLongTokenAmount和initialShortTokenAmount都为0的话
        //说明没有任何金额进来这个池子,终止交易
        if (initialLongTokenAmount == 0 && initialShortTokenAmount == 0) {
            revert Errors.EmptyDepositAmounts();
        }
        //验证接收者地址不能为0
        AccountUtils.validateReceiver(params.receiver);
        //构造Deposit订单的参数
        Deposit.Props memory deposit = Deposit.Props(
            Deposit.Addresses(
                account,
                params.receiver,
                params.callbackContract,
                params.uiFeeReceiver,
                market.marketToken,
                params.initialLongToken,
                params.initialShortToken,
                params.longTokenSwapPath,
                params.shortTokenSwapPath
            ),
            Deposit.Numbers(
                initialLongTokenAmount,
                initialShortTokenAmount,
                params.minMarketTokens,
                Chain.currentBlockNumber(),
                params.executionFee,
                params.callbackGasLimit
            ),
            Deposit.Flags(params.shouldUnwrapNativeToken)
        );
        //验证不超过 gas Limit
        CallbackUtils.validateCallbackGasLimit(dataStore, deposit.callbackGasLimit());
        //预估一下执行gas
        uint256 estimatedGasLimit = GasUtils.estimateExecuteDepositGasLimit(dataStore, deposit);
        //如果执行费不够支付 gas*gasprice 则终止交易
        GasUtils.validateExecutionFee(dataStore, estimatedGasLimit, params.executionFee);
        //生成订单key
        bytes32 key = NonceUtils.getNextKey(dataStore);
        //存储到数据库
        DepositStoreUtils.set(dataStore, key, deposit);
        //触发event
        DepositEventUtils.emitDepositCreated(eventEmitter, key, deposit);

        return key;
    }

这个函数比较简单,大体逻辑就是校验参数,必须明确有足够的执行费转移到DespositVault中,然后构造铸入流动性的参数,并存储到数据库

执行订单的流程

点赞 3
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
zero
zero
0xc803...057e
江湖只有他的大名,没有他的介绍。