{"content":{"title":"在GMX v2中 发起一笔添加流动性交易背后会发生什么(上)","body":"# GMX协议是什么\r\nGMX 是一种去中心化的现货和永续交易所，支持低swap费用和零价格影响交易。 交易由独特的多资产池支持，该池通过做市、swap费、杠杆交易（点差、融资费和清算）和资产再平衡赚取流动性提供者费用。V2版本上线之后短短两个月TVL做到5千万，GMX也是Arbitrum 网络 TVL 最高的Dapp ，让我们逐步解析他背后的技术。\r\n# 发起一笔添加流动性的交易\r\n   这是我发起交易的交易[ hash](https://dashboard.tenderly.co/tx/arbitrum/0xaec854f8ab7cb722fa3b59374c5200a67ea1b5d7843c6484b0fd912e9053df47) , 这笔交易通过[muticall](https://github.com/mds1/multicall)将3步操作合成一步，感兴趣的可以通过tenderly打开查看交易细节\r\n1. ###  扣除本笔交易的执行费 \r\n    通过 ExchangeRouter中的 sendWnt 方法将本笔交易的执行费先从ETH兑换成WETH 并转入 DespositVault\r\n    \r\n```js\r\n // @dev Wraps the specified amount of native tokens into WNT then sends the WNT to the specified address\r\n    function sendWnt(address receiver, uint256 amount) external payable nonReentrant {\r\n        //校验receiver不能为零地址\r\n        AccountUtils.validateReceiver(receiver);\r\n        //将\r\n        TokenUtils.depositAndSendWrappedNativeToken(dataStore, receiver, amount);\r\n    }\r\n```\r\n2. ### 将要添加流动性的金额转入指定的账户\r\n    通过ExchangeRouter中的 sendTokens 方法将要铸流资金转入 DespositVault\r\n    \r\n```js\r\n    function sendTokens(address token, address receiver, uint256 amount) external payable nonReentrant {\r\n        AccountUtils.validateReceiver(receiver);\r\n        address account = msg.sender;\r\n        router.pluginTransfer(token, account, receiver, amount);\r\n    }\r\n```\r\n3. ### 创建流入流动性的的订单\r\n    通过ExchangeRouter中的createDeposit的方法经过DepositHandler的createDeposit方法最终会调到DepositUtils的createDeposit的方法。    DepositUtils的createDeposit的方法是创建订单的核心函数，接下来我们看下createDeposit方法得详细信息\r\n\r\n```js\r\n    // @dev creates a deposit\r\n    //\r\n    // @param dataStore DataStore\r\n    // @param eventEmitter EventEmitter\r\n    // @param depositVault DepositVault\r\n    // @param account the depositing account\r\n    // @param params CreateDepositParams\r\n    function createDeposit(\r\n        DataStore dataStore,\r\n        EventEmitter eventEmitter,\r\n        DepositVault depositVault,\r\n        address account,\r\n        CreateDepositParams memory params\r\n    ) external returns (bytes32) {\r\n        //验证account地址不能为0\r\n        AccountUtils.validateAccount(account);\r\n        //验证这个市场是否开启\r\n        Market.Props memory market = MarketUtils.getEnabledMarket(dataStore, params.market);\r\n        //校验swap路径是否正确\r\n        MarketUtils.validateSwapPath(dataStore, params.longTokenSwapPath);\r\n        MarketUtils.validateSwapPath(dataStore, params.shortTokenSwapPath);\r\n\r\n        // if the initialLongToken and initialShortToken are the same, only the initialLongTokenAmount would\r\n        // be non-zero, the initialShortTokenAmount would be zero\r\n        //如果initialLongToken和initialShortToken相同的话,initialLongTokenAmount 不为0，initialShortTokenAmount应该为0\r\n        //recordTransferIn计算一下本次交易转进来多少代币\r\n        uint256 initialLongTokenAmount = depositVault.recordTransferIn(params.initialLongToken);\r\n        uint256 initialShortTokenAmount = depositVault.recordTransferIn(params.initialShortToken);\r\n\r\n        address wnt = TokenUtils.wnt(dataStore);\r\n        //如果是该链的治理代币的Wrap应该扣除执行费\r\n        //检查initialLongToken和initialShortToken是否为治理代币\r\n        //如果是就扣除执行费\r\n        //如果不是,查看一下本笔交易转入的治理代币的金额是否够支付执行费\r\n        //如果不够 则终止交易\r\n        if (params.initialLongToken == wnt) {\r\n            initialLongTokenAmount -= params.executionFee;\r\n        } else if (params.initialShortToken == wnt) {\r\n            initialShortTokenAmount -= params.executionFee;\r\n        } else {\r\n            //应该在muticall 函数将执行费计算好,进行扣除\r\n            uint256 wntAmount = depositVault.recordTransferIn(wnt);\r\n            if (wntAmount < params.executionFee) {\r\n                revert Errors.InsufficientWntAmountForExecutionFee(wntAmount, params.executionFee);\r\n            }\r\n\r\n            params.executionFee = wntAmount;\r\n        }\r\n        //如果initialLongTokenAmount和initialShortTokenAmount都为0的话\r\n        //说明没有任何金额进来这个池子，终止交易\r\n        if (initialLongTokenAmount == 0 && initialShortTokenAmount == 0) {\r\n            revert Errors.EmptyDepositAmounts();\r\n        }\r\n        //验证接收者地址不能为0\r\n        AccountUtils.validateReceiver(params.receiver);\r\n        //构造Deposit订单的参数\r\n        Deposit.Props memory deposit = Deposit.Props(\r\n            Deposit.Addresses(\r\n                account,\r\n                params.receiver,\r\n                params.callbackContract,\r\n                params.uiFeeReceiver,\r\n                market.marketToken,\r\n                params.initialLongToken,\r\n                params.initialShortToken,\r\n                params.longTokenSwapPath,\r\n                params.shortTokenSwapPath\r\n            ),\r\n            Deposit.Numbers(\r\n                initialLongTokenAmount,\r\n                initialShortTokenAmount,\r\n                params.minMarketTokens,\r\n                Chain.currentBlockNumber(),\r\n                params.executionFee,\r\n                params.callbackGasLimit\r\n            ),\r\n            Deposit.Flags(params.shouldUnwrapNativeToken)\r\n        );\r\n        //验证不超过 gas Limit\r\n        CallbackUtils.validateCallbackGasLimit(dataStore, deposit.callbackGasLimit());\r\n        //预估一下执行gas\r\n        uint256 estimatedGasLimit = GasUtils.estimateExecuteDepositGasLimit(dataStore, deposit);\r\n        //如果执行费不够支付 gas*gasprice 则终止交易\r\n        GasUtils.validateExecutionFee(dataStore, estimatedGasLimit, params.executionFee);\r\n        //生成订单key\r\n        bytes32 key = NonceUtils.getNextKey(dataStore);\r\n        //存储到数据库\r\n        DepositStoreUtils.set(dataStore, key, deposit);\r\n        //触发event\r\n        DepositEventUtils.emitDepositCreated(eventEmitter, key, deposit);\r\n\r\n        return key;\r\n    }\r\n\r\n```\r\n这个函数比较简单，大体逻辑就是校验参数，必须明确有足够的执行费转移到DespositVault中，然后构造铸入流动性的参数，并存储到数据库\r\n\r\n# 执行订单的流程"},"author":{"user":"https://learnblockchain.cn/people/9639","address":null},"history":null,"timestamp":1697726439,"version":1}