# web3py第四课：处理nonce常见的坑 — Mirror

By [ourens.eth](https://paragraph.com/@ourens) · 2022-01-02

---

前面几节课讲了很多内容，但是没有说nonce的几个坑，今天我就来好好说说。

关于nonce的使用建议可以参考下面群友发的内容：

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

然后我们就说说nonce。

nonce的坑
-------

如果跑过前面几课的代码，你会发现跑我的例子都能成功。当你写一个批量交易的脚本就会出问题，尤其是同一个地址发送多次请求时会遇到各种各样的问题，大部分原因就是nonce产生的问题。

我们都知道获取nonce的方法就是：\`web3.eth.getTransactionCount(address)\`

这句方法就是取你的地址下一个nonce的值是多少，但是这个方法取出来的值会有几个坑。

坑1：当你的上一次交易还没有打包完，你取到的nonce跟上一次的值是一样。

坑2：上一次交易已经成功打包了，得到的nonce值居然还跟上一次的一样。

坑3：上一次交易已经成功打包了，得到的nonce值居然还小于上一次的值。

这些坑产生的具体原因不在这里讨论了，其实我也没有去深入去查，但是有一些解决办法跟大家分享一下。

解决方案
----

我在我的代码中，都把发送交易的方法封装了，因为都是通用的代码。在里面我加上了一步，就是如果交易成功被矿工打包上链了，再返回。

这就相当于作了一种同步方式，因为这种同步的方式比较方便查错，虽然效率低了，但是开发过程比较顺畅。

首先是查询交易状态的代码：

    web3.eth.getTransactionReceipt(tx_hash)['status']
    

这句话可以根据tx来查询状态，1:完成 0：失败 其他：等待

我们判定这个返回值为1，再继续，可以用循环这种比较笨的方法改一下：

        def send_transaction(func, params):
            tx = func.buildTransaction(params)
            signed_tx = self.web3.eth.account.sign_transaction(tx, private_key=wallet_key)
            tx_hash = self.web3.eth.sendRawTransaction(signed_tx.rawTransaction)
    
            while True:
                status = get_transaction_status(tx_hash)
                if status == 1:
                    break
                elif status == 0:
                    print('失败')
                    exit()
                time.sleep(1)
    
            return tx_hash
    

经过这样的改造，你的send\_transaction方法就变成同步了，只有矿工打包之后才会返回tx。

然后我们再处理nonce，在构造params之前，你可以这样处理一下：

    global old_nonce
            nonce = self.web3.eth.getTransactionCount(self.wallet_address)
            num = 0
            while True:
                if num > 20:
                    break
                if nonce <= old_nonce and nonce != 0:
                    nonce = self.web3.eth.getTransactionCount(self.wallet_address)
                    print(f'发现nonce[{old_nonce}]重复，已重新获取。[{nonce}]')
                    time.sleep(1)
                else:
                    break
                num = num + 1
    
            params = {
              ...
            }
            old_nonce = nonce
    

我在这里存了上一次的nonce为old\_nonce，每次会判断一下是否大于小于上一次的值，如果不对继续取。注意，我这里加了num为20，是只去重试20次，20次都取不到就是别的问题了。

一般如果你把发送交易方法改成了同步，后面遇到nonce这个问题就会少很多。

最后
--

上面这几个方法都比较民科，如果有更科学的方式欢迎跟我讨论~

上面的这些方法我也经过了一些时间的验证，解决问题肯定是可以的！

这周我多发了一篇文章哦，如果下周我鸽了也很正常~

---

*Originally published on [ourens.eth](https://paragraph.com/@ourens/web3py-nonce-mirror)*
