# 空头科学家 -- GMX 交互

By [June](https://paragraph.com/@june023) · 2022-06-29

---

Arbitrum Odyssey 第二周的任务开始啦。搞起来！此次的教程是关于GMX的代码交互，希望能对大家有所帮助。

> 我手动撸过银河小女孩，doctor sol和最近的web3 bnb，是挺枯燥的，但其中有个我愿意做下去的很重要的原因，就是我能通过这些交互式的任务去接触到新的协议和项目。每次看到感兴趣的DEFI项目，我都会去仔细读他们的官方文档，以去了解他们的协议的底层逻辑。这让我兴奋。我现在都记得第一次看到uniswap时，发现还有这种交易模式的喜悦。这次的教程里包含了一点对项目的介绍，和我自己对项目的理解。也希望大家能多去看看这些新的项目，多去支持和拥抱感兴趣的协议。

GMX
---

*   去中心化交易所，提供主流代币的杠杆合约交易。
    
*   不是自动做市商，没有自己的价格决定机制，价格取决于外部预言机，因此零滑点。
    
    *   预言机价格 → chainlink + 主流中心化交易所（Binance, FTX and Bitfinex）
        
*   `GLP`是GMX发行的一个指数，是平台的流动性提供者代币。`GLP`池本质上是一个流动性池，用户购买`GLP`，充当trader的对手方，赚取手续费。
    
    *   如果GMX上的交易者获利，GLP持有者就会出现亏损，反之亦然。
        
*   GMX协议通过对交易的开仓和平仓收取费用，以及每隔一小时扣除杠杆头寸的 "借贷费 "来获得收入。
    
    *   协议收入（ETH） 70% 支付给 `GLP`持有者，30%支付给`GMX`质押者。
        
    *   目前 `GLP` 的 apr 32.04%，其中包括了 70%的协议收入和 `esGMX` 奖励。`esGMX` 可以和普通GMX代币一样质押，或者1年后转换成 GMX。
        

个人很喜欢这个协议。它提供了2种角色，指数投资者和交易员。两者都能获得其对应的服务。

*   指数投资者（`GLP`持有者）除了本身投资指数外，还可以享受额外的70%协议收入（ETH）
    
*   交易员可以享受0滑点以及高额杠杆（30x）。
    

但由于可以开杠杆的量是有GLP所持有的总代币数量决定的，所以资金利用率方面有一定上限。此外，由于`GLP`持有者为杠杆交易提供流动性，当杠杆交易者出现亏损时，他们将获得利润，反之亦然。所以还有有一定风险的，而整体的风险和币价是正相关的。

Arbitrum Odyssey- GMX任务
-----------------------

下面进入正题，三个任务都要做：

*   在 [https://gmx.io/trade](https://gmx.io/trade) 开一个杠杆交易的头寸
    
*   在[https://gmx.io/trade](https://gmx.io/trade) 上做交易
    
*   在 [https://gmx.io/buy](https://gmx.io/buy) 网站上进行 Mint GLP
    

首先我们来理一下逻辑。我们之前通过HOP把ETH 从polygon 跨链到了 Arbiturm。所以我们现在账户上，只有eth资产。因此我们要做的，就是接下来三件事：

1.  通过swap，把 ETH 换成 USDC
    
    1.  yield protocol 需要 USDC，可以换 50 u。
        
    2.  用 USDC 比用 ETH 购买GLP更加便宜，可以再多换点。
        
2.  开一个杠杆交易的头寸，过一段时间，把它平掉
    
3.  用USDC购买 GLP
    

Contract ABI
------------

我们做交互的底层逻辑，就是去使用这些应用/协议的合约。所以首先要找到合约地址。下面是GMX中，我们需要用的到合约地址，GMX全部地址可以看[这个链接](https://gmxio.gitbook.io/gmx/contracts)。

*   Router: [0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064](https://arbiscan.io/address/0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064)
    
*   Reader: [0x22199a49A999c351eF7927602CFB187ec3cae489](https://arbiscan.io/address/0x22199a49A999c351eF7927602CFB187ec3cae489)
    
*   PositionRouter: [0x3D6bA331e3D9702C5e8A8d254e5d8a285F223aba](https://arbiscan.io/address/0x3D6bA331e3D9702C5e8A8d254e5d8a285F223aba)
    
*   Vault: [0x489ee077994B6658eAfA855C308275EAd8097C4A](https://arbiscan.io/address/0x489ee077994B6658eAfA855C308275EAd8097C4A)
    
*   VaultPriceFeed: [0xa18bb1003686d0854ef989bb936211c59eb6e363](https://arbiscan.io/address/0xa18bb1003686d0854ef989bb936211c59eb6e363)
    

获得地址后，我们要知道怎么才能和合约进行交互。这里就要用到ABI。ABI → Application Binary Interface，应用二进制接口。\*\*ABI决定了我们如何在合同中调用函数并获得数据。\*\*以太坊智能合约是部署在以太坊区块链上的字节码。一个合约中可能有几个函数。通过ABI，我们就可以指定调用合约中的哪个函数，以及我们所期望返回的数据格式。

### 怎么获得 contract abi

1.  找到合约地址，例如，Router: [0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064](https://arbiscan.io/address/0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064)
    
2.  把合约代码复制到[remix](https://remix.ethereum.org/)里面，
    
    1.  选择合适的compiler。
        
    2.  点击编译（compile）
        
    3.  然后复制abi 到 想要的文件夹里面。我们这里是放在`abi.json`里面的。
        
3.  发现 开源合约在 [arbiscan.io](http://arbiscan.io) 最下面直接就有 abi，可以忽略 2了。
    

![](https://storage.googleapis.com/papyrus_images/c11efd5a29efdee5bab1f440def552618d33f9fef89b55828b07f07baff38079.png)

SWAP
----

逻辑和[上次](https://mirror.xyz/june023.eth/1kZknEDkUmlkDIk3vWya7r5U4QZ0C7nGLdqDffOQ0Ss)用1inch交易是一样的：

1.  连接钱包
    
2.  批准使用代币
    
3.  发送交易
    

我们需要做一些准备工作：

1.  去[Alchemy](https://www.alchemy.com/)注册个账号，申请一个免费的Arbitrum的api\_key。
    
    1.  不懂的去谷歌上搜一搜教程，应该挺多的。
        
2.  找到我们要用到的合约地址，复制ABI，并分别存成各自的文件。 上面讲过了。
    

准备工作做好了，就可以开始撸代码了。首先初始化一些，我们会用到的常量：

*   由于我们是用ETH换取USDC，所以 tokenIn 是 WETH， tokenOut 是 USDC。
    

![初始化常量](https://storage.googleapis.com/papyrus_images/f6f0335bdee2da812e816ba7efd8324c01c70a3ff51f93f1c4f9b9579b39e51b.png)

初始化常量

接下来就是 批准代币交易，此时我们要批准WETH。合约的方程都是在合约代码里面能看到的。比如`approvedPlugins` 和 `approvePlugin` ，就可以在[router](https://arbiscan.io/address/0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064)里面查看到。链接进去，点contract，点code，就可以看到了。这里这个合约是开源的，可以看到。不是开源的就得找其他办法了。

![](https://storage.googleapis.com/papyrus_images/5fd6f5d37296838dc664fa40290fd44d980157879acadfec0fa7f3a483fb3bf6.png)

下面写了个函数，估计我们能收到多少USDC

![](https://storage.googleapis.com/papyrus_images/6cd4f7d1e0c5c771b86079356ef571817d610f46d5ce2c6a3ff31f9603863467.png)

接下来就是交易了。可以看到，我设置的`minAmountOut`是预计能拿到的99%，也就是允许了1%的滑点。

![交易](https://storage.googleapis.com/papyrus_images/be53b8e34cbc928a2583bc1a953f40d841dd56cb308e133570fedf6c656a798f.png)

交易

思路
--

可能有朋友会问，我是怎么找到这些信息的，我的思路从哪里来的？

其实我一般是从结果来思考的。比如如何实现这个swap的脚本？我就先去[官方文档](https://gmxio.gitbook.io/gmx/contracts)看有没有例子。发现并没有代码例子，但是给了合约地址和简单的说明。发现用的是[router](https://arbiscan.io/address/0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064)这个合约，我就去看了一下这些合约上的交易。看到有人执行过SWAP，我就仔细去看了看它调用了什么方程，然后他的输入是什么。比如下面这个[例子](https://arbiscan.io/tx/0x868f06e31a09caa41308763e05a5d476669682ef561d55a00a340cd1f0c2fe8e)：

![](https://storage.googleapis.com/papyrus_images/e4b3adbd528f412db149424b3297534645331f85c17cdd9e7968e22134ab6575.png)

我们可以看到他是把WETH换成了USDC。

![](https://storage.googleapis.com/papyrus_images/af48eb69e0c869a01cc823e4effe63d9782cb54d21e7182998c7dcbfb7f3a201.png)

看input data，就看到了他是调用了 `swapETHToTokens` 这个函数。再把input data 解析一下，就看到了我们想要的 input 内容，知道我们需要调用`swapETHToTokens` ，并且传入下图的参数，就可以执行swap了。再去看看具体的合约代码，就知道 `swapETHToTokens` 到底是在干嘛了。

![](https://storage.googleapis.com/papyrus_images/a3a189dd6633595dc88bb431417535c88db88ef585fe7298faa80b01e30423be.png)

这里的 `_path` 就是我们的交易的路径；`_minOut` 就是我们期待的最小产出，如果低于产出，我们就不会执行交易；`_receiver` 就是最后收代币的地址。`_path` 和 `_receiver` 都是固定的，但`_minOut` 就不是。那怎么确定`_minOut`呢？一个简单的逻辑就是设置成以当前价格计价的产出的百分比。官方文档其实是有写 [reader](https://arbiscan.io/address/0x22199a49A999c351eF7927602CFB187ec3cae489) 里有一个 `getAmountOut`函数，可以获得执行前获得互换金额。这样我们就可以让 `_minOut` = `getAmountOut()`\* 百分比。

Bingo！这样，我们就可以调用 `swapETHToTokens` 函数，实现代码交互了！

杠杆交易
----

杠杆交易就稍微复杂点。同样的逻辑，我们去看官方文档。里面有说如何进行杠杆交易。

![https://gmxio.gitbook.io/gmx/contracts](https://storage.googleapis.com/papyrus_images/a07fc442066734bfdc1f6f4e3635be3355eca10452796f30104b47048e934bca.png)

https://gmxio.gitbook.io/gmx/contracts

这里的话，我是自己在官网上试了一下，我自己开仓做多ETH的话，发现项目调用的是 [PositionRouter](https://arbiscan.io/address/0x3D6bA331e3D9702C5e8A8d254e5d8a285F223aba) 这个合约，然后使用的是`createIncreasePositionETH` 这个函数。输入值破译后如下图：

![](https://storage.googleapis.com/papyrus_images/635e083ac0097364048c2168d1123196b3ae22179a072c8410ec96d0c4131273.png)

具体每个参数的解释，可以看官方文档。这里主要讲一下`_acceptablePrice`，是指的开仓时接受的最大（多头）或最小（空头）指数价格的美元值。我在`Vault.priceFeed`找到了提供价格的[合约](https://arbiscan.io/address/0x22199a49A999c351eF7927602CFB187ec3cae489)，发现了提供价格的函数 `getPrice`。所以下面写了一个函数，用来获取当前价格。

![](https://storage.googleapis.com/papyrus_images/a1a88f3e6d53bac1cece86bc96a69ff2ab6369ee2eec0550f9b180cf50e74e75.png)

接下来就是开仓了，首先准备调用 `createIncreasePositionETH` 需要用到的参数和输入。其中最近这一天， execution fee 从 0.0006e 变成了 0.002e 又变回了0.0006e，感觉是根据arbitrum 网络拥堵情况决定的。

![GMX是真的鸡贼，把 ](https://storage.googleapis.com/papyrus_images/1cc19ef46baaa726cb5e87baead16996c2e737cb7123b7fae5cc20345be36630.png)

GMX是真的鸡贼，把

`amountIn` 和 `sizeDelta` 是直接传入的输入。

*   `amountIn` 是指我们的抵押品的数量（我们这里是ETH的数量）
    
*   `sizeDelta` 是指我们想要到达的仓位大小。GMX 规定的是直接 数额 乘以 10^30，遵守就好。
    

![最小抵押品的USD值为10USD](https://storage.googleapis.com/papyrus_images/0735dfda9bec2322f7ed6ccb03718037c5d3ccbf5e82373a4264531b9da4f8dc.png)

最小抵押品的USD值为10USD

可以看到上图，我们是存入0.01ETH，开仓15美金。假设ETH=1000，存入价值为0.001\*1000 = 10美金，开仓15美金，杠杆比例就是1.5。

接下来，我们就批准一下代币使用。

![](https://storage.googleapis.com/papyrus_images/b0ace6ca69ece8df730ba6c4f5081934500726c071e39dc532e68ab5d3cb52f0.png)

最后调用 `createIncreasePositionETH` 开仓就好了。

![ ](https://storage.googleapis.com/papyrus_images/b0ace6ca69ece8df730ba6c4f5081934500726c071e39dc532e68ab5d3cb52f0.png)

开仓完成后，可以去GMX上看一下，是否有对应的仓位。我试过，没啥问题。开仓的脚本化，就完成了！接下就是平仓。

同样的逻辑，我们在router合约里找到了，平仓用的是`createDecreasePosition` 这个函数。然后去找找调用了这个函数的交易，破译input。这里[官方文档](https://gmxio.gitbook.io/gmx/contracts)讲的挺详细了，就直接用官方文档了。

![](https://storage.googleapis.com/papyrus_images/2d233494ad4b306191c40aeffb9fde54a39464619f8e9848f3dfab354bff8fa0.png)

和开仓没有什么大的区别。着重讲2个inputs。`_collateralDelta` 是指要提取的抵押品金额的USD值，因为我们是取eth出来，这里填0就好了。`_sizeDelta`是指仓位大小变化的USD值。

![输入](https://storage.googleapis.com/papyrus_images/d62bd53121e5ebc91dd139a33b4a12d4a127e7eeead744af6e02b4d67f533017.png)

输入

可以看到 `_sizeDelta` 我其实是自己写了个 `get_position` 的函数，从 reader\_contract 读取我当前仓位的总值。也就是，我实际执行的是平掉当前的仓位。

![](https://storage.googleapis.com/papyrus_images/b244beb8045eb4bae3a0cdb80b249446db0b7f0d0f8eb656915569a3261ecf74.png)

下面在获取一下当前的价格，把可接受的价格设置成当前价格的99%，也就是允许1%的滑点。因为是平多仓，所以可接受的价格应该低于当前价格。

![](https://storage.googleapis.com/papyrus_images/c08ad4fc9475f1a06c4ddf70a6464cab77e05d2db9f53d6d4d0515173c1473e4.png)

接下来就是执行交易了。

![](https://storage.googleapis.com/papyrus_images/d27ef4d10960bd32ff891b4606cb13c4108b4bc0527af1ea1ae9a80c03150cd3.png)

Mint GLP
--------

前面有提到，`GLP`是GMX发行的一个指数，是平台的流动性提供者代币。这里如何 mint GLP 就不继续展开了，大家可以尝试一下，和我交流哈哈。

最后
--

因为大家的热情，Arbitrum 的 gas fee 被干到了好高，现在Odyssey的任务也[暂停](https://twitter.com/arbitrum/status/1542159105946787840)了，刚好可以好好学习一下代码交互。

![https://dune.com/luigy/Arbitrum-Analytics](https://storage.googleapis.com/papyrus_images/2ca9bbadcf02a309ae3b0cf55a07c9f772c632c904691cf27d7683bbaee86a11.png)

https://dune.com/luigy/Arbitrum-Analytics

这个教程大概花了我3天左右的时间（=人工交互1000+个号😂），我也是一步一步自己摸索，不断地试错。但我发现其实，底层逻辑都差不多。第一就是明确要实现什么，然后去协议把整个流程走一遍。懂了用户的交互流程后，就可以去看官方的开发者文档，然后找例子。如果例子不够详细，就自己找合约，找官方的github去看解释，或者看别人或自己的交易记录，去看调用的什么合约，用的什么input，最后写写代码就出来了。当然这里面还有很多学问，包括你对项目背后逻辑的理解等。

如果有什么错误，还望大家能指出来。

合约交互的教程，我就停更在这儿了。后面有兴趣再写。希望能对大家有所帮助。Cheers！

* * *

项目代码
----

[https://github.com/June911/GMX\_Interact](https://github.com/June911/GMX_Interact)

参考链接
----

[https://ethereum.stackexchange.com/questions/234/what-is-an-abi-and-why-is-it-needed-to-interact-with-contracts](https://ethereum.stackexchange.com/questions/234/what-is-an-abi-and-why-is-it-needed-to-interact-with-contracts)

下面是一些个人链接，欢迎大家关注。

[https://twitter.com/june023\_eth](https://twitter.com/june023_eth)

[https://github.com/June911](https://github.com/June911)

---

*Originally published on [June](https://paragraph.com/@june023/gmx)*
