<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>point</title>
        <link>https://paragraph.com/@point</link>
        <description>undefined</description>
        <lastBuildDate>Fri, 17 Apr 2026 04:10:05 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[rerrwerq]]></title>
            <link>https://paragraph.com/@point/rerrwerq</link>
            <guid>Bvv3zC8HaxawJnYv9uYO</guid>
            <pubDate>Wed, 02 Nov 2022 05:37:50 GMT</pubDate>
            <description><![CDATA[https://exnew.finding3.io/jdDetail?id=181&color-theme=light&desc=&has-h-padding=true&has-v-padding=true&modules=comment&receiver=0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29&target_uri=baidu.com&height=800&display=iframehttp:/exnew.finding3.io/jdDetail?id=181&#x26;color-theme=light&#x26;desc=&#x26;has-h-padding=true&#x26;has-v-padding=true&#x26;modules=comment&#x26;receiver=0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29&#x26;target_uri=baidu.com&#x26;height=800&#x26;display=iframe]]></description>
            <content:encoded><![CDATA[<p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://exnew.finding3.io/jdDetail?id=181&amp;color-theme=light&amp;desc=&amp;has-h-padding=true&amp;has-v-padding=true&amp;modules=comment&amp;receiver=0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29&amp;target_uri=baidu.com&amp;height=800&amp;display=iframe">https://exnew.finding3.io/jdDetail?id=181&amp;color-theme=light&amp;desc=&amp;has-h-padding=true&amp;has-v-padding=true&amp;modules=comment&amp;receiver=0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29&amp;target_uri=baidu.com&amp;height=800&amp;display=iframe</a></p><pre data-type="codeBlock" text="http:/exnew.finding3.io/jdDetail?id=181&amp;color-theme=light&amp;desc=&amp;has-h-padding=true&amp;has-v-padding=true&amp;modules=comment&amp;receiver=0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29&amp;target_uri=baidu.com&amp;height=800&amp;display=iframe
"><code>http:<span class="hljs-operator">/</span>exnew.finding3.io/jdDetail?id<span class="hljs-operator">=</span><span class="hljs-number">181</span><span class="hljs-operator">&#x26;</span>color<span class="hljs-operator">-</span>theme<span class="hljs-operator">=</span>light<span class="hljs-operator">&#x26;</span>desc<span class="hljs-operator">=</span><span class="hljs-operator">&#x26;</span>has<span class="hljs-operator">-</span>h<span class="hljs-operator">-</span>padding<span class="hljs-operator">=</span><span class="hljs-literal">true</span><span class="hljs-operator">&#x26;</span>has<span class="hljs-operator">-</span>v<span class="hljs-operator">-</span>padding<span class="hljs-operator">=</span><span class="hljs-literal">true</span><span class="hljs-operator">&#x26;</span>modules<span class="hljs-operator">=</span>comment<span class="hljs-operator">&#x26;</span>receiver<span class="hljs-operator">=</span><span class="hljs-number">0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29</span><span class="hljs-operator">&#x26;</span>target_uri<span class="hljs-operator">=</span>baidu.com&#x26;height<span class="hljs-operator">=</span><span class="hljs-number">800</span><span class="hljs-operator">&#x26;</span>display<span class="hljs-operator">=</span>iframe
</code></pre>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[kkkkk]]></title>
            <link>https://paragraph.com/@point/kkkkk</link>
            <guid>Itm4WBRkUx3PgtJqn0mk</guid>
            <pubDate>Tue, 01 Nov 2022 15:09:59 GMT</pubDate>
            <description><![CDATA[https://embed.0xecho.com.ipns.page&color-theme=light&desc=&has-h-padding=true&has-v-padding=true&modules=comment&display=iframe https://www.ezswap.io&display=iframe 热武器热武器若发的说法 https://www.ezswap.io?color-theme=light&desc=&has-h-padding=true&has-v-padding=true&modules=comment&receiver=0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29&target_uri=baidu.com&height=800&display=iframe]]></description>
            <content:encoded><![CDATA[<p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://embed.0xecho.com.ipns.page&amp;color-theme=light&amp;desc=&amp;has-h-padding=true&amp;has-v-padding=true&amp;modules=comment&amp;display=iframe">https://embed.0xecho.com.ipns.page&amp;color-theme=light&amp;desc=&amp;has-h-padding=true&amp;has-v-padding=true&amp;modules=comment&amp;display=iframe</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.ezswap.io&amp;display=iframe">https://www.ezswap.io&amp;display=iframe</a></p><p>热武器热武器若发的说法</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.ezswap.io?color-theme=light&amp;desc=&amp;has-h-padding=true&amp;has-v-padding=true&amp;modules=comment&amp;receiver=0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29&amp;target_uri=baidu.com&amp;height=800&amp;display=iframe">https://www.ezswap.io?color-theme=light&amp;desc=&amp;has-h-padding=true&amp;has-v-padding=true&amp;modules=comment&amp;receiver=0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29&amp;target_uri=baidu.com&amp;height=800&amp;display=iframe</a></p>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[开源合约的二三事]]></title>
            <link>https://paragraph.com/@point/00TfGIcl573oP8GAkreu</link>
            <guid>00TfGIcl573oP8GAkreu</guid>
            <pubDate>Sun, 25 Sep 2022 06:04:59 GMT</pubDate>
            <description><![CDATA[写完代码后，请务必确定下项目是否要开源。如果不确定，请在测试环境尝试开源开源大概率会因为这啊那啊的事情导致问题，不能及时开源。血泪的教训，还不止一次。哭开源方法：我比较喜欢的是标准json开源。如果是remix部署，请删除artifacts文件夹重新编译。然后获取build-info文件夹里的图片中的json如果无法开源，看下构造函数中是否有复杂的参数，不如list。有的话请使用这个网站构造参数的abi-encode https://abi.hashex.org/# 然后填入这里]]></description>
            <content:encoded><![CDATA[<ol><li><p>写完代码后，请务必确定下项目是否要开源。如果不确定，请在测试环境尝试开源</p></li><li><p>开源大概率会因为这啊那啊的事情导致问题，不能及时开源。血泪的教训，还不止一次。哭</p></li></ol><p>开源方法：</p><ol><li><p>我比较喜欢的是标准json开源。如果是remix部署，请删除artifacts文件夹重新编译。然后获取build-info文件夹里的图片中的json</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0bbca6e2ae466cc9df2078c42d1fdb6fca84e5d0f62783ee7ea268342e8ba66b.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>如果无法开源，看下构造函数中是否有复杂的参数，不如list。有的话请使用这个网站构造参数的abi-encode</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://abi.hashex.org/#">https://abi.hashex.org/#</a></p><p>然后填入这里</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1d161cc567dbd2d21868288928cb1bea92f9693f02627b5a7945dfebae011fd3.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[记录一次盲盒项目经验]]></title>
            <link>https://paragraph.com/@point/GrcfcnxgXSvbcTRdLDpE</link>
            <guid>GrcfcnxgXSvbcTRdLDpE</guid>
            <pubDate>Fri, 05 Aug 2022 02:49:17 GMT</pubDate>
            <description><![CDATA[1.有ip的话用户需要知道项目的真实性，要准备好，不然用户会一直追着问，拿不出来项目不会好的。没ip的终究也是要打造一个ip的。好好运营，好好拉盘 2.ama很重要，也不要让人觉得太空洞无用，说点项目规划，真诚一点 3.合约需要验证，现在大家都不傻，多多少少能看明白 4.退款函数也要有，拉盘利器，什么时候放看自己项目，但是有这个就是很好的保障，对mint和币价都有支撑作用 5.mint三阶段 白名单，waitlist，公募。公募也设置好结束时间 6.转化率：Twitter40000人关注，premint 2万人，mint了几百个，来听space的几十个。第一天mint的人最多，效果不好，要及时ama之类的动作，不然凉的很快 7.premint很多机器人，要处理，网站上的的筛选条件不行，筛选可以设置的严格一点，premint设置的条件也可以严格一些 8.很意外，opensea上mint20个eth都能上排行榜，nft可能并没有想象中那么火 9.会有合作团队找上来要空投，白单，白的来源有多个地方，mint的时候也可能有人来找，梅克尔树可能会随时改 10.梅克尔树方面，5000个add...]]></description>
            <content:encoded><![CDATA[<p>1.有ip的话用户需要知道项目的真实性，要准备好，不然用户会一直追着问，拿不出来项目不会好的。没ip的终究也是要打造一个ip的。好好运营，好好拉盘</p><p>2.ama很重要，也不要让人觉得太空洞无用，说点项目规划，真诚一点</p><p>3.合约需要验证，现在大家都不傻，多多少少能看明白</p><p>4.退款函数也要有，拉盘利器，什么时候放看自己项目，但是有这个就是很好的保障，对mint和币价都有支撑作用</p><p>5.mint三阶段 白名单，waitlist，公募。公募也设置好结束时间</p><p>6.转化率：Twitter40000人关注，premint 2万人，mint了几百个，来听space的几十个。第一天mint的人最多，效果不好，要及时ama之类的动作，不然凉的很快</p><p>7.premint很多机器人，要处理，网站上的的筛选条件不行，筛选可以设置的严格一点，premint设置的条件也可以严格一些</p><p>8.很意外，opensea上mint20个eth都能上排行榜，nft可能并没有想象中那么火</p><p>9.会有合作团队找上来要空投，白单，白的来源有多个地方，mint的时候也可能有人来找，梅克尔树可能会随时改</p><p>10.梅克尔树方面，5000个address，前端处理也问题不大，用户不会感觉慢，但是因为会随时改，所以可以弄个接口或者读文件，免的还要上线前端</p><p>11.就算opensea比mint的价格高，20%以内的收益都不会抹平，也会有大量的人观望，而不是像swap一样搬砖抹平差价，因为nft的流动性太小了。</p><p>12.谨记：赚钱的时候，请不要用公共的电脑，请不要用公共网络，请妥善管理相关钱包，黑客比想象中强大</p><p>13.用户会各种角度找问题质疑你，连域名剩余时间都会拿来做文章。所以多买几年吧，域名花不了几个钱</p><p>14.宣传要早，半个月就开mint会让人觉得赶时间，月抛</p><p>15.dc很重要，基本只有dc能很好的处理项目方和用户之间的沟通问题，请项目方做好mod工作</p><p>16.mod也是外人，但还是属于团队中的人，需要及时沟通，做到消息对等</p><p>17.pinata要用，但是注意要baseuri要设置成ipfs://xxxxx而不是他网站复制出来的网关。不然很容易爆</p><p>18.和实物结合的nft有问题，实物的真假，邮寄，权益的转移，权益使用的记录</p><p>19.opensea的元数据刷新机制很傻逼，要一个个进入详情点击刷新，opensea24小时内不会自动刷新缓存，之后会不会我就不知道了。我目前只知道这一个方法刷新缓存</p>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[ETH源码学习（5）处理tx]]></title>
            <link>https://paragraph.com/@point/eth-5-tx</link>
            <guid>E5hoXLnrf9IOgM2y4tvd</guid>
            <pubDate>Fri, 13 May 2022 07:30:31 GMT</pubDate>
            <description><![CDATA[处理tx要先开启挖矿总结设置好新区块总gas检查总gas，重放将gas转化成msg创建evm检查我们的余额是否足够接下来的操作调用evm执行合约/eth转账转账的话addr放入内存，然后执行transfer，修改stateobject的值给矿工钱返回receipt源码miner.start() //入口 func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) bool { // Short circuit if current is nil if w.current == nil { return true } //先设置好总的gas，因为eth是按gas上限来规定一个区块的大小，btc是按区块容量 gasLimit := w.current.header.GasLimit if w.current.gasPool == nil { w.current.gasPool = new(core.GasPo...]]></description>
            <content:encoded><![CDATA[<p>处理tx要先开启挖矿</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">总结</h2><ol><li><p>设置好新区块总gas</p></li><li><p>检查总gas，重放</p></li><li><p>将gas转化成msg</p></li><li><p>创建evm</p></li><li><p>检查我们的余额是否足够接下来的操作</p></li><li><p>调用evm执行合约/eth转账</p></li><li><p>转账的话addr放入内存，然后执行transfer，修改stateobject的值</p></li><li><p>给矿工钱</p></li><li><p>返回receipt</p></li></ol><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">源码</h2><pre data-type="codeBlock" text="miner.start()
"><code>miner.start()
</code></pre><pre data-type="codeBlock" text="//入口
func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) bool {
   // Short circuit if current is nil
   if w.current == nil {
      return true
   }
  //先设置好总的gas，因为eth是按gas上限来规定一个区块的大小，btc是按区块容量
   gasLimit := w.current.header.GasLimit
   if w.current.gasPool == nil {
      w.current.gasPool = new(core.GasPool).AddGas(gasLimit)
   }

   var coalescedLogs []*types.Log

   for {
      // In the following three cases, we will interrupt the execution of the transaction.
在以下三种情况下，我们将中断事务的执行。
      // (1) new head block event arrival, the interrupt signal is 1
新磁头块事件到达时，中断信号为1
      // (2) worker start or restart, the interrupt signal is 1
工人启动或重启，中断信号为1
      // (3) worker recreate the mining block with any newly arrived transactions, the interrupt signal is 2.
工人用任何新到达的事务重新创建采矿区块，中断信号为2。
      // For the first two cases, the semi-finished work will be discarded.
对于前两种情况，半成品将被丢弃。
      // For the third case, the semi-finished work will be submitted to the consensus engine.
对于第三种情况，半成品将提交给共识引擎。
      if interrupt != nil &amp;&amp; atomic.LoadInt32(interrupt) != commitInterruptNone {
         // Notify resubmit loop to increase resubmitting interval due to too frequent commits.
由于提交过于频繁，通知重新提交循环以增加重新提交间隔。
         if atomic.LoadInt32(interrupt) == commitInterruptResubmit {
            ratio := float64(gasLimit-w.current.gasPool.Gas()) / float64(gasLimit)
            if ratio &lt; 0.1 {
               ratio = 0.1
            }
            w.resubmitAdjustCh &lt;- &amp;intervalAdjust{
               ratio: ratio,
               inc:   true,
            }
         }
         return atomic.LoadInt32(interrupt) == commitInterruptNewHead
      }
      // If we don&apos;t have enough gas for any further transactions then we&apos;re done
如果我们没有足够的gas来做进一步的交易，那我们结束
      if w.current.gasPool.Gas() &lt; params.TxGas {
         log.Trace(&quot;Not enough gas for further transactions&quot;, &quot;have&quot;, w.current.gasPool, &quot;want&quot;, params.TxGas)
         break
      }
      // Retrieve the next transaction and abort if all done
//检索下一笔交易，如果交易集合为空则退出 commit
      tx := txs.Peek()
      if tx == nil {
         break
      }
      // Error may be ignored here. The error has already been checked
//这里可以忽略错误。已检查错误
      // during transaction acceptance is the transaction pool.
//在事务接受期间，是事务池。
      // We use the eip155 signer regardless of the current hf.
//我们使用eip155签名者，而不考虑当前的hf。eth硬分叉事件
      from, _ := types.Sender(w.current.signer, tx)
      // Check whether the tx is replay protected. If we&apos;re not in the EIP155 hf
      // phase, start ignoring the sender until we do.
      if tx.Protected() &amp;&amp; !w.chainConfig.IsEIP155(w.current.header.Number) {
         log.Trace(&quot;Ignoring reply protected transaction&quot;, &quot;hash&quot;, tx.Hash(), &quot;eip155&quot;, w.chainConfig.EIP155Block)

         txs.Pop()
         continue
      }
      // Start executing the transaction
      w.current.state.Prepare(tx.Hash(), w.current.tcount)
      // 执行交易
      logs, err := w.commitTransaction(tx, coinbase)
      switch {
      case errors.Is(err, core.ErrGasLimitReached):
         // Pop the current out-of-gas transaction without shifting in the next from the account
在不从账户转入下一笔交易的情况下，弹出当前的out of gas交易
         log.Trace(&quot;Gas limit exceeded for current block&quot;, &quot;sender&quot;, from)
         txs.Pop()

      case errors.Is(err, core.ErrNonceTooLow):
         // New head notification data race between the transaction pool and miner, shift
         log.Trace(&quot;Skipping transaction with low nonce&quot;, &quot;sender&quot;, from, &quot;nonce&quot;, tx.Nonce())
            // 移动到用户的下一个交易
         txs.Shift()

      case errors.Is(err, core.ErrNonceTooHigh):
         // Reorg notification data race between the transaction pool and miner, skip account =
         log.Trace(&quot;Skipping account with hight nonce&quot;, &quot;sender&quot;, from, &quot;nonce&quot;, tx.Nonce())
         txs.Pop()

      case errors.Is(err, nil):
         // Everything ok, collect the logs and shift in the next transaction from the same account
         coalescedLogs = append(coalescedLogs, logs...)
         w.current.tcount++
         txs.Shift()

      case errors.Is(err, core.ErrTxTypeNotSupported):
         // Pop the unsupported transaction without shifting in the next from the account
         log.Trace(&quot;Skipping unsupported transaction type&quot;, &quot;sender&quot;, from, &quot;type&quot;, tx.Type())
         txs.Pop()

      default:
         // Strange error, discard the transaction and get the next in line (note, the
         // nonce-too-high clause will prevent us from executing in vain).
         log.Debug(&quot;Transaction failed, account skipped&quot;, &quot;hash&quot;, tx.Hash(), &quot;err&quot;, err)
         txs.Shift()
      }
   }

   if !w.isRunning() &amp;&amp; len(coalescedLogs) &gt; 0 {
      // We don&apos;t push the pendingLogsEvent while we are mining. The reason is that
      // when we are mining, the worker will regenerate a mining block every 3 seconds.
      // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.

      // make a copy, the state caches the logs and these logs get &quot;upgraded&quot; from pending to mined
      // logs by filling in the block hash when the block was mined by the local miner. This can
      // cause a race condition if a log was &quot;upgraded&quot; before the PendingLogsEvent is processed.
        // 因为需要把log发送出去，而这边在挖矿完成后需要对log进行修改，所以拷贝一份发送出去，避免争用。
      cpy := make([]*types.Log, len(coalescedLogs))
      for i, l := range coalescedLogs {
         cpy[i] = new(types.Log)
         *cpy[i] = *l
      }
      w.pendingLogsFeed.Send(cpy)
   }
   // Notify resubmit loop to decrease resubmitting interval if current interval is larger
   // than the user-specified one.
   if interrupt != nil {
      w.resubmitAdjustCh &lt;- &amp;intervalAdjust{inc: false}
   }
   return false
}
"><code><span class="hljs-comment">//入口</span>
func (w <span class="hljs-operator">*</span>worker) commitTransactions(txs <span class="hljs-operator">*</span>types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt <span class="hljs-operator">*</span><span class="hljs-keyword">int32</span>) <span class="hljs-keyword">bool</span> {
   <span class="hljs-comment">// Short circuit if current is nil</span>
   <span class="hljs-keyword">if</span> w.current <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
   }
  <span class="hljs-comment">//先设置好总的gas，因为eth是按gas上限来规定一个区块的大小，btc是按区块容量</span>
   gasLimit :<span class="hljs-operator">=</span> w.current.header.GasLimit
   <span class="hljs-keyword">if</span> w.current.gasPool <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil {
      w.current.gasPool <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span>(core.GasPool).AddGas(gasLimit)
   }

   <span class="hljs-keyword">var</span> coalescedLogs []<span class="hljs-operator">*</span>types.Log

   <span class="hljs-keyword">for</span> {
      <span class="hljs-comment">// In the following three cases, we will interrupt the execution of the transaction.</span>
在以下三种情况下，我们将中断事务的执行。
      <span class="hljs-comment">// (1) new head block event arrival, the interrupt signal is 1</span>
新磁头块事件到达时，中断信号为<span class="hljs-number">1</span>
      <span class="hljs-comment">// (2) worker start or restart, the interrupt signal is 1</span>
工人启动或重启，中断信号为<span class="hljs-number">1</span>
      <span class="hljs-comment">// (3) worker recreate the mining block with any newly arrived transactions, the interrupt signal is 2.</span>
工人用任何新到达的事务重新创建采矿区块，中断信号为<span class="hljs-number">2</span>。
      <span class="hljs-comment">// For the first two cases, the semi-finished work will be discarded.</span>
对于前两种情况，半成品将被丢弃。
      <span class="hljs-comment">// For the third case, the semi-finished work will be submitted to the consensus engine.</span>
对于第三种情况，半成品将提交给共识引擎。
      <span class="hljs-keyword">if</span> interrupt <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> atomic.LoadInt32(interrupt) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> commitInterruptNone {
         <span class="hljs-comment">// Notify resubmit loop to increase resubmitting interval due to too frequent commits.</span>
由于提交过于频繁，通知重新提交循环以增加重新提交间隔。
         <span class="hljs-keyword">if</span> atomic.LoadInt32(interrupt) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> commitInterruptResubmit {
            ratio :<span class="hljs-operator">=</span> float64(gasLimit<span class="hljs-operator">-</span>w.current.gasPool.Gas()) <span class="hljs-operator">/</span> float64(gasLimit)
            <span class="hljs-keyword">if</span> ratio <span class="hljs-operator">&#x3C;</span> <span class="hljs-number">0</span><span class="hljs-number">.1</span> {
               ratio <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.1</span>
            }
            w.resubmitAdjustCh <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">-</span> <span class="hljs-operator">&#x26;</span>intervalAdjust{
               ratio: ratio,
               inc:   <span class="hljs-literal">true</span>,
            }
         }
         <span class="hljs-keyword">return</span> atomic.LoadInt32(interrupt) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> commitInterruptNewHead
      }
      <span class="hljs-comment">// If we don't have enough gas for any further transactions then we're done</span>
如果我们没有足够的gas来做进一步的交易，那我们结束
      <span class="hljs-keyword">if</span> w.current.gasPool.Gas() <span class="hljs-operator">&#x3C;</span> params.TxGas {
         log.Trace(<span class="hljs-string">"Not enough gas for further transactions"</span>, <span class="hljs-string">"have"</span>, w.current.gasPool, <span class="hljs-string">"want"</span>, params.TxGas)
         <span class="hljs-keyword">break</span>
      }
      <span class="hljs-comment">// Retrieve the next transaction and abort if all done</span>
<span class="hljs-comment">//检索下一笔交易，如果交易集合为空则退出 commit</span>
      <span class="hljs-built_in">tx</span> :<span class="hljs-operator">=</span> txs.Peek()
      <span class="hljs-keyword">if</span> <span class="hljs-built_in">tx</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil {
         <span class="hljs-keyword">break</span>
      }
      <span class="hljs-comment">// Error may be ignored here. The error has already been checked</span>
<span class="hljs-comment">//这里可以忽略错误。已检查错误</span>
      <span class="hljs-comment">// during transaction acceptance is the transaction pool.</span>
<span class="hljs-comment">//在事务接受期间，是事务池。</span>
      <span class="hljs-comment">// We use the eip155 signer regardless of the current hf.</span>
<span class="hljs-comment">//我们使用eip155签名者，而不考虑当前的hf。eth硬分叉事件</span>
      <span class="hljs-keyword">from</span>, <span class="hljs-keyword">_</span> :<span class="hljs-operator">=</span> types.Sender(w.current.signer, <span class="hljs-built_in">tx</span>)
      <span class="hljs-comment">// Check whether the tx is replay protected. If we're not in the EIP155 hf</span>
      <span class="hljs-comment">// phase, start ignoring the sender until we do.</span>
      <span class="hljs-keyword">if</span> <span class="hljs-built_in">tx</span>.Protected() <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> <span class="hljs-operator">!</span>w.chainConfig.IsEIP155(w.current.header.Number) {
         log.Trace(<span class="hljs-string">"Ignoring reply protected transaction"</span>, <span class="hljs-string">"hash"</span>, <span class="hljs-built_in">tx</span>.Hash(), <span class="hljs-string">"eip155"</span>, w.chainConfig.EIP155Block)

         txs.Pop()
         <span class="hljs-keyword">continue</span>
      }
      <span class="hljs-comment">// Start executing the transaction</span>
      w.current.state.Prepare(<span class="hljs-built_in">tx</span>.Hash(), w.current.tcount)
      <span class="hljs-comment">// 执行交易</span>
      logs, err :<span class="hljs-operator">=</span> w.commitTransaction(<span class="hljs-built_in">tx</span>, coinbase)
      switch {
      case errors.Is(err, core.ErrGasLimitReached):
         <span class="hljs-comment">// Pop the current out-of-gas transaction without shifting in the next from the account</span>
在不从账户转入下一笔交易的情况下，弹出当前的out of gas交易
         log.Trace(<span class="hljs-string">"Gas limit exceeded for current block"</span>, <span class="hljs-string">"sender"</span>, <span class="hljs-keyword">from</span>)
         txs.Pop()

      case errors.Is(err, core.ErrNonceTooLow):
         <span class="hljs-comment">// New head notification data race between the transaction pool and miner, shift</span>
         log.Trace(<span class="hljs-string">"Skipping transaction with low nonce"</span>, <span class="hljs-string">"sender"</span>, <span class="hljs-keyword">from</span>, <span class="hljs-string">"nonce"</span>, <span class="hljs-built_in">tx</span>.Nonce())
            <span class="hljs-comment">// 移动到用户的下一个交易</span>
         txs.Shift()

      case errors.Is(err, core.ErrNonceTooHigh):
         <span class="hljs-comment">// Reorg notification data race between the transaction pool and miner, skip account =</span>
         log.Trace(<span class="hljs-string">"Skipping account with hight nonce"</span>, <span class="hljs-string">"sender"</span>, <span class="hljs-keyword">from</span>, <span class="hljs-string">"nonce"</span>, <span class="hljs-built_in">tx</span>.Nonce())
         txs.Pop()

      case errors.Is(err, nil):
         <span class="hljs-comment">// Everything ok, collect the logs and shift in the next transaction from the same account</span>
         coalescedLogs <span class="hljs-operator">=</span> append(coalescedLogs, logs...)
         w.current.tcount+<span class="hljs-operator">+</span>
         txs.Shift()

      case errors.Is(err, core.ErrTxTypeNotSupported):
         <span class="hljs-comment">// Pop the unsupported transaction without shifting in the next from the account</span>
         log.Trace(<span class="hljs-string">"Skipping unsupported transaction type"</span>, <span class="hljs-string">"sender"</span>, <span class="hljs-keyword">from</span>, <span class="hljs-string">"type"</span>, <span class="hljs-built_in">tx</span>.Type())
         txs.Pop()

      default:
         <span class="hljs-comment">// Strange error, discard the transaction and get the next in line (note, the</span>
         <span class="hljs-comment">// nonce-too-high clause will prevent us from executing in vain).</span>
         log.Debug(<span class="hljs-string">"Transaction failed, account skipped"</span>, <span class="hljs-string">"hash"</span>, <span class="hljs-built_in">tx</span>.Hash(), <span class="hljs-string">"err"</span>, err)
         txs.Shift()
      }
   }

   <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>w.isRunning() <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> len(coalescedLogs) <span class="hljs-operator">></span> <span class="hljs-number">0</span> {
      <span class="hljs-comment">// We don't push the pendingLogsEvent while we are mining. The reason is that</span>
      <span class="hljs-comment">// when we are mining, the worker will regenerate a mining block every 3 seconds.</span>
      <span class="hljs-comment">// In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.</span>

      <span class="hljs-comment">// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined</span>
      <span class="hljs-comment">// logs by filling in the block hash when the block was mined by the local miner. This can</span>
      <span class="hljs-comment">// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.</span>
        <span class="hljs-comment">// 因为需要把log发送出去，而这边在挖矿完成后需要对log进行修改，所以拷贝一份发送出去，避免争用。</span>
      cpy :<span class="hljs-operator">=</span> make([]<span class="hljs-operator">*</span>types.Log, len(coalescedLogs))
      <span class="hljs-keyword">for</span> i, l :<span class="hljs-operator">=</span> range coalescedLogs {
         cpy[i] <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span>(types.Log)
         <span class="hljs-operator">*</span>cpy[i] <span class="hljs-operator">=</span> <span class="hljs-operator">*</span>l
      }
      w.pendingLogsFeed.Send(cpy)
   }
   <span class="hljs-comment">// Notify resubmit loop to decrease resubmitting interval if current interval is larger</span>
   <span class="hljs-comment">// than the user-specified one.</span>
   <span class="hljs-keyword">if</span> interrupt <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      w.resubmitAdjustCh <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">-</span> <span class="hljs-operator">&#x26;</span>intervalAdjust{inc: <span class="hljs-literal">false</span>}
   }
   <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
}
</code></pre><pre data-type="codeBlock" text="func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
//将types.Transaction结构变量转为core.Message对象；这过程中会对发送者做签名验证，并获得发送者的地址缓存起来
   msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee)
   if err != nil {
      return nil, err
   }
   // Create a new context to be used in the EVM environment
    //创建新的上下文（Context），此上下文将在EVM 环境（EVM environment）中使用；上下文中包含msg，区块头、区块指针、作者（挖矿者、获益者）

   blockContext := NewEVMBlockContext(header, bc, author)
    //创建新的EVM environment，其中包括了交易相关的所有信息以及调用机制；
   vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
    //处理交易，将交易应用于当前的状态中，也就是执行状态转换，新的状态包含在环境对象中；得到执行结果以及花费的gas；
   return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
}
"><code>func ApplyTransaction(config <span class="hljs-operator">*</span>params.ChainConfig, bc ChainContext, author <span class="hljs-operator">*</span>common.Address, gp <span class="hljs-operator">*</span>GasPool, statedb <span class="hljs-operator">*</span>state.StateDB, header <span class="hljs-operator">*</span>types.Header, <span class="hljs-built_in">tx</span> <span class="hljs-operator">*</span>types.Transaction, usedGas <span class="hljs-operator">*</span><span class="hljs-keyword">uint64</span>, cfg vm.Config) (<span class="hljs-operator">*</span>types.Receipt, <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
<span class="hljs-comment">//将types.Transaction结构变量转为core.Message对象；这过程中会对发送者做签名验证，并获得发送者的地址缓存起来</span>
   <span class="hljs-built_in">msg</span>, err :<span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee)
   <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> nil, err
   }
   <span class="hljs-comment">// Create a new context to be used in the EVM environment</span>
    <span class="hljs-comment">//创建新的上下文（Context），此上下文将在EVM 环境（EVM environment）中使用；上下文中包含msg，区块头、区块指针、作者（挖矿者、获益者）</span>

   blockContext :<span class="hljs-operator">=</span> NewEVMBlockContext(header, bc, author)
    <span class="hljs-comment">//创建新的EVM environment，其中包括了交易相关的所有信息以及调用机制；</span>
   vmenv :<span class="hljs-operator">=</span> vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
    <span class="hljs-comment">//处理交易，将交易应用于当前的状态中，也就是执行状态转换，新的状态包含在环境对象中；得到执行结果以及花费的gas；</span>
   <span class="hljs-keyword">return</span> applyTransaction(<span class="hljs-built_in">msg</span>, config, bc, author, gp, statedb, header.Number, header.Hash(), <span class="hljs-built_in">tx</span>, usedGas, vmenv)
}
</code></pre><p>跳过一段调用ApplyMessage，并将返回的内容整理成receipt代码</p><pre data-type="codeBlock" text="func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
   // First check this message satisfies all consensus rules before
//首先检查此消息是否满足所有共识规则
   // applying the message. The rules include these clauses
//应用消息。规则包括这些条款
   //
   // 1. the nonce of the message caller is correct
//调用方的nonce是正确的
   // 2. caller has enough balance to cover transaction fee(gaslimit * gasprice)
//调用者有足够的余额支付交易费用（gaslimit*gasprice）
   // 3. the amount of gas required is available in the block
区块内有可用的所需gas
   // 4. the purchased gas is enough to cover intrinsic usage
   // 5. there is no overflow when calculating intrinsic gas
没有gas溢出
   // 6. caller has enough balance to cover asset transfer for **topmost** call
//调用者有足够的gas to transfer
   // Check clauses 1-3, buy gas if everything is correct
    //    b. buyGas：根据发送者定的gaslimit和GasPrice，从发送者余额中扣除以太币；从区块gas池中减掉本次gas；并对运行环境做好更新；
   if err := st.preCheck(); err != nil {
      return nil, err
   }
   msg := st.msg
   sender := vm.AccountRef(msg.From())
   homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber)
   istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber)
   contractCreation := msg.To() == nil

   // Check clauses 4-5, subtract intrinsic gas if everything is correct
    //支付固定费用 intrinsic gas。contractCreation为true即为合约创建,固定费用为53000；为false，则为普通交易固定费用为21000
   gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul)
   if err != nil {
      return nil, err
   }
   if st.gas &lt; gas {
      return nil, fmt.Errorf(&quot;%w: have %d, want %d&quot;, ErrIntrinsicGas, st.gas, gas)
   }
   st.gas -= gas

   // Check clause 6
   if msg.Value().Sign() &gt; 0 &amp;&amp; !st.evm.Context.CanTransfer(st.state, msg.From(), msg.Value()) {
      return nil, fmt.Errorf(&quot;%w: address %v&quot;, ErrInsufficientFundsForTransfer, msg.From().Hex())
   }

   // Set up the initial access list.
   if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsBerlin {
      st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
   }
   var (
      ret   []byte
      vmerr error // vm errors do not effect consensus and are therefore not assigned to err
   )
    //如果是合约创建， 那么调用evm的Create方法创建新的合约，使用交易的data作为新合约的部署代码
   if contractCreation {
      ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value)
   } else {
      // Increment the nonce for the next transaction
//否则不是合约创建，增加发送者的Nonce值，然后调用evm.Call执行交易
      st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
//真正转账的地方
      ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value)
   }
   if !st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
      // Before EIP-3529: refunds were capped to gasUsed / 2
      st.refundGas(params.RefundQuotient)
   } else {
      // After EIP-3529: refunds are capped to gasUsed / 5
      st.refundGas(params.RefundQuotientEIP3529)
   }
   effectiveTip := st.gasPrice
   if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
      effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
   }
   st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))

   return &amp;ExecutionResult{
      UsedGas:    st.gasUsed(),
      Err:        vmerr,
      ReturnData: ret,
   }, nil
}
"><code>func (st <span class="hljs-operator">*</span>StateTransition) TransitionDb() (<span class="hljs-operator">*</span>ExecutionResult, <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
   <span class="hljs-comment">// First check this message satisfies all consensus rules before</span>
<span class="hljs-comment">//首先检查此消息是否满足所有共识规则</span>
   <span class="hljs-comment">// applying the message. The rules include these clauses</span>
<span class="hljs-comment">//应用消息。规则包括这些条款</span>
   <span class="hljs-comment">//</span>
   <span class="hljs-comment">// 1. the nonce of the message caller is correct</span>
<span class="hljs-comment">//调用方的nonce是正确的</span>
   <span class="hljs-comment">// 2. caller has enough balance to cover transaction fee(gaslimit * gasprice)</span>
<span class="hljs-comment">//调用者有足够的余额支付交易费用（gaslimit*gasprice）</span>
   <span class="hljs-comment">// 3. the amount of gas required is available in the block</span>
区块内有可用的所需gas
   <span class="hljs-comment">// 4. the purchased gas is enough to cover intrinsic usage</span>
   <span class="hljs-comment">// 5. there is no overflow when calculating intrinsic gas</span>
没有gas溢出
   <span class="hljs-comment">// 6. caller has enough balance to cover asset transfer for **topmost** call</span>
<span class="hljs-comment">//调用者有足够的gas to transfer</span>
   <span class="hljs-comment">// Check clauses 1-3, buy gas if everything is correct</span>
    <span class="hljs-comment">//    b. buyGas：根据发送者定的gaslimit和GasPrice，从发送者余额中扣除以太币；从区块gas池中减掉本次gas；并对运行环境做好更新；</span>
   <span class="hljs-keyword">if</span> err :<span class="hljs-operator">=</span> st.preCheck(); err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> nil, err
   }
   <span class="hljs-built_in">msg</span> :<span class="hljs-operator">=</span> st.msg
   sender :<span class="hljs-operator">=</span> vm.AccountRef(<span class="hljs-built_in">msg</span>.From())
   homestead :<span class="hljs-operator">=</span> st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber)
   istanbul :<span class="hljs-operator">=</span> st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber)
   contractCreation :<span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.To() <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil

   <span class="hljs-comment">// Check clauses 4-5, subtract intrinsic gas if everything is correct</span>
    <span class="hljs-comment">//支付固定费用 intrinsic gas。contractCreation为true即为合约创建,固定费用为53000；为false，则为普通交易固定费用为21000</span>
   gas, err :<span class="hljs-operator">=</span> IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul)
   <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> nil, err
   }
   <span class="hljs-keyword">if</span> st.<span class="hljs-built_in">gas</span> <span class="hljs-operator">&#x3C;</span> gas {
      <span class="hljs-keyword">return</span> nil, fmt.Errorf(<span class="hljs-string">"%w: have %d, want %d"</span>, ErrIntrinsicGas, st.<span class="hljs-built_in">gas</span>, gas)
   }
   st.<span class="hljs-built_in">gas</span> <span class="hljs-operator">-</span><span class="hljs-operator">=</span> gas

   <span class="hljs-comment">// Check clause 6</span>
   <span class="hljs-keyword">if</span> <span class="hljs-built_in">msg</span>.Value().Sign() <span class="hljs-operator">></span> <span class="hljs-number">0</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> <span class="hljs-operator">!</span>st.evm.Context.CanTransfer(st.state, <span class="hljs-built_in">msg</span>.From(), <span class="hljs-built_in">msg</span>.Value()) {
      <span class="hljs-keyword">return</span> nil, fmt.Errorf(<span class="hljs-string">"%w: address %v"</span>, ErrInsufficientFundsForTransfer, <span class="hljs-built_in">msg</span>.From().Hex())
   }

   <span class="hljs-comment">// Set up the initial access list.</span>
   <span class="hljs-keyword">if</span> rules :<span class="hljs-operator">=</span> st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsBerlin {
      st.state.PrepareAccessList(<span class="hljs-built_in">msg</span>.From(), <span class="hljs-built_in">msg</span>.To(), vm.ActivePrecompiles(rules), <span class="hljs-built_in">msg</span>.AccessList())
   }
   <span class="hljs-keyword">var</span> (
      ret   []<span class="hljs-keyword">byte</span>
      vmerr <span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-comment">// vm errors do not effect consensus and are therefore not assigned to err</span>
   )
    <span class="hljs-comment">//如果是合约创建， 那么调用evm的Create方法创建新的合约，使用交易的data作为新合约的部署代码</span>
   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">contractCreation</span> </span>{
      ret, <span class="hljs-keyword">_</span>, st.<span class="hljs-built_in">gas</span>, vmerr <span class="hljs-operator">=</span> st.evm.Create(sender, st.data, st.<span class="hljs-built_in">gas</span>, st.<span class="hljs-built_in">value</span>)
   } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// Increment the nonce for the next transaction</span>
<span class="hljs-comment">//否则不是合约创建，增加发送者的Nonce值，然后调用evm.Call执行交易</span>
      st.state.SetNonce(<span class="hljs-built_in">msg</span>.From(), st.state.GetNonce(sender.Address())<span class="hljs-operator">+</span><span class="hljs-number">1</span>)
<span class="hljs-comment">//真正转账的地方</span>
      ret, st.<span class="hljs-built_in">gas</span>, vmerr <span class="hljs-operator">=</span> st.evm.Call(sender, st.to(), st.data, st.<span class="hljs-built_in">gas</span>, st.<span class="hljs-built_in">value</span>)
   }
   <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
      <span class="hljs-comment">// Before EIP-3529: refunds were capped to gasUsed / 2</span>
      st.refundGas(params.RefundQuotient)
   } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// After EIP-3529: refunds are capped to gasUsed / 5</span>
      st.refundGas(params.RefundQuotientEIP3529)
   }
   effectiveTip :<span class="hljs-operator">=</span> st.gasPrice
   <span class="hljs-keyword">if</span> st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
      effectiveTip <span class="hljs-operator">=</span> cmath.BigMin(st.gasTipCap, <span class="hljs-keyword">new</span>(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
   }
   st.state.AddBalance(st.evm.Context.Coinbase, <span class="hljs-keyword">new</span>(big.Int).Mul(<span class="hljs-keyword">new</span>(big.Int).SetUint64(st.gasUsed()), effectiveTip))

   <span class="hljs-keyword">return</span> <span class="hljs-operator">&#x26;</span>ExecutionResult{
      UsedGas:    st.gasUsed(),
      Err:        vmerr,
      ReturnData: ret,
   }, nil
}
</code></pre><pre data-type="codeBlock" text="//转账
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
    //检查是否允许递归执行以及执行深度，若深度超过params.CallCreateDepth（即1024）就出错返回；
   if evm.Config.NoRecursion &amp;&amp; evm.depth &gt; 0 {
      return nil, gas, nil
   }
   // Fail if we&apos;re trying to execute above the call depth limit
   if evm.depth &gt; int(params.CallCreateDepth) {
      return nil, gas, ErrDepth
   }
   // Fail if we&apos;re trying to transfer more than the available balance
   if value.Sign() != 0 &amp;&amp; !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
      return nil, gas, ErrInsufficientBalance
   }
   snapshot := evm.StateDB.Snapshot()
   p, isPrecompile := evm.precompile(addr)
    
  //这个地址如果不在statedb中，则创建，就是不在内存中，就放到内存里
   if !evm.StateDB.Exist(addr) {
      if !isPrecompile &amp;&amp; evm.chainRules.IsEIP158 &amp;&amp; value.Sign() == 0 {
         // Calling a non existing account, don&apos;t do anything, but ping the tracer
         if evm.Config.Debug &amp;&amp; evm.depth == 0 {
            evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
            evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
         }
         return nil, gas, nil
      }
      evm.StateDB.CreateAccount(addr)
   }
//转账
   evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)

   // Capture the tracer start/end events in debug mode
   if evm.Config.Debug &amp;&amp; evm.depth == 0 {
      evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
      defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
         evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
      }(gas, time.Now())
   }

   if isPrecompile {
      ret, gas, err = RunPrecompiledContract(p, input, gas)
   } else {
      // Initialise a new contract and set the code that is to be used by the EVM.
      // The contract is a scoped environment for this execution context only.
      code := evm.StateDB.GetCode(addr)
      if len(code) == 0 {
         ret, err = nil, nil // gas is unchanged
      } else {
         addrCopy := addr
         // If the account has no code, we can abort here
         // The depth-check is already done, and precompiles handled above
         contract := NewContract(caller, AccountRef(addrCopy), value, gas)
         contract.SetCallCode(&amp;addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
         ret, err = evm.interpreter.Run(contract, input, false)
         gas = contract.Gas
      }
   }
   // When an error was returned by the EVM or when setting the creation code
   // above we revert to the snapshot and consume any gas remaining. Additionally
   // when we&apos;re in homestead this also counts for code storage gas errors.
   if err != nil {
      evm.StateDB.RevertToSnapshot(snapshot)
      if err != ErrExecutionReverted {
         gas = 0
      }
      // TODO: consider clearing up unused snapshots:
      //} else {
      // evm.StateDB.DiscardSnapshot(snapshot)
   }
   return ret, gas, err
}
"><code><span class="hljs-comment">//转账</span>
func (evm <span class="hljs-operator">*</span>EVM) Call(caller ContractRef, addr common.Address, input []<span class="hljs-keyword">byte</span>, gas <span class="hljs-keyword">uint64</span>, value <span class="hljs-operator">*</span>big.Int) (ret []<span class="hljs-keyword">byte</span>, leftOverGas <span class="hljs-keyword">uint64</span>, err <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
    <span class="hljs-comment">//检查是否允许递归执行以及执行深度，若深度超过params.CallCreateDepth（即1024）就出错返回；</span>
   <span class="hljs-keyword">if</span> evm.Config.NoRecursion <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> evm.depth <span class="hljs-operator">></span> <span class="hljs-number">0</span> {
      <span class="hljs-keyword">return</span> nil, gas, nil
   }
   <span class="hljs-comment">// Fail if we're trying to execute above the call depth limit</span>
   <span class="hljs-keyword">if</span> evm.depth <span class="hljs-operator">></span> <span class="hljs-keyword">int</span>(params.CallCreateDepth) {
      <span class="hljs-keyword">return</span> nil, gas, ErrDepth
   }
   <span class="hljs-comment">// Fail if we're trying to transfer more than the available balance</span>
   <span class="hljs-keyword">if</span> value.Sign() <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> <span class="hljs-operator">!</span>evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
      <span class="hljs-keyword">return</span> nil, gas, ErrInsufficientBalance
   }
   snapshot :<span class="hljs-operator">=</span> evm.StateDB.Snapshot()
   p, isPrecompile :<span class="hljs-operator">=</span> evm.precompile(addr)
    
  <span class="hljs-comment">//这个地址如果不在statedb中，则创建，就是不在内存中，就放到内存里</span>
   <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>evm.StateDB.Exist(addr) {
      <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>isPrecompile <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> evm.chainRules.IsEIP158 <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> value.Sign() <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> {
         <span class="hljs-comment">// Calling a non existing account, don't do anything, but ping the tracer</span>
         <span class="hljs-keyword">if</span> evm.Config.Debug <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> evm.depth <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> {
            evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, <span class="hljs-literal">false</span>, input, gas, value)
            evm.Config.Tracer.CaptureEnd(ret, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, nil)
         }
         <span class="hljs-keyword">return</span> nil, gas, nil
      }
      evm.StateDB.CreateAccount(addr)
   }
<span class="hljs-comment">//转账</span>
   evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)

   <span class="hljs-comment">// Capture the tracer start/end events in debug mode</span>
   <span class="hljs-keyword">if</span> evm.Config.Debug <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> evm.depth <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> {
      evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, <span class="hljs-literal">false</span>, input, gas, value)
      defer func(startGas <span class="hljs-keyword">uint64</span>, startTime time.Time) { <span class="hljs-comment">// Lazy evaluation of the parameters</span>
         evm.Config.Tracer.CaptureEnd(ret, startGas<span class="hljs-operator">-</span>gas, time.Since(startTime), err)
      }(gas, time.Now())
   }

   <span class="hljs-keyword">if</span> isPrecompile {
      ret, gas, err <span class="hljs-operator">=</span> RunPrecompiledContract(p, input, gas)
   } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// Initialise a new contract and set the code that is to be used by the EVM.</span>
      <span class="hljs-comment">// The contract is a scoped environment for this execution context only.</span>
      code :<span class="hljs-operator">=</span> evm.StateDB.GetCode(addr)
      <span class="hljs-keyword">if</span> len(code) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> {
         ret, err <span class="hljs-operator">=</span> nil, nil <span class="hljs-comment">// gas is unchanged</span>
      } <span class="hljs-keyword">else</span> {
         addrCopy :<span class="hljs-operator">=</span> addr
         <span class="hljs-comment">// If the account has no code, we can abort here</span>
         <span class="hljs-comment">// The depth-check is already done, and precompiles handled above</span>
         <span class="hljs-class"><span class="hljs-keyword">contract</span> := <span class="hljs-title">NewContract</span>(<span class="hljs-params">caller, AccountRef(<span class="hljs-params">addrCopy</span>), value, gas</span>)
         <span class="hljs-title"><span class="hljs-keyword">contract</span></span>.<span class="hljs-title">SetCallCode</span>(<span class="hljs-params">&#x26;addrCopy, evm.StateDB.GetCodeHash(<span class="hljs-params">addrCopy</span>), code</span>)
         <span class="hljs-title">ret</span>, <span class="hljs-title">err</span> = <span class="hljs-title">evm</span>.<span class="hljs-title">interpreter</span>.<span class="hljs-title">Run</span>(<span class="hljs-params"><span class="hljs-keyword">contract</span>, input, <span class="hljs-literal">false</span></span>)
         <span class="hljs-title">gas</span> = <span class="hljs-title"><span class="hljs-keyword">contract</span></span>.<span class="hljs-title">Gas</span>
      }
   }
   <span class="hljs-comment">// When an error was returned by the EVM or when setting the creation code</span>
   <span class="hljs-comment">// above we revert to the snapshot and consume any gas remaining. Additionally</span>
   <span class="hljs-comment">// when we're in homestead this also counts for code storage gas errors.</span>
   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">err</span> != <span class="hljs-title">nil</span> </span>{
      evm.StateDB.RevertToSnapshot(snapshot)
      <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> ErrExecutionReverted {
         gas <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
      }
      <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> consider clearing up unused snapshots:</span>
      <span class="hljs-comment">//} else {</span>
      <span class="hljs-comment">// evm.StateDB.DiscardSnapshot(snapshot)</span>
   }
   <span class="hljs-keyword">return</span> ret, gas, err
}
</code></pre><pre data-type="codeBlock" text="//转账操作，实际上就是再stateobject中加减，然后写入journal
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
    db.SubBalance(sender, amount)
    db.AddBalance(recipient, amount)
}
"><code><span class="hljs-comment">//转账操作，实际上就是再stateobject中加减，然后写入journal</span>
func Transfer(db vm.StateDB, sender, recipient common.Address, amount <span class="hljs-operator">*</span>big.Int) {
    db.SubBalance(sender, amount)
    db.AddBalance(recipient, amount)
}
</code></pre>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[ETH源码学习（4）sendTransaction]]></title>
            <link>https://paragraph.com/@point/eth-4-sendtransaction</link>
            <guid>Yf6pD86gEIAq5LrNW16V</guid>
            <pubDate>Thu, 12 May 2022 13:47:34 GMT</pubDate>
            <description><![CDATA[看了getbalance和getblock，对leveldb存的内容有了大致的印象，下面就是怎么往里面存这些数据了。最基础的，发送一笔交易简单总结流程：将from打包成钱包将请求参数包装成tx签名验证签名验证交易是否合法加入queue前的一些校验加入pool的queue队列和all数组加入pending前的一些校验加入pool的pending队列广播//命令 eth.sendTransaction({from:'0xc57998cd5e4ad8bd82f7f6495771edb438d7452c' , to: '0xe16b36be71d89641bd2bae440b348bdcd93f127c', value: web3.toWei(1,"ether")}) //入口 func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args TransactionArgs) (common.Hash, error) { // Look up the wallet containing the req...]]></description>
            <content:encoded><![CDATA[<p>看了getbalance和getblock，对leveldb存的内容有了大致的印象，下面就是怎么往里面存这些数据了。最基础的，发送一笔交易</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">简单总结流程：</h2><ol><li><p>将from打包成钱包</p></li><li><p>将请求参数包装成tx</p></li><li><p>签名</p></li><li><p>验证签名</p></li><li><p>验证交易是否合法</p></li><li><p>加入queue前的一些校验</p></li><li><p>加入pool的queue队列和all数组</p></li><li><p>加入pending前的一些校验</p></li><li><p>加入pool的pending队列</p></li><li><p>广播</p></li></ol><pre data-type="codeBlock" text="//命令
eth.sendTransaction({from:&apos;0xc57998cd5e4ad8bd82f7f6495771edb438d7452c&apos; , to: &apos;0xe16b36be71d89641bd2bae440b348bdcd93f127c&apos;, value: web3.toWei(1,&quot;ether&quot;)})
//入口
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args TransactionArgs) (common.Hash, error) {
    // Look up the wallet containing the requested signer
    account := accounts.Account{Address: args.from()}

    wallet, err := s.b.AccountManager().Find(account)
    if err != nil {
        return common.Hash{}, err
    }

    if args.Nonce == nil {
        // Hold the addresse&apos;s mutex around signing to prevent concurrent assignment of
        // the same nonce to multiple accounts.
        s.nonceLock.LockAddr(args.from())
        defer s.nonceLock.UnlockAddr(args.from())
    }

    // Set some sanity defaults and terminate on failure
    if err := args.setDefaults(ctx, s.b); err != nil {
        return common.Hash{}, err
    }
    // Assemble the transaction and sign with the wallet
        // 打包请求参数，封装成tx
    tx := args.toTransaction()
        //签名
    signed, err := wallet.SignTx(account, tx, s.b.ChainConfig().ChainID)
    if err != nil {
        return common.Hash{}, err
    }
        //处理交易
    return SubmitTransaction(ctx, s.b, signed)
}
"><code><span class="hljs-comment">//命令</span>
eth.sendTransaction({<span class="hljs-keyword">from</span>:<span class="hljs-string">'0xc57998cd5e4ad8bd82f7f6495771edb438d7452c'</span> , to: <span class="hljs-string">'0xe16b36be71d89641bd2bae440b348bdcd93f127c'</span>, <span class="hljs-built_in">value</span>: web3.toWei(<span class="hljs-number">1</span>,<span class="hljs-string">"ether"</span>)})
<span class="hljs-comment">//入口</span>
func (s <span class="hljs-operator">*</span>PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args TransactionArgs) (common.Hash, <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
    <span class="hljs-comment">// Look up the wallet containing the requested signer</span>
    account :<span class="hljs-operator">=</span> accounts.Account{Address: args.from()}

    wallet, err :<span class="hljs-operator">=</span> s.b.AccountManager().Find(account)
    <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
        <span class="hljs-keyword">return</span> common.Hash{}, err
    }

    <span class="hljs-keyword">if</span> args.Nonce <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil {
        <span class="hljs-comment">// Hold the addresse's mutex around signing to prevent concurrent assignment of</span>
        <span class="hljs-comment">// the same nonce to multiple accounts.</span>
        s.nonceLock.LockAddr(args.from())
        defer s.nonceLock.UnlockAddr(args.from())
    }

    <span class="hljs-comment">// Set some sanity defaults and terminate on failure</span>
    <span class="hljs-keyword">if</span> err :<span class="hljs-operator">=</span> args.setDefaults(ctx, s.b); err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
        <span class="hljs-keyword">return</span> common.Hash{}, err
    }
    <span class="hljs-comment">// Assemble the transaction and sign with the wallet</span>
        <span class="hljs-comment">// 打包请求参数，封装成tx</span>
    <span class="hljs-built_in">tx</span> :<span class="hljs-operator">=</span> args.toTransaction()
        <span class="hljs-comment">//签名</span>
    signed, err :<span class="hljs-operator">=</span> wallet.SignTx(account, <span class="hljs-built_in">tx</span>, s.b.ChainConfig().ChainID)
    <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
        <span class="hljs-keyword">return</span> common.Hash{}, err
    }
        <span class="hljs-comment">//处理交易</span>
    <span class="hljs-keyword">return</span> SubmitTransaction(ctx, s.b, signed)
}
</code></pre><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">签名</h3><pre data-type="codeBlock" text="//使用规定的规范进行签名，我的版本是伦敦升级版本
func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
  //1.对交易进行哈希
   h := s.Hash(tx)
//2.生成签名，里面就看不懂了，总之就是算出R，S，V，在放入tx中
   sig, err := crypto.Sign(h[:], prv)
   if err != nil {
      return nil, err
   }
//3.将签名数据填充到Tx信息中
   return tx.WithSignature(s, sig)
}
"><code><span class="hljs-comment">//使用规定的规范进行签名，我的版本是伦敦升级版本</span>
func SignTx(<span class="hljs-built_in">tx</span> <span class="hljs-operator">*</span>Transaction, s Signer, prv <span class="hljs-operator">*</span>ecdsa.PrivateKey) (<span class="hljs-operator">*</span>Transaction, <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
  <span class="hljs-comment">//1.对交易进行哈希</span>
   h :<span class="hljs-operator">=</span> s.Hash(<span class="hljs-built_in">tx</span>)
<span class="hljs-comment">//2.生成签名，里面就看不懂了，总之就是算出R，S，V，在放入tx中</span>
   sig, err :<span class="hljs-operator">=</span> crypto.Sign(h[:], prv)
   <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> nil, err
   }
<span class="hljs-comment">//3.将签名数据填充到Tx信息中</span>
   <span class="hljs-keyword">return</span> <span class="hljs-built_in">tx</span>.WithSignature(s, sig)
}
</code></pre><pre data-type="codeBlock" text="type txLookup struct {
   slots   int
   lock    sync.RWMutex
   locals  map[common.Hash]*types.Transaction
   remotes map[common.Hash]*types.Transaction
}
"><code><span class="hljs-keyword">type</span> txLookup <span class="hljs-keyword">struct</span> {
   slots   <span class="hljs-keyword">int</span>
   lock    sync.RWMutex
   locals  map[common.Hash]<span class="hljs-operator">*</span>types.Transaction
   remotes map[common.Hash]<span class="hljs-operator">*</span>types.Transaction
}
</code></pre><h3 id="h-pool" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">交易加入pool</h3><pre data-type="codeBlock" text="//前面跳过了一些可以不用关心的步骤
func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
    // If the transaction is already known, discard it
//获取交易hash并以此判断交易池中是否已存在该笔交易
    hash := tx.Hash()
    if pool.all.Get(hash) != nil {
        log.Trace(&quot;Discarding already known transaction&quot;, &quot;hash&quot;, hash)
        knownTxMeter.Mark(1)
        return false, ErrAlreadyKnown
    }
    // Make the local flag. If it&apos;s from local source or it&apos;s from the network but
    // the sender is marked as local previously, treat it as the local transaction.
    isLocal := local || pool.locals.containsTx(tx)

    // If the transaction fails basic validation, discard it
// 验证交易合法性
    if err := pool.validateTx(tx, isLocal); err != nil {
        log.Trace(&quot;Discarding invalid transaction&quot;, &quot;hash&quot;, hash, &quot;err&quot;, err)
        invalidTxMeter.Mark(1)
        return false, err
    }
    // If the transaction pool is full, discard underpriced transactions
// 如果交易池已满，按priced数组中gas price较低的交易剔除
    if uint64(pool.all.Slots()+numSlots(tx)) &gt; pool.config.GlobalSlots+pool.config.GlobalQueue {
        // If the new transaction is underpriced, don&apos;t accept it
        if !isLocal &amp;&amp; pool.priced.Underpriced(tx) {
            log.Trace(&quot;Discarding underpriced transaction&quot;, &quot;hash&quot;, hash, &quot;gasTipCap&quot;, tx.GasTipCap(), &quot;gasFeeCap&quot;, tx.GasFeeCap())
            underpricedTxMeter.Mark(1)
            return false, ErrUnderpriced
        }
        // New transaction is better than our worse ones, make room for it.
        // If it&apos;s a local transaction, forcibly discard all available transactions.
        // Otherwise if we can&apos;t make enough room for new one, abort the operation.
        drop, success := pool.priced.Discard(pool.all.Slots()-int(pool.config.GlobalSlots+pool.config.GlobalQueue)+numSlots(tx), isLocal)

        // Special case, we still can&apos;t make the room for the new remote one.
        if !isLocal &amp;&amp; !success {
            log.Trace(&quot;Discarding overflown transaction&quot;, &quot;hash&quot;, hash)
            overflowedTxMeter.Mark(1)
            return false, ErrTxPoolOverflow
        }
        // Kick out the underpriced remote transactions.
        for _, tx := range drop {
            log.Trace(&quot;Discarding freshly underpriced transaction&quot;, &quot;hash&quot;, tx.Hash(), &quot;gasTipCap&quot;, tx.GasTipCap(), &quot;gasFeeCap&quot;, tx.GasFeeCap())
            underpricedTxMeter.Mark(1)
            pool.removeTx(tx.Hash(), false)
        }
    }
    // Try to replace an existing transaction in the pending pool
// 如果交易已经存在于pending列表，比较新旧交易gasPrice的差值是否超过PriceBump
    // 若超过则使用新交易代替旧交易
    from, _ := types.Sender(pool.signer, tx) // already validated
    if list := pool.pending[from]; list != nil &amp;&amp; list.Overlaps(tx) {
        // Nonce already pending, check if required price bump is met
        inserted, old := list.Add(tx, pool.config.PriceBump)
        if !inserted {
            pendingDiscardMeter.Mark(1)
            return false, ErrReplaceUnderpriced
        }
        // New transaction is better, replace old one
        if old != nil {
            pool.all.Remove(old.Hash())
            pool.priced.Removed(1)
            pendingReplaceMeter.Mark(1)
        }
        pool.all.Add(tx, isLocal)
        pool.priced.Put(tx, isLocal)
        pool.journalTx(from, tx)
        pool.queueTxEvent(tx)
        log.Trace(&quot;Pooled new executable transaction&quot;, &quot;hash&quot;, hash, &quot;from&quot;, from, &quot;to&quot;, tx.To())

        // Successful promotion, bump the heartbeat
        pool.beats[from] = time.Now()
        return old != nil, nil
    }
    // New transaction isn&apos;t replacing a pending one, push into queue
// 将交易添加到queue队列
    replaced, err = pool.enqueueTx(hash, tx, isLocal, true)
    if err != nil {
        return false, err
    }
    // Mark local addresses and journal local transactions
// 判断是否本地交易，保证本地交易优先被加入到TxPool
    if local &amp;&amp; !pool.locals.contains(from) {
        log.Info(&quot;Setting new local account&quot;, &quot;address&quot;, from)
        pool.locals.add(from)
        pool.priced.Removed(pool.all.RemoteToLocals(pool.locals)) // Migrate the remotes if it&apos;s marked as local first time.
    }
    if isLocal {
        localGauge.Inc(1)
    }
    pool.journalTx(from, tx)

    log.Trace(&quot;Pooled new future transaction&quot;, &quot;hash&quot;, hash, &quot;from&quot;, from, &quot;to&quot;, tx.To())
    return replaced, nil
}
"><code><span class="hljs-comment">//前面跳过了一些可以不用关心的步骤</span>
func (pool <span class="hljs-operator">*</span>TxPool) add(<span class="hljs-built_in">tx</span> <span class="hljs-operator">*</span>types.Transaction, local <span class="hljs-keyword">bool</span>) (replaced <span class="hljs-keyword">bool</span>, err <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
    <span class="hljs-comment">// If the transaction is already known, discard it</span>
<span class="hljs-comment">//获取交易hash并以此判断交易池中是否已存在该笔交易</span>
    hash :<span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.Hash()
    <span class="hljs-keyword">if</span> pool.all.Get(hash) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
        log.Trace(<span class="hljs-string">"Discarding already known transaction"</span>, <span class="hljs-string">"hash"</span>, hash)
        knownTxMeter.Mark(<span class="hljs-number">1</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>, ErrAlreadyKnown
    }
    <span class="hljs-comment">// Make the local flag. If it's from local source or it's from the network but</span>
    <span class="hljs-comment">// the sender is marked as local previously, treat it as the local transaction.</span>
    isLocal :<span class="hljs-operator">=</span> local <span class="hljs-operator">|</span><span class="hljs-operator">|</span> pool.locals.containsTx(<span class="hljs-built_in">tx</span>)

    <span class="hljs-comment">// If the transaction fails basic validation, discard it</span>
<span class="hljs-comment">// 验证交易合法性</span>
    <span class="hljs-keyword">if</span> err :<span class="hljs-operator">=</span> pool.validateTx(<span class="hljs-built_in">tx</span>, isLocal); err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
        log.Trace(<span class="hljs-string">"Discarding invalid transaction"</span>, <span class="hljs-string">"hash"</span>, hash, <span class="hljs-string">"err"</span>, err)
        invalidTxMeter.Mark(<span class="hljs-number">1</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>, err
    }
    <span class="hljs-comment">// If the transaction pool is full, discard underpriced transactions</span>
<span class="hljs-comment">// 如果交易池已满，按priced数组中gas price较低的交易剔除</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">uint64</span>(pool.all.Slots()<span class="hljs-operator">+</span>numSlots(<span class="hljs-built_in">tx</span>)) <span class="hljs-operator">></span> pool.config.GlobalSlots+pool.config.GlobalQueue {
        <span class="hljs-comment">// If the new transaction is underpriced, don't accept it</span>
        <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>isLocal <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> pool.priced.Underpriced(<span class="hljs-built_in">tx</span>) {
            log.Trace(<span class="hljs-string">"Discarding underpriced transaction"</span>, <span class="hljs-string">"hash"</span>, hash, <span class="hljs-string">"gasTipCap"</span>, <span class="hljs-built_in">tx</span>.GasTipCap(), <span class="hljs-string">"gasFeeCap"</span>, <span class="hljs-built_in">tx</span>.GasFeeCap())
            underpricedTxMeter.Mark(<span class="hljs-number">1</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>, ErrUnderpriced
        }
        <span class="hljs-comment">// New transaction is better than our worse ones, make room for it.</span>
        <span class="hljs-comment">// If it's a local transaction, forcibly discard all available transactions.</span>
        <span class="hljs-comment">// Otherwise if we can't make enough room for new one, abort the operation.</span>
        drop, success :<span class="hljs-operator">=</span> pool.priced.Discard(pool.all.Slots()<span class="hljs-operator">-</span><span class="hljs-keyword">int</span>(pool.config.GlobalSlots+pool.config.GlobalQueue)<span class="hljs-operator">+</span>numSlots(<span class="hljs-built_in">tx</span>), isLocal)

        <span class="hljs-comment">// Special case, we still can't make the room for the new remote one.</span>
        <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>isLocal <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> <span class="hljs-operator">!</span>success {
            log.Trace(<span class="hljs-string">"Discarding overflown transaction"</span>, <span class="hljs-string">"hash"</span>, hash)
            overflowedTxMeter.Mark(<span class="hljs-number">1</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>, ErrTxPoolOverflow
        }
        <span class="hljs-comment">// Kick out the underpriced remote transactions.</span>
        <span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span>, <span class="hljs-built_in">tx</span> :<span class="hljs-operator">=</span> range drop {
            log.Trace(<span class="hljs-string">"Discarding freshly underpriced transaction"</span>, <span class="hljs-string">"hash"</span>, <span class="hljs-built_in">tx</span>.Hash(), <span class="hljs-string">"gasTipCap"</span>, <span class="hljs-built_in">tx</span>.GasTipCap(), <span class="hljs-string">"gasFeeCap"</span>, <span class="hljs-built_in">tx</span>.GasFeeCap())
            underpricedTxMeter.Mark(<span class="hljs-number">1</span>)
            pool.removeTx(<span class="hljs-built_in">tx</span>.Hash(), <span class="hljs-literal">false</span>)
        }
    }
    <span class="hljs-comment">// Try to replace an existing transaction in the pending pool</span>
<span class="hljs-comment">// 如果交易已经存在于pending列表，比较新旧交易gasPrice的差值是否超过PriceBump</span>
    <span class="hljs-comment">// 若超过则使用新交易代替旧交易</span>
    <span class="hljs-keyword">from</span>, <span class="hljs-keyword">_</span> :<span class="hljs-operator">=</span> types.Sender(pool.signer, <span class="hljs-built_in">tx</span>) <span class="hljs-comment">// already validated</span>
    <span class="hljs-keyword">if</span> list :<span class="hljs-operator">=</span> pool.pending[<span class="hljs-keyword">from</span>]; list <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> list.Overlaps(<span class="hljs-built_in">tx</span>) {
        <span class="hljs-comment">// Nonce already pending, check if required price bump is met</span>
        inserted, old :<span class="hljs-operator">=</span> list.Add(<span class="hljs-built_in">tx</span>, pool.config.PriceBump)
        <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>inserted {
            pendingDiscardMeter.Mark(<span class="hljs-number">1</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>, ErrReplaceUnderpriced
        }
        <span class="hljs-comment">// New transaction is better, replace old one</span>
        <span class="hljs-keyword">if</span> old <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
            pool.all.Remove(old.Hash())
            pool.priced.Removed(<span class="hljs-number">1</span>)
            pendingReplaceMeter.Mark(<span class="hljs-number">1</span>)
        }
        pool.all.Add(<span class="hljs-built_in">tx</span>, isLocal)
        pool.priced.Put(<span class="hljs-built_in">tx</span>, isLocal)
        pool.journalTx(<span class="hljs-keyword">from</span>, <span class="hljs-built_in">tx</span>)
        pool.queueTxEvent(<span class="hljs-built_in">tx</span>)
        log.Trace(<span class="hljs-string">"Pooled new executable transaction"</span>, <span class="hljs-string">"hash"</span>, hash, <span class="hljs-string">"from"</span>, <span class="hljs-keyword">from</span>, <span class="hljs-string">"to"</span>, <span class="hljs-built_in">tx</span>.To())

        <span class="hljs-comment">// Successful promotion, bump the heartbeat</span>
        pool.beats[<span class="hljs-keyword">from</span>] <span class="hljs-operator">=</span> time.Now()
        <span class="hljs-keyword">return</span> old <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil, nil
    }
    <span class="hljs-comment">// New transaction isn't replacing a pending one, push into queue</span>
<span class="hljs-comment">// 将交易添加到queue队列</span>
    replaced, err <span class="hljs-operator">=</span> pool.enqueueTx(hash, <span class="hljs-built_in">tx</span>, isLocal, <span class="hljs-literal">true</span>)
    <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>, err
    }
    <span class="hljs-comment">// Mark local addresses and journal local transactions</span>
<span class="hljs-comment">// 判断是否本地交易，保证本地交易优先被加入到TxPool</span>
    <span class="hljs-keyword">if</span> local <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> <span class="hljs-operator">!</span>pool.locals.contains(<span class="hljs-keyword">from</span>) {
        log.Info(<span class="hljs-string">"Setting new local account"</span>, <span class="hljs-string">"address"</span>, <span class="hljs-keyword">from</span>)
        pool.locals.add(<span class="hljs-keyword">from</span>)
        pool.priced.Removed(pool.all.RemoteToLocals(pool.locals)) <span class="hljs-comment">// Migrate the remotes if it's marked as local first time.</span>
    }
    <span class="hljs-keyword">if</span> isLocal {
        localGauge.Inc(<span class="hljs-number">1</span>)
    }
    pool.journalTx(<span class="hljs-keyword">from</span>, <span class="hljs-built_in">tx</span>)

    log.Trace(<span class="hljs-string">"Pooled new future transaction"</span>, <span class="hljs-string">"hash"</span>, hash, <span class="hljs-string">"from"</span>, <span class="hljs-keyword">from</span>, <span class="hljs-string">"to"</span>, <span class="hljs-built_in">tx</span>.To())
    <span class="hljs-keyword">return</span> replaced, nil
}
</code></pre><pre data-type="codeBlock" text="//验证tx是否合法
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
   // Accept only legacy transactions until EIP-2718/2930 activates.
   if !pool.eip2718 &amp;&amp; tx.Type() != types.LegacyTxType {
      return ErrTxTypeNotSupported
   }
   // Reject dynamic fee transactions until EIP-1559 activates.
   if !pool.eip1559 &amp;&amp; tx.Type() == types.DynamicFeeTxType {
      return ErrTxTypeNotSupported
   }
   // Reject transactions over defined size to prevent DOS attacks
   if uint64(tx.Size()) &gt; txMaxSize {
      return ErrOversizedData
   }
   // Transactions can&apos;t be negative. This may never happen using RLP decoded
   // transactions but may occur if you create a transaction using the RPC.
   if tx.Value().Sign() &lt; 0 {
      return ErrNegativeValue
   }
   // Ensure the transaction doesn&apos;t exceed the current block limit gas.
   if pool.currentMaxGas &lt; tx.Gas() {
      return ErrGasLimit
   }
   // Sanity check for extremely large numbers
   if tx.GasFeeCap().BitLen() &gt; 256 {
      return ErrFeeCapVeryHigh
   }
   if tx.GasTipCap().BitLen() &gt; 256 {
      return ErrTipVeryHigh
   }
   // Ensure gasFeeCap is greater than or equal to gasTipCap.
   if tx.GasFeeCapIntCmp(tx.GasTipCap()) &lt; 0 {
      return ErrTipAboveFeeCap
   }
   // Make sure the transaction is signed properly.
   from, err := types.Sender(pool.signer, tx)
   if err != nil {
      return ErrInvalidSender
   }
   // Drop non-local transactions under our own minimal accepted gas price or tip
   if !local &amp;&amp; tx.GasTipCapIntCmp(pool.gasPrice) &lt; 0 {
      return ErrUnderpriced
   }
   // Ensure the transaction adheres to nonce ordering
   if pool.currentState.GetNonce(from) &gt; tx.Nonce() {
      return ErrNonceTooLow
   }
   // Transactor should have enough funds to cover the costs
   // cost == V + GP * GL
   if pool.currentState.GetBalance(from).Cmp(tx.Cost()) &lt; 0 {
      return ErrInsufficientFunds
   }
   // Ensure the transaction has more gas than the basic tx fee.
   intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul)
   if err != nil {
      return err
   }
   if tx.Gas() &lt; intrGas {
      return ErrIntrinsicGas
   }
   return nil
}
"><code><span class="hljs-comment">//验证tx是否合法</span>
func (pool <span class="hljs-operator">*</span>TxPool) validateTx(<span class="hljs-built_in">tx</span> <span class="hljs-operator">*</span>types.Transaction, local <span class="hljs-keyword">bool</span>) <span class="hljs-function"><span class="hljs-keyword">error</span> </span>{
   <span class="hljs-comment">// Accept only legacy transactions until EIP-2718/2930 activates.</span>
   <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>pool.eip2718 <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> <span class="hljs-built_in">tx</span>.Type() <span class="hljs-operator">!</span><span class="hljs-operator">=</span> types.LegacyTxType {
      <span class="hljs-keyword">return</span> ErrTxTypeNotSupported
   }
   <span class="hljs-comment">// Reject dynamic fee transactions until EIP-1559 activates.</span>
   <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>pool.eip1559 <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> <span class="hljs-built_in">tx</span>.Type() <span class="hljs-operator">=</span><span class="hljs-operator">=</span> types.DynamicFeeTxType {
      <span class="hljs-keyword">return</span> ErrTxTypeNotSupported
   }
   <span class="hljs-comment">// Reject transactions over defined size to prevent DOS attacks</span>
   <span class="hljs-keyword">if</span> <span class="hljs-keyword">uint64</span>(<span class="hljs-built_in">tx</span>.Size()) <span class="hljs-operator">></span> txMaxSize {
      <span class="hljs-keyword">return</span> ErrOversizedData
   }
   <span class="hljs-comment">// Transactions can't be negative. This may never happen using RLP decoded</span>
   <span class="hljs-comment">// transactions but may occur if you create a transaction using the RPC.</span>
   <span class="hljs-keyword">if</span> <span class="hljs-built_in">tx</span>.Value().Sign() <span class="hljs-operator">&#x3C;</span> <span class="hljs-number">0</span> {
      <span class="hljs-keyword">return</span> ErrNegativeValue
   }
   <span class="hljs-comment">// Ensure the transaction doesn't exceed the current block limit gas.</span>
   <span class="hljs-keyword">if</span> pool.currentMaxGas <span class="hljs-operator">&#x3C;</span> <span class="hljs-built_in">tx</span>.Gas() {
      <span class="hljs-keyword">return</span> ErrGasLimit
   }
   <span class="hljs-comment">// Sanity check for extremely large numbers</span>
   <span class="hljs-keyword">if</span> <span class="hljs-built_in">tx</span>.GasFeeCap().BitLen() <span class="hljs-operator">></span> <span class="hljs-number">256</span> {
      <span class="hljs-keyword">return</span> ErrFeeCapVeryHigh
   }
   <span class="hljs-keyword">if</span> <span class="hljs-built_in">tx</span>.GasTipCap().BitLen() <span class="hljs-operator">></span> <span class="hljs-number">256</span> {
      <span class="hljs-keyword">return</span> ErrTipVeryHigh
   }
   <span class="hljs-comment">// Ensure gasFeeCap is greater than or equal to gasTipCap.</span>
   <span class="hljs-keyword">if</span> <span class="hljs-built_in">tx</span>.GasFeeCapIntCmp(<span class="hljs-built_in">tx</span>.GasTipCap()) <span class="hljs-operator">&#x3C;</span> <span class="hljs-number">0</span> {
      <span class="hljs-keyword">return</span> ErrTipAboveFeeCap
   }
   <span class="hljs-comment">// Make sure the transaction is signed properly.</span>
   <span class="hljs-keyword">from</span>, err :<span class="hljs-operator">=</span> types.Sender(pool.signer, <span class="hljs-built_in">tx</span>)
   <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> ErrInvalidSender
   }
   <span class="hljs-comment">// Drop non-local transactions under our own minimal accepted gas price or tip</span>
   <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>local <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> <span class="hljs-built_in">tx</span>.GasTipCapIntCmp(pool.gasPrice) <span class="hljs-operator">&#x3C;</span> <span class="hljs-number">0</span> {
      <span class="hljs-keyword">return</span> ErrUnderpriced
   }
   <span class="hljs-comment">// Ensure the transaction adheres to nonce ordering</span>
   <span class="hljs-keyword">if</span> pool.currentState.GetNonce(<span class="hljs-keyword">from</span>) <span class="hljs-operator">></span> <span class="hljs-built_in">tx</span>.Nonce() {
      <span class="hljs-keyword">return</span> ErrNonceTooLow
   }
   <span class="hljs-comment">// Transactor should have enough funds to cover the costs</span>
   <span class="hljs-comment">// cost == V + GP * GL</span>
   <span class="hljs-keyword">if</span> pool.currentState.GetBalance(<span class="hljs-keyword">from</span>).Cmp(<span class="hljs-built_in">tx</span>.Cost()) <span class="hljs-operator">&#x3C;</span> <span class="hljs-number">0</span> {
      <span class="hljs-keyword">return</span> ErrInsufficientFunds
   }
   <span class="hljs-comment">// Ensure the transaction has more gas than the basic tx fee.</span>
   intrGas, err :<span class="hljs-operator">=</span> IntrinsicGas(<span class="hljs-built_in">tx</span>.Data(), <span class="hljs-built_in">tx</span>.AccessList(), <span class="hljs-built_in">tx</span>.To() <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil, <span class="hljs-literal">true</span>, pool.istanbul)
   <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> err
   }
   <span class="hljs-keyword">if</span> <span class="hljs-built_in">tx</span>.Gas() <span class="hljs-operator">&#x3C;</span> intrGas {
      <span class="hljs-keyword">return</span> ErrIntrinsicGas
   }
   <span class="hljs-keyword">return</span> nil
}
</code></pre><pre data-type="codeBlock" text="//将交易添加到queue队列，这个队列是代表暂时不可处理的交易队列
func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local bool, addAll bool) (bool, error) {
   // Try to insert the transaction into the future queue
  //这个account是否有tx list，没有的话创建一个 
  from, _ := types.Sender(pool.signer, tx) // already validated
   if pool.queue[from] == nil {
      pool.queue[from] = newTxList(false)
   }
   inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump)
   if !inserted {
      // An older transaction was better, discard this
      queuedDiscardMeter.Mark(1)
      return false, ErrReplaceUnderpriced
   }
   // Discard any previous transaction and mark this
   if old != nil {
      pool.all.Remove(old.Hash())
      pool.priced.Removed(1)
      queuedReplaceMeter.Mark(1)
   } else {
      // Nothing was replaced, bump the queued counter
      queuedGauge.Inc(1)
   }
   // If the transaction isn&apos;t in lookup set but it&apos;s expected to be there,
   // show the error log.
//all队列是否有这笔交易
   if pool.all.Get(hash) == nil &amp;&amp; !addAll {
      log.Error(&quot;Missing transaction in lookup set, please report the issue&quot;, &quot;hash&quot;, hash)
   }
   if addAll {
//将这个tx添加到all队列
      pool.all.Add(tx, local)
      pool.priced.Put(tx, local)
   }
   // If we never record the heartbeat, do it right now.
   if _, exist := pool.beats[from]; !exist {
      pool.beats[from] = time.Now()
   }
   return old != nil, nil
}
"><code><span class="hljs-comment">//将交易添加到queue队列，这个队列是代表暂时不可处理的交易队列</span>
func (pool <span class="hljs-operator">*</span>TxPool) enqueueTx(hash common.Hash, <span class="hljs-built_in">tx</span> <span class="hljs-operator">*</span>types.Transaction, local <span class="hljs-keyword">bool</span>, addAll <span class="hljs-keyword">bool</span>) (<span class="hljs-keyword">bool</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
   <span class="hljs-comment">// Try to insert the transaction into the future queue</span>
  <span class="hljs-comment">//这个account是否有tx list，没有的话创建一个 </span>
  <span class="hljs-keyword">from</span>, <span class="hljs-keyword">_</span> :<span class="hljs-operator">=</span> types.Sender(pool.signer, <span class="hljs-built_in">tx</span>) <span class="hljs-comment">// already validated</span>
   <span class="hljs-keyword">if</span> pool.queue[<span class="hljs-keyword">from</span>] <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil {
      pool.queue[<span class="hljs-keyword">from</span>] <span class="hljs-operator">=</span> newTxList(<span class="hljs-literal">false</span>)
   }
   inserted, old :<span class="hljs-operator">=</span> pool.queue[<span class="hljs-keyword">from</span>].Add(<span class="hljs-built_in">tx</span>, pool.config.PriceBump)
   <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>inserted {
      <span class="hljs-comment">// An older transaction was better, discard this</span>
      queuedDiscardMeter.Mark(<span class="hljs-number">1</span>)
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>, ErrReplaceUnderpriced
   }
   <span class="hljs-comment">// Discard any previous transaction and mark this</span>
   <span class="hljs-keyword">if</span> old <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      pool.all.Remove(old.Hash())
      pool.priced.Removed(<span class="hljs-number">1</span>)
      queuedReplaceMeter.Mark(<span class="hljs-number">1</span>)
   } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// Nothing was replaced, bump the queued counter</span>
      queuedGauge.Inc(<span class="hljs-number">1</span>)
   }
   <span class="hljs-comment">// If the transaction isn't in lookup set but it's expected to be there,</span>
   <span class="hljs-comment">// show the error log.</span>
<span class="hljs-comment">//all队列是否有这笔交易</span>
   <span class="hljs-keyword">if</span> pool.all.Get(hash) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> <span class="hljs-operator">!</span>addAll {
      log.Error(<span class="hljs-string">"Missing transaction in lookup set, please report the issue"</span>, <span class="hljs-string">"hash"</span>, hash)
   }
   <span class="hljs-keyword">if</span> addAll {
<span class="hljs-comment">//将这个tx添加到all队列</span>
      pool.all.Add(<span class="hljs-built_in">tx</span>, local)
      pool.priced.Put(<span class="hljs-built_in">tx</span>, local)
   }
   <span class="hljs-comment">// If we never record the heartbeat, do it right now.</span>
   <span class="hljs-keyword">if</span> <span class="hljs-keyword">_</span>, exist :<span class="hljs-operator">=</span> pool.beats[<span class="hljs-keyword">from</span>]; <span class="hljs-operator">!</span>exist {
      pool.beats[<span class="hljs-keyword">from</span>] <span class="hljs-operator">=</span> time.Now()
   }
   <span class="hljs-keyword">return</span> old <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil, nil
}
</code></pre><pre data-type="codeBlock" text="func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Transaction {
   // Track the promoted transactions to broadcast them at once
   var promoted []*types.Transaction

   // Iterate over all accounts and promote any executable transactions
   for _, addr := range accounts {
      list := pool.queue[addr]
      if list == nil {
         continue // Just in case someone calls with a non existing account
      }
      // Drop all transactions that are deemed too old (low nonce)
// 1.1丢弃交易nonce值 &lt; 账户当前nonce的交易
      forwards := list.Forward(pool.currentState.GetNonce(addr))
      for _, tx := range forwards {
         hash := tx.Hash()
         pool.all.Remove(hash)
      }
      log.Trace(&quot;Removed old queued transactions&quot;, &quot;count&quot;, len(forwards))
      // Drop all transactions that are too costly (low balance or out of gas)
// 1.2.丢弃账户余额不足的
      drops, _ := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas)
      for _, tx := range drops {
         hash := tx.Hash()
         pool.all.Remove(hash)
      }
      log.Trace(&quot;Removed unpayable queued transactions&quot;, &quot;count&quot;, len(drops))
      queuedNofundsMeter.Mark(int64(len(drops)))

      // Gather all executable transactions and promote them
// 3.将交易添加到pending列表
      readies := list.Ready(pool.pendingNonces.get(addr))
      for _, tx := range readies {
         hash := tx.Hash()
         //广播tx
         if pool.promoteTx(addr, hash, tx) {
            promoted = append(promoted, tx)
         }
      }
      log.Trace(&quot;Promoted queued transactions&quot;, &quot;count&quot;, len(promoted))
      queuedGauge.Dec(int64(len(readies)))

      // Drop all transactions over the allowed limit
      var caps types.Transactions
      if !pool.locals.contains(addr) {
         caps = list.Cap(int(pool.config.AccountQueue))
         for _, tx := range caps {
            hash := tx.Hash()
            pool.all.Remove(hash)
            log.Trace(&quot;Removed cap-exceeding queued transaction&quot;, &quot;hash&quot;, hash)
         }
         queuedRateLimitMeter.Mark(int64(len(caps)))
      }
      // Mark all the items dropped as removed
      pool.priced.Removed(len(forwards) + len(drops) + len(caps))
      queuedGauge.Dec(int64(len(forwards) + len(drops) + len(caps)))
      if pool.locals.contains(addr) {
         localGauge.Dec(int64(len(forwards) + len(drops) + len(caps)))
      }
      // Delete the entire queue entry if it became empty.
      if list.Empty() {
         delete(pool.queue, addr)
         delete(pool.beats, addr)
      }
   }
   return promoted
}
"><code>func (pool <span class="hljs-operator">*</span>TxPool) promoteExecutables(accounts []common.Address) []<span class="hljs-operator">*</span>types.Transaction {
   <span class="hljs-comment">// Track the promoted transactions to broadcast them at once</span>
   <span class="hljs-keyword">var</span> promoted []<span class="hljs-operator">*</span>types.Transaction

   <span class="hljs-comment">// Iterate over all accounts and promote any executable transactions</span>
   <span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span>, addr :<span class="hljs-operator">=</span> range accounts {
      list :<span class="hljs-operator">=</span> pool.queue[addr]
      <span class="hljs-keyword">if</span> list <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil {
         <span class="hljs-keyword">continue</span> <span class="hljs-comment">// Just in case someone calls with a non existing account</span>
      }
      <span class="hljs-comment">// Drop all transactions that are deemed too old (low nonce)</span>
<span class="hljs-comment">// 1.1丢弃交易nonce值 &#x3C; 账户当前nonce的交易</span>
      forwards :<span class="hljs-operator">=</span> list.Forward(pool.currentState.GetNonce(addr))
      <span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span>, <span class="hljs-built_in">tx</span> :<span class="hljs-operator">=</span> range forwards {
         hash :<span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.Hash()
         pool.all.Remove(hash)
      }
      log.Trace(<span class="hljs-string">"Removed old queued transactions"</span>, <span class="hljs-string">"count"</span>, len(forwards))
      <span class="hljs-comment">// Drop all transactions that are too costly (low balance or out of gas)</span>
<span class="hljs-comment">// 1.2.丢弃账户余额不足的</span>
      drops, <span class="hljs-keyword">_</span> :<span class="hljs-operator">=</span> list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas)
      <span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span>, <span class="hljs-built_in">tx</span> :<span class="hljs-operator">=</span> range drops {
         hash :<span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.Hash()
         pool.all.Remove(hash)
      }
      log.Trace(<span class="hljs-string">"Removed unpayable queued transactions"</span>, <span class="hljs-string">"count"</span>, len(drops))
      queuedNofundsMeter.Mark(<span class="hljs-keyword">int64</span>(len(drops)))

      <span class="hljs-comment">// Gather all executable transactions and promote them</span>
<span class="hljs-comment">// 3.将交易添加到pending列表</span>
      readies :<span class="hljs-operator">=</span> list.Ready(pool.pendingNonces.get(addr))
      <span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span>, <span class="hljs-built_in">tx</span> :<span class="hljs-operator">=</span> range readies {
         hash :<span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.Hash()
         <span class="hljs-comment">//广播tx</span>
         <span class="hljs-keyword">if</span> pool.promoteTx(addr, hash, <span class="hljs-built_in">tx</span>) {
            promoted <span class="hljs-operator">=</span> append(promoted, <span class="hljs-built_in">tx</span>)
         }
      }
      log.Trace(<span class="hljs-string">"Promoted queued transactions"</span>, <span class="hljs-string">"count"</span>, len(promoted))
      queuedGauge.Dec(<span class="hljs-keyword">int64</span>(len(readies)))

      <span class="hljs-comment">// Drop all transactions over the allowed limit</span>
      <span class="hljs-keyword">var</span> caps types.Transactions
      <span class="hljs-keyword">if</span> <span class="hljs-operator">!</span>pool.locals.contains(addr) {
         caps <span class="hljs-operator">=</span> list.Cap(<span class="hljs-keyword">int</span>(pool.config.AccountQueue))
         <span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span>, <span class="hljs-built_in">tx</span> :<span class="hljs-operator">=</span> range caps {
            hash :<span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.Hash()
            pool.all.Remove(hash)
            log.Trace(<span class="hljs-string">"Removed cap-exceeding queued transaction"</span>, <span class="hljs-string">"hash"</span>, hash)
         }
         queuedRateLimitMeter.Mark(<span class="hljs-keyword">int64</span>(len(caps)))
      }
      <span class="hljs-comment">// Mark all the items dropped as removed</span>
      pool.priced.Removed(len(forwards) <span class="hljs-operator">+</span> len(drops) <span class="hljs-operator">+</span> len(caps))
      queuedGauge.Dec(<span class="hljs-keyword">int64</span>(len(forwards) <span class="hljs-operator">+</span> len(drops) <span class="hljs-operator">+</span> len(caps)))
      <span class="hljs-keyword">if</span> pool.locals.contains(addr) {
         localGauge.Dec(<span class="hljs-keyword">int64</span>(len(forwards) <span class="hljs-operator">+</span> len(drops) <span class="hljs-operator">+</span> len(caps)))
      }
      <span class="hljs-comment">// Delete the entire queue entry if it became empty.</span>
      <span class="hljs-keyword">if</span> list.Empty() {
         <span class="hljs-keyword">delete</span>(pool.queue, addr)
         <span class="hljs-keyword">delete</span>(pool.beats, addr)
      }
   }
   <span class="hljs-keyword">return</span> promoted
}
</code></pre>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[ETH源码学习（3）getBlock]]></title>
            <link>https://paragraph.com/@point/eth-3-getblock</link>
            <guid>5UOIGn6KlIXqywSgEmZB</guid>
            <pubDate>Thu, 12 May 2022 08:23:31 GMT</pubDate>
            <description><![CDATA[通过上篇getBalance和这个getBlock，理论上eth也能变成一个中心化的服务，因为用户和交易最终也都是保存到了数据库，那就和我们平时开发的项目没什么区别。这还蛮有意思的，eth实际上是区块链+传统项目的结合体。源码入口//命令 eth.getBlock(1,true) //跳过了前面几层 func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block { //用block number换block的hash hash := rawdb.ReadCanonicalHash(bc.db, number) if hash == (common.Hash{}) { return nil } //根据hash和number获取block return bc.GetBlock(hash, number) } //获取hash func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash { //这个还没弄明白 data, _ := db.A...]]></description>
            <content:encoded><![CDATA[<p>通过上篇getBalance和这个getBlock，理论上eth也能变成一个中心化的服务，因为用户和交易最终也都是保存到了数据库，那就和我们平时开发的项目没什么区别。这还蛮有意思的，eth实际上是区块链+传统项目的结合体。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">源码</h2><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">入口</h3><pre data-type="codeBlock" text="//命令
eth.getBlock(1,true)
//跳过了前面几层
func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
        //用block number换block的hash
    hash := rawdb.ReadCanonicalHash(bc.db, number)
    if hash == (common.Hash{}) {
        return nil
    }
        //根据hash和number获取block
    return bc.GetBlock(hash, number)
}
"><code><span class="hljs-comment">//命令</span>
eth.getBlock(<span class="hljs-number">1</span>,<span class="hljs-literal">true</span>)
<span class="hljs-comment">//跳过了前面几层</span>
func (bc <span class="hljs-operator">*</span>BlockChain) GetBlockByNumber(number <span class="hljs-keyword">uint64</span>) <span class="hljs-operator">*</span>types.Block {
        <span class="hljs-comment">//用block number换block的hash</span>
    hash :<span class="hljs-operator">=</span> rawdb.ReadCanonicalHash(bc.db, number)
    <span class="hljs-keyword">if</span> hash <span class="hljs-operator">=</span><span class="hljs-operator">=</span> (common.Hash{}) {
        <span class="hljs-keyword">return</span> nil
    }
        <span class="hljs-comment">//根据hash和number获取block</span>
    <span class="hljs-keyword">return</span> bc.GetBlock(hash, number)
}
</code></pre><pre data-type="codeBlock" text="//获取hash
func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash {
  //这个还没弄明白
   data, _ := db.Ancient(freezerHashTable, number)
   if len(data) == 0 {
  //从db中读
      data, _ = db.Get(headerHashKey(number))
      if len(data) == 0 {
         data, _ = db.Ancient(freezerHashTable, number)
      }
   }
   if len(data) == 0 {
      return common.Hash{}
   }
   return common.BytesToHash(data)
}
"><code><span class="hljs-comment">//获取hash</span>
func ReadCanonicalHash(db ethdb.Reader, number <span class="hljs-keyword">uint64</span>) common.Hash {
  <span class="hljs-comment">//这个还没弄明白</span>
   data, <span class="hljs-keyword">_</span> :<span class="hljs-operator">=</span> db.Ancient(freezerHashTable, number)
   <span class="hljs-keyword">if</span> len(data) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> {
  <span class="hljs-comment">//从db中读</span>
      data, <span class="hljs-keyword">_</span> <span class="hljs-operator">=</span> db.Get(headerHashKey(number))
      <span class="hljs-keyword">if</span> len(data) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> {
         data, <span class="hljs-keyword">_</span> <span class="hljs-operator">=</span> db.Ancient(freezerHashTable, number)
      }
   }
   <span class="hljs-keyword">if</span> len(data) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> {
      <span class="hljs-keyword">return</span> common.Hash{}
   }
   <span class="hljs-keyword">return</span> common.BytesToHash(data)
}
</code></pre><pre data-type="codeBlock" text="//获取区块
func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
   //从缓存中读，这里用到了lur
   if block, ok := bc.blockCache.Get(hash); ok {
      return block.(*types.Block)
   }
//从db中读，内容和getbalance一样，里面是先获取header，再获取body，然后创建区块，将header和body copy到block返回
   block := rawdb.ReadBlock(bc.db, hash, number)
   if block == nil {
      return nil
   }
//缓存区块
   bc.blockCache.Add(block.Hash(), block)
   return block
}
"><code><span class="hljs-comment">//获取区块</span>
func (bc <span class="hljs-operator">*</span>BlockChain) GetBlock(hash common.Hash, number <span class="hljs-keyword">uint64</span>) <span class="hljs-operator">*</span>types.Block {
   <span class="hljs-comment">//从缓存中读，这里用到了lur</span>
   <span class="hljs-keyword">if</span> <span class="hljs-built_in">block</span>, ok :<span class="hljs-operator">=</span> bc.blockCache.Get(hash); ok {
      <span class="hljs-keyword">return</span> <span class="hljs-built_in">block</span>.(<span class="hljs-operator">*</span>types.Block)
   }
<span class="hljs-comment">//从db中读，内容和getbalance一样，里面是先获取header，再获取body，然后创建区块，将header和body copy到block返回</span>
   <span class="hljs-built_in">block</span> :<span class="hljs-operator">=</span> rawdb.ReadBlock(bc.db, hash, number)
   <span class="hljs-keyword">if</span> <span class="hljs-built_in">block</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> nil
   }
<span class="hljs-comment">//缓存区块</span>
   bc.blockCache.Add(<span class="hljs-built_in">block</span>.Hash(), <span class="hljs-built_in">block</span>)
   <span class="hljs-keyword">return</span> <span class="hljs-built_in">block</span>
}
</code></pre><p>两篇get方法都是大同小异，进一步了解到eth是可以很简单的实现的，那些很牛逼的东西，都是为了满足区块链、性能这些需求。</p><p>另外这篇内容的主要代码都在blockchain内，又一次出现了这个，说明很重要，而statedb却没有出现，目前可以猜测statedb和账户有关，和block无关，需要后续验证。</p><p>也能知道leveldb里大致存的内容</p><ol><li><p>用户数据</p></li><li><p>header</p></li><li><p>number→hash</p></li><li><p>body,body里存了tx，uncle</p></li><li><p>可以想象还有receipt</p></li></ol><pre data-type="codeBlock" text="//block的例子
{
  difficulty: 1461115576915,
  extraData: &quot;0x476574682f76312e302e312f6c696e75782f676f312e342e32&quot;,
  gasLimit: 22150,
  gasUsed: 21000,
  hash: &quot;0xeecb25ce31fb7a212500443a31d463b663a2c5f65e7838c5364bb8110a0750f1&quot;,
  logsBloom: &quot;0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000&quot;,
  miner: &quot;0x47ff6576639c2e94762ea5443978d7681c0e78dc&quot;,
  mixHash: &quot;0xaaad7bf5c87f1b2fc08ed73a1519dae6832b458a112891cd7c5a0a86125a8321&quot;,
  nonce: &quot;0x4e164418632effd2&quot;,
  number: 46251,
  parentHash: &quot;0x3c93a7ff6fb6f39e63456e9ea6fb069c7783f3c00b1012e36f0a8a02bdae604a&quot;,
  receiptsRoot: &quot;0x5add83c7805c02ab079871e3a2379b6a764d34e557d758ddce00b6a1bf5cc79f&quot;,
  sha3Uncles: &quot;0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347&quot;,
  size: 652,
  stateRoot: &quot;0x7692ecfeac33453620701bad9eae23f89c01e845274f0e5adec78a5ea96f5083&quot;,
  timestamp: 1438919990,
  totalDifficulty: 42835839136947252,
  transactions: [{
      blockHash: &quot;0xeecb25ce31fb7a212500443a31d463b663a2c5f65e7838c5364bb8110a0750f1&quot;,
      blockNumber: 46251,
      from: &quot;0xfd2605a2bf58fdbb90db1da55df61628b47f9e8c&quot;,
      gas: 21000,
      gasPrice: 88893746891,
      hash: &quot;0x304346de678ddca5b1311970e7e8d3f26a42d2924aaa2f2d73b8781a9a9a2c73&quot;,
      input: &quot;0x&quot;,
      nonce: 1,
      r: &quot;0x26f3e5c541f526b549d9f003c8e7de442d3cf4bbf9c66934027672693a88275f&quot;,
      s: &quot;0x7bd40d54be85b58444d6f0a28f22591474a84380fc8a6a7515bda42b5ebb0e2b&quot;,
      to: &quot;0x073f70b5bfade6409e4951ef72bc8f4157677729&quot;,
      transactionIndex: 0,
      v: &quot;0x1c&quot;,
      value: 10000000000000000
  }],
  transactionsRoot: &quot;0xf642520ac0ce7008292d2c835b8d2e6eb90822f8a68b8ae368b0774229371b5d&quot;,
  uncles: []
}
"><code><span class="hljs-string">//block的例子</span>
{
  <span class="hljs-attr">difficulty:</span> <span class="hljs-number">1461115576915</span>,
  <span class="hljs-attr">extraData:</span> <span class="hljs-string">"0x476574682f76312e302e312f6c696e75782f676f312e342e32"</span>,
  <span class="hljs-attr">gasLimit:</span> <span class="hljs-number">22150</span>,
  <span class="hljs-attr">gasUsed:</span> <span class="hljs-number">21000</span>,
  <span class="hljs-attr">hash:</span> <span class="hljs-string">"0xeecb25ce31fb7a212500443a31d463b663a2c5f65e7838c5364bb8110a0750f1"</span>,
  <span class="hljs-attr">logsBloom:</span> <span class="hljs-string">"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"</span>,
  <span class="hljs-attr">miner:</span> <span class="hljs-string">"0x47ff6576639c2e94762ea5443978d7681c0e78dc"</span>,
  <span class="hljs-attr">mixHash:</span> <span class="hljs-string">"0xaaad7bf5c87f1b2fc08ed73a1519dae6832b458a112891cd7c5a0a86125a8321"</span>,
  <span class="hljs-attr">nonce:</span> <span class="hljs-string">"0x4e164418632effd2"</span>,
  <span class="hljs-attr">number:</span> <span class="hljs-number">46251</span>,
  <span class="hljs-attr">parentHash:</span> <span class="hljs-string">"0x3c93a7ff6fb6f39e63456e9ea6fb069c7783f3c00b1012e36f0a8a02bdae604a"</span>,
  <span class="hljs-attr">receiptsRoot:</span> <span class="hljs-string">"0x5add83c7805c02ab079871e3a2379b6a764d34e557d758ddce00b6a1bf5cc79f"</span>,
  <span class="hljs-attr">sha3Uncles:</span> <span class="hljs-string">"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"</span>,
  <span class="hljs-attr">size:</span> <span class="hljs-number">652</span>,
  <span class="hljs-attr">stateRoot:</span> <span class="hljs-string">"0x7692ecfeac33453620701bad9eae23f89c01e845274f0e5adec78a5ea96f5083"</span>,
  <span class="hljs-attr">timestamp:</span> <span class="hljs-number">1438919990</span>,
  <span class="hljs-attr">totalDifficulty:</span> <span class="hljs-number">42835839136947252</span>,
  <span class="hljs-attr">transactions:</span> [{
      <span class="hljs-attr">blockHash:</span> <span class="hljs-string">"0xeecb25ce31fb7a212500443a31d463b663a2c5f65e7838c5364bb8110a0750f1"</span>,
      <span class="hljs-attr">blockNumber:</span> <span class="hljs-number">46251</span>,
      <span class="hljs-attr">from:</span> <span class="hljs-string">"0xfd2605a2bf58fdbb90db1da55df61628b47f9e8c"</span>,
      <span class="hljs-attr">gas:</span> <span class="hljs-number">21000</span>,
      <span class="hljs-attr">gasPrice:</span> <span class="hljs-number">88893746891</span>,
      <span class="hljs-attr">hash:</span> <span class="hljs-string">"0x304346de678ddca5b1311970e7e8d3f26a42d2924aaa2f2d73b8781a9a9a2c73"</span>,
      <span class="hljs-attr">input:</span> <span class="hljs-string">"0x"</span>,
      <span class="hljs-attr">nonce:</span> <span class="hljs-number">1</span>,
      <span class="hljs-attr">r:</span> <span class="hljs-string">"0x26f3e5c541f526b549d9f003c8e7de442d3cf4bbf9c66934027672693a88275f"</span>,
      <span class="hljs-attr">s:</span> <span class="hljs-string">"0x7bd40d54be85b58444d6f0a28f22591474a84380fc8a6a7515bda42b5ebb0e2b"</span>,
      <span class="hljs-attr">to:</span> <span class="hljs-string">"0x073f70b5bfade6409e4951ef72bc8f4157677729"</span>,
      <span class="hljs-attr">transactionIndex:</span> <span class="hljs-number">0</span>,
      <span class="hljs-attr">v:</span> <span class="hljs-string">"0x1c"</span>,
      <span class="hljs-attr">value:</span> <span class="hljs-number">10000000000000000</span>
  }],
  <span class="hljs-attr">transactionsRoot:</span> <span class="hljs-string">"0xf642520ac0ce7008292d2c835b8d2e6eb90822f8a68b8ae368b0774229371b5d"</span>,
  <span class="hljs-attr">uncles:</span> []
}
</code></pre>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[ETH源码学习（1）创建私有链]]></title>
            <link>https://paragraph.com/@point/eth-1</link>
            <guid>RAbGNwYSQnLv3ZoW2Po1</guid>
            <pubDate>Thu, 12 May 2022 08:03:29 GMT</pubDate>
            <description><![CDATA[下载geth，创建genesis.json{ "config": { "chainId": 8888, //自行修改 "homesteadBlock": 0, "daoForkBlock": 0, "daoForkSupport": true, "eip150Block": 0, "eip155Block": 0, "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, "ethash": {} }, "nonce": "0x42", "timestamp": "0x0", "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", "gasLimit": "0xffffffff", "difficulty": "0x700000",//难度，越大挖矿越慢 "alloc": { "093f59f1d91017d30d8c2caa78feb5beb0d2cfaf...]]></description>
            <content:encoded><![CDATA[<p>下载geth，创建genesis.json</p><pre data-type="codeBlock" text="{
    &quot;config&quot;: {
        &quot;chainId&quot;: 8888, //自行修改
        &quot;homesteadBlock&quot;: 0,
        &quot;daoForkBlock&quot;: 0,
        &quot;daoForkSupport&quot;: true,
        &quot;eip150Block&quot;: 0,
        &quot;eip155Block&quot;: 0,
        &quot;eip158Block&quot;: 0,
        &quot;byzantiumBlock&quot;: 0,
        &quot;constantinopleBlock&quot;: 0,
        &quot;petersburgBlock&quot;: 0,
        &quot;ethash&quot;: {}
    },
    &quot;nonce&quot;: &quot;0x42&quot;,
    &quot;timestamp&quot;: &quot;0x0&quot;,
    &quot;extraData&quot;: &quot;0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa&quot;,
    &quot;gasLimit&quot;: &quot;0xffffffff&quot;,
    &quot;difficulty&quot;: &quot;0x700000&quot;,//难度，越大挖矿越慢
    &quot;alloc&quot;: {
        &quot;093f59f1d91017d30d8c2caa78feb5beb0d2cfaf&quot;: {
            &quot;balance&quot;: &quot;0xffffffffffffffff&quot;
        },
        &quot;ddf7202cbe0aaed1c2d5c4ef05e386501a054406&quot;: {
            &quot;balance&quot;: &quot;0xffffffffffffffff&quot;
        }
    }
}
"><code><span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"config"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
        <span class="hljs-attr">"chainId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">8888</span><span class="hljs-punctuation">,</span> <span class="hljs-comment">//自行修改</span>
        <span class="hljs-attr">"homesteadBlock"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"daoForkBlock"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"daoForkSupport"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"eip150Block"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"eip155Block"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"eip158Block"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"byzantiumBlock"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"constantinopleBlock"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"petersburgBlock"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"ethash"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><span class="hljs-punctuation">}</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"nonce"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0x42"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"timestamp"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0x0"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"extraData"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"gasLimit"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xffffffff"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"difficulty"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0x700000"</span><span class="hljs-punctuation">,</span><span class="hljs-comment">//难度，越大挖矿越慢</span>
    <span class="hljs-attr">"alloc"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
        <span class="hljs-attr">"093f59f1d91017d30d8c2caa78feb5beb0d2cfaf"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
            <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xffffffffffffffff"</span>
        <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">"ddf7202cbe0aaed1c2d5c4ef05e386501a054406"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
            <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xffffffffffffffff"</span>
        <span class="hljs-punctuation">}</span>
    <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre><p>因为要分析源码，所以不是像网上命令行执行，而是使用goland debug</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/af19010dce4685525a5478495e6a3470fbe712d1c8c3a0abd810a1dbd9f4003e.png" alt="执行命令" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">执行命令</figcaption></figure><pre data-type="codeBlock" text="参数
--datadir &quot;/Users/zhangnan/Downloads/self_eth/&quot; init &quot;/Users/zhangnan/Downloads/geth/genesis.json&quot;
"><code>参数
<span class="hljs-operator">-</span><span class="hljs-operator">-</span>datadir <span class="hljs-string">"/Users/zhangnan/Downloads/self_eth/"</span> init <span class="hljs-string">"/Users/zhangnan/Downloads/geth/genesis.json"</span>
</code></pre><pre data-type="codeBlock" text="初始化
func initGenesis(ctx *cli.Context) error {
    // Make sure we have a valid genesis JSON
    genesisPath := ctx.Args().First()
    if len(genesisPath) == 0 {
        utils.Fatalf(&quot;Must supply path to genesis JSON file&quot;)
    }
    file, err := os.Open(genesisPath)
    if err != nil {
        utils.Fatalf(&quot;Failed to read genesis file: %v&quot;, err)
    }
    defer file.Close()

    genesis := new(core.Genesis)
    if err := json.NewDecoder(file).Decode(genesis); err != nil {
        utils.Fatalf(&quot;invalid genesis file: %v&quot;, err)
    }
//以上是读初始化文件，下面开始第一个重要步骤，配置config
    // Open and initialise both full and light databases
    stack, _ := makeConfigNode(ctx)
    defer stack.Close()

    for _, name := range []string{&quot;chaindata&quot;, &quot;lightchaindata&quot;} {
        chaindb, err := stack.OpenDatabase(name, 0, 0, &quot;&quot;, false)
        if err != nil {
            utils.Fatalf(&quot;Failed to open database: %v&quot;, err)
        }
        _, hash, err := core.SetupGenesisBlock(chaindb, genesis)
        if err != nil {
            utils.Fatalf(&quot;Failed to write genesis block: %v&quot;, err)
        }
        chaindb.Close()
        log.Info(&quot;Successfully wrote genesis state&quot;, &quot;database&quot;, name, &quot;hash&quot;, hash)
    }
    return nil
}
"><code>初始化
func initGenesis(ctx <span class="hljs-operator">*</span>cli.Context) <span class="hljs-function"><span class="hljs-keyword">error</span> </span>{
    <span class="hljs-comment">// Make sure we have a valid genesis JSON</span>
    genesisPath :<span class="hljs-operator">=</span> ctx.Args().First()
    <span class="hljs-keyword">if</span> len(genesisPath) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> {
        utils.Fatalf(<span class="hljs-string">"Must supply path to genesis JSON file"</span>)
    }
    file, err :<span class="hljs-operator">=</span> os.Open(genesisPath)
    <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
        utils.Fatalf(<span class="hljs-string">"Failed to read genesis file: %v"</span>, err)
    }
    defer file.Close()

    genesis :<span class="hljs-operator">=</span> <span class="hljs-keyword">new</span>(core.Genesis)
    <span class="hljs-keyword">if</span> err :<span class="hljs-operator">=</span> json.NewDecoder(file).Decode(genesis); err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
        utils.Fatalf(<span class="hljs-string">"invalid genesis file: %v"</span>, err)
    }
<span class="hljs-comment">//以上是读初始化文件，下面开始第一个重要步骤，配置config</span>
    <span class="hljs-comment">// Open and initialise both full and light databases</span>
    stack, <span class="hljs-keyword">_</span> :<span class="hljs-operator">=</span> makeConfigNode(ctx)
    defer stack.Close()

    <span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span>, name :<span class="hljs-operator">=</span> range []<span class="hljs-keyword">string</span>{<span class="hljs-string">"chaindata"</span>, <span class="hljs-string">"lightchaindata"</span>} {
        chaindb, err :<span class="hljs-operator">=</span> stack.OpenDatabase(name, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-string">""</span>, <span class="hljs-literal">false</span>)
        <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
            utils.Fatalf(<span class="hljs-string">"Failed to open database: %v"</span>, err)
        }
        <span class="hljs-keyword">_</span>, hash, err :<span class="hljs-operator">=</span> core.SetupGenesisBlock(chaindb, genesis)
        <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
            utils.Fatalf(<span class="hljs-string">"Failed to write genesis block: %v"</span>, err)
        }
        chaindb.Close()
        log.Info(<span class="hljs-string">"Successfully wrote genesis state"</span>, <span class="hljs-string">"database"</span>, name, <span class="hljs-string">"hash"</span>, hash)
    }
    <span class="hljs-keyword">return</span> nil
}
</code></pre>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[ETH源码学习（2）GetBalance]]></title>
            <link>https://paragraph.com/@point/eth-2-getbalance</link>
            <guid>ipbH5kFQKE1g2OBF8g3F</guid>
            <pubDate>Thu, 12 May 2022 04:31:33 GMT</pubDate>
            <description><![CDATA[从这开始读，因为最简单。还能对重要的东西有所理解。如果是单节点debug，重启链之后余额会归零，所以最少要两个节点，同步区块就没这个问题了，具体原因还未知。 声明：eth版本：Geth/v1.10.7-unstable/darwin-arm64/go1.17.5，我对比了网上的一些文章，这块代码有改动，主要以下改变从BlockChain的snap中读account几乎放弃从trie中获取account，判断极为严苛，我想不到有什么情况能进入，应该是放弃了这层分析入口//命令 eth.getBalance('0x0d7dd6dbabee2ec9b325aa7aa8b42d75068e8597') //入口 func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { //获取statedb state, _, err := s.b.StateAndHea...]]></description>
            <content:encoded><![CDATA[<p>从这开始读，因为最简单。还能对重要的东西有所理解。如果是单节点debug，重启链之后余额会归零，所以最少要两个节点，同步区块就没这个问题了，具体原因还未知。</p><p>声明：eth版本：Geth/v1.10.7-unstable/darwin-arm64/go1.17.5，我对比了网上的一些文章，这块代码有改动，主要以下改变</p><ol><li><p>从BlockChain的snap中读account</p></li><li><p>几乎放弃从trie中获取account，判断极为严苛，我想不到有什么情况能进入，应该是放弃了这层</p></li></ol><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">分析</h2><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">入口</h3><pre data-type="codeBlock" text="//命令
eth.getBalance(&apos;0x0d7dd6dbabee2ec9b325aa7aa8b42d75068e8597&apos;)

//入口
func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) {
  //获取statedb
   state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
   if state == nil || err != nil {
      return nil, err
   }
//获取余额
   return (*hexutil.Big)(state.GetBalance(address)), state.Error()
}
"><code><span class="hljs-comment">//命令</span>
eth.getBalance(<span class="hljs-string">'0x0d7dd6dbabee2ec9b325aa7aa8b42d75068e8597'</span>)

<span class="hljs-comment">//入口</span>
func (s <span class="hljs-operator">*</span>PublicBlockChainAPI) GetBalance(ctx context.Context, <span class="hljs-keyword">address</span> common.Address, blockNrOrHash rpc.BlockNumberOrHash) (<span class="hljs-operator">*</span>hexutil.Big, <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
  <span class="hljs-comment">//获取statedb</span>
   state, <span class="hljs-keyword">_</span>, err :<span class="hljs-operator">=</span> s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
   <span class="hljs-keyword">if</span> state <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil <span class="hljs-operator">|</span><span class="hljs-operator">|</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> nil, err
   }
<span class="hljs-comment">//获取余额</span>
   <span class="hljs-keyword">return</span> (<span class="hljs-operator">*</span>hexutil.Big)(state.GetBalance(<span class="hljs-keyword">address</span>)), state.Error()
}
</code></pre><p>创建statedb</p><pre data-type="codeBlock" text="func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
//根据最新的hader state root创建statedb
   if blockNr, ok := blockNrOrHash.Number(); ok {
      return b.StateAndHeaderByNumber(ctx, blockNr)
   }
   if hash, ok := blockNrOrHash.Hash(); ok {
      header, err := b.HeaderByHash(ctx, hash)
      if err != nil {
         return nil, nil, err
      }
      if header == nil {
         return nil, nil, errors.New(&quot;header for hash not found&quot;)
      }
      if blockNrOrHash.RequireCanonical &amp;&amp; b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
         return nil, nil, errors.New(&quot;hash is not currently canonical&quot;)
      }
      stateDb, err := b.eth.BlockChain().StateAt(header.Root)
      return stateDb, header, err
   }
   return nil, nil, errors.New(&quot;invalid arguments; neither block nor hash specified&quot;)
}
"><code>func (b <span class="hljs-operator">*</span>EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (<span class="hljs-operator">*</span>state.StateDB, <span class="hljs-operator">*</span>types.Header, <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
<span class="hljs-comment">//根据最新的hader state root创建statedb</span>
   <span class="hljs-keyword">if</span> blockNr, ok :<span class="hljs-operator">=</span> blockNrOrHash.Number(); ok {
      <span class="hljs-keyword">return</span> b.StateAndHeaderByNumber(ctx, blockNr)
   }
   <span class="hljs-keyword">if</span> hash, ok :<span class="hljs-operator">=</span> blockNrOrHash.Hash(); ok {
      header, err :<span class="hljs-operator">=</span> b.HeaderByHash(ctx, hash)
      <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
         <span class="hljs-keyword">return</span> nil, nil, err
      }
      <span class="hljs-keyword">if</span> header <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil {
         <span class="hljs-keyword">return</span> nil, nil, errors.New(<span class="hljs-string">"header for hash not found"</span>)
      }
      <span class="hljs-keyword">if</span> blockNrOrHash.RequireCanonical <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> hash {
         <span class="hljs-keyword">return</span> nil, nil, errors.New(<span class="hljs-string">"hash is not currently canonical"</span>)
      }
      stateDb, err :<span class="hljs-operator">=</span> b.eth.BlockChain().StateAt(header.Root)
      <span class="hljs-keyword">return</span> stateDb, header, err
   }
   <span class="hljs-keyword">return</span> nil, nil, errors.New(<span class="hljs-string">"invalid arguments; neither block nor hash specified"</span>)
}
</code></pre><pre data-type="codeBlock" text="func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
   // Pending state is only known by the miner
  //有Pending的区块，从这里面获取statedb
   if number == rpc.PendingBlockNumber {
      block, state := b.eth.miner.Pending()
      return state, block.Header(), nil
   }
   // Otherwise resolve the block number and return its state
   header, err := b.HeaderByNumber(ctx, number)
   if err != nil {
      return nil, nil, err
   }
   if header == nil {
      return nil, nil, errors.New(&quot;header not found&quot;)
   }
//创建新的statedb
   stateDb, err := b.eth.BlockChain().StateAt(header.Root)
   return stateDb, header, err
}
"><code>func (b <span class="hljs-operator">*</span>EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (<span class="hljs-operator">*</span>state.StateDB, <span class="hljs-operator">*</span>types.Header, <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
   <span class="hljs-comment">// Pending state is only known by the miner</span>
  <span class="hljs-comment">//有Pending的区块，从这里面获取statedb</span>
   <span class="hljs-keyword">if</span> number <span class="hljs-operator">=</span><span class="hljs-operator">=</span> rpc.PendingBlockNumber {
      <span class="hljs-built_in">block</span>, state :<span class="hljs-operator">=</span> b.eth.miner.Pending()
      <span class="hljs-keyword">return</span> state, <span class="hljs-built_in">block</span>.Header(), nil
   }
   <span class="hljs-comment">// Otherwise resolve the block number and return its state</span>
   header, err :<span class="hljs-operator">=</span> b.HeaderByNumber(ctx, number)
   <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> nil, nil, err
   }
   <span class="hljs-keyword">if</span> header <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> nil, nil, errors.New(<span class="hljs-string">"header not found"</span>)
   }
<span class="hljs-comment">//创建新的statedb</span>
   stateDb, err :<span class="hljs-operator">=</span> b.eth.BlockChain().StateAt(header.Root)
   <span class="hljs-keyword">return</span> stateDb, header, err
}
</code></pre><pre data-type="codeBlock" text="//主要做了两件事，创建trie和snap
func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) {
   fmt.Println(&quot;root=====&quot;, root.String())
   tr, err := db.OpenTrie(root)
   if err != nil {
      return nil, err
   }
   sdb := &amp;StateDB{
      db:                  db,
      trie:                tr,
      originalRoot:        root,
      snaps:               snaps,
      stateObjects:        make(map[common.Address]*stateObject),
      stateObjectsPending: make(map[common.Address]struct{}),
      stateObjectsDirty:   make(map[common.Address]struct{}),
      logs:                make(map[common.Hash][]*types.Log),
      preimages:           make(map[common.Hash][]byte),
      journal:             newJournal(),
      accessList:          newAccessList(),
      hasher:              crypto.NewKeccakState(),
   }
   if sdb.snaps != nil {
      if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap != nil {
         sdb.snapDestructs = make(map[common.Hash]struct{})
         sdb.snapAccounts = make(map[common.Hash][]byte)
         sdb.snapStorage = make(map[common.Hash]map[common.Hash][]byte)
      }
   }
   return sdb, nil
}
"><code><span class="hljs-comment">//主要做了两件事，创建trie和snap</span>
func New(root common.Hash, db Database, snaps <span class="hljs-operator">*</span>snapshot.Tree) (<span class="hljs-operator">*</span>StateDB, <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
   fmt.Println(<span class="hljs-string">"root====="</span>, root.String())
   tr, err :<span class="hljs-operator">=</span> db.OpenTrie(root)
   <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> nil, err
   }
   sdb :<span class="hljs-operator">=</span> <span class="hljs-operator">&#x26;</span>StateDB{
      db:                  db,
      trie:                tr,
      originalRoot:        root,
      snaps:               snaps,
      stateObjects:        make(map[common.Address]<span class="hljs-operator">*</span>stateObject),
      stateObjectsPending: make(map[common.Address]<span class="hljs-keyword">struct</span>{}),
      stateObjectsDirty:   make(map[common.Address]<span class="hljs-keyword">struct</span>{}),
      logs:                make(map[common.Hash][]<span class="hljs-operator">*</span>types.Log),
      preimages:           make(map[common.Hash][]<span class="hljs-keyword">byte</span>),
      journal:             newJournal(),
      accessList:          newAccessList(),
      hasher:              crypto.NewKeccakState(),
   }
   <span class="hljs-keyword">if</span> sdb.snaps <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">if</span> sdb.snap <span class="hljs-operator">=</span> sdb.snaps.Snapshot(root); sdb.snap <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
         sdb.snapDestructs <span class="hljs-operator">=</span> make(map[common.Hash]<span class="hljs-keyword">struct</span>{})
         sdb.snapAccounts <span class="hljs-operator">=</span> make(map[common.Hash][]<span class="hljs-keyword">byte</span>)
         sdb.snapStorage <span class="hljs-operator">=</span> make(map[common.Hash]map[common.Hash][]<span class="hljs-keyword">byte</span>)
      }
   }
   <span class="hljs-keyword">return</span> sdb, nil
}
</code></pre><h3 id="h-statedbbalance" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">statedb获取完毕，获取balance</h3><p>回到最开始从GetBalance进入</p><pre data-type="codeBlock" text="func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
   fmt.Println(&quot;addr=====&quot;, addr.String())
   // Prefer live objects if any is available
  //从stateobject中获取
   if obj := s.stateObjects[addr]; obj != nil {
      return obj
   }
   // If no live objects are available, attempt to use snapshots
   var (
      data *Account
      err  error
   )
//从缓存中获取
   if s.snap != nil {
      if metrics.EnabledExpensive {
         defer func(start time.Time) { s.SnapshotAccountReads += time.Since(start) }(time.Now())
      }
      var acc *snapshot.Account
      if acc, err = s.snap.Account(crypto.HashData(s.hasher, addr.Bytes())); err == nil {
         if acc == nil {
            return nil
         }
         data = &amp;Account{
            Nonce:    acc.Nonce,
            Balance:  acc.Balance,
            CodeHash: acc.CodeHash,
            Root:     common.BytesToHash(acc.Root),
         }
         if len(data.CodeHash) == 0 {
            data.CodeHash = emptyCodeHash
         }
         if data.Root == (common.Hash{}) {
            data.Root = emptyRoot
         }
      }
   }
   // If snapshot unavailable or reading from it failed, load from the database
//从trie中获取
   if s.snap == nil || err != nil {
      if metrics.EnabledExpensive {
         defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now())
      }
      enc, err := s.trie.TryGet(addr.Bytes())
      if err != nil {
         s.setError(fmt.Errorf(&quot;getDeleteStateObject (%x) error: %v&quot;, addr.Bytes(), err))
         return nil
      }
      if len(enc) == 0 {
         return nil
      }
      data = new(Account)
      if err := rlp.DecodeBytes(enc, data); err != nil {
         log.Error(&quot;Failed to decode state object&quot;, &quot;addr&quot;, addr, &quot;err&quot;, err)
         return nil
      }
   }
   // Insert into the live set
   obj := newObject(s, addr, *data)
   s.setStateObject(obj)
   return obj
}
"><code>func (s <span class="hljs-operator">*</span>StateDB) getDeletedStateObject(addr common.Address) <span class="hljs-operator">*</span>stateObject {
   fmt.Println(<span class="hljs-string">"addr====="</span>, addr.String())
   <span class="hljs-comment">// Prefer live objects if any is available</span>
  <span class="hljs-comment">//从stateobject中获取</span>
   <span class="hljs-keyword">if</span> obj :<span class="hljs-operator">=</span> s.stateObjects[addr]; obj <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> obj
   }
   <span class="hljs-comment">// If no live objects are available, attempt to use snapshots</span>
   <span class="hljs-keyword">var</span> (
      data <span class="hljs-operator">*</span>Account
      err  <span class="hljs-function"><span class="hljs-keyword">error</span>
   )
<span class="hljs-comment">//从缓存中获取</span>
   <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">s</span>.<span class="hljs-title">snap</span> != <span class="hljs-title">nil</span> </span>{
      <span class="hljs-keyword">if</span> metrics.EnabledExpensive {
         defer func(start time.Time) { s.SnapshotAccountReads <span class="hljs-operator">+</span><span class="hljs-operator">=</span> time.Since(start) }(time.Now())
      }
      <span class="hljs-keyword">var</span> acc <span class="hljs-operator">*</span>snapshot.Account
      <span class="hljs-keyword">if</span> acc, err <span class="hljs-operator">=</span> s.snap.Account(crypto.HashData(s.hasher, addr.Bytes())); err <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil {
         <span class="hljs-keyword">if</span> acc <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil {
            <span class="hljs-keyword">return</span> nil
         }
         data <span class="hljs-operator">=</span> <span class="hljs-operator">&#x26;</span>Account{
            Nonce:    acc.Nonce,
            Balance:  acc.Balance,
            CodeHash: acc.CodeHash,
            Root:     common.BytesToHash(acc.Root),
         }
         <span class="hljs-keyword">if</span> len(data.CodeHash) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> {
            data.CodeHash <span class="hljs-operator">=</span> emptyCodeHash
         }
         <span class="hljs-keyword">if</span> data.Root <span class="hljs-operator">=</span><span class="hljs-operator">=</span> (common.Hash{}) {
            data.Root <span class="hljs-operator">=</span> emptyRoot
         }
      }
   }
   <span class="hljs-comment">// If snapshot unavailable or reading from it failed, load from the database</span>
<span class="hljs-comment">//从trie中获取</span>
   <span class="hljs-keyword">if</span> s.snap <span class="hljs-operator">=</span><span class="hljs-operator">=</span> nil <span class="hljs-operator">|</span><span class="hljs-operator">|</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">if</span> metrics.EnabledExpensive {
         defer func(start time.Time) { s.AccountReads <span class="hljs-operator">+</span><span class="hljs-operator">=</span> time.Since(start) }(time.Now())
      }
      enc, err :<span class="hljs-operator">=</span> s.trie.TryGet(addr.Bytes())
      <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
         s.setError(fmt.Errorf(<span class="hljs-string">"getDeleteStateObject (%x) error: %v"</span>, addr.Bytes(), err))
         <span class="hljs-keyword">return</span> nil
      }
      <span class="hljs-keyword">if</span> len(enc) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> {
         <span class="hljs-keyword">return</span> nil
      }
      data <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span>(Account)
      <span class="hljs-keyword">if</span> err :<span class="hljs-operator">=</span> rlp.DecodeBytes(enc, data); err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
         log.Error(<span class="hljs-string">"Failed to decode state object"</span>, <span class="hljs-string">"addr"</span>, addr, <span class="hljs-string">"err"</span>, err)
         <span class="hljs-keyword">return</span> nil
      }
   }
   <span class="hljs-comment">// Insert into the live set</span>
   obj :<span class="hljs-operator">=</span> newObject(s, addr, <span class="hljs-operator">*</span>data)
   s.setStateObject(obj)
   <span class="hljs-keyword">return</span> obj
}
</code></pre><pre data-type="codeBlock" text="func (dl *diskLayer) AccountRLP(hash common.Hash) ([]byte, error) {
    dl.lock.RLock()
    defer dl.lock.RUnlock()

    // If the layer was flattened into, consider it invalid (any live reference to
    // the original should be marked as unusable).
    if dl.stale {
        return nil, ErrSnapshotStale
    }
    // If the layer is being generated, ensure the requested hash has already been
    // covered by the generator.
    if dl.genMarker != nil &amp;&amp; bytes.Compare(hash[:], dl.genMarker) &gt; 0 {
        return nil, ErrNotCoveredYet
    }
    // If we&apos;re in the disk layer, all diff layers missed
    snapshotDirtyAccountMissMeter.Mark(1)

    // Try to retrieve the account from the memory cache
        //从缓存中读
    if blob, found := dl.cache.HasGet(nil, hash[:]); found {
        snapshotCleanAccountHitMeter.Mark(1)
        snapshotCleanAccountReadMeter.Mark(int64(len(blob)))
        return blob, nil
    }
    // Cache doesn&apos;t contain account, pull from disk and cache for later
        //从硬盘中读
    blob := rawdb.ReadAccountSnapshot(dl.diskdb, hash)
    dl.cache.Set(hash[:], blob)

    snapshotCleanAccountMissMeter.Mark(1)
    if n := len(blob); n &gt; 0 {
        snapshotCleanAccountWriteMeter.Mark(int64(n))
    } else {
        snapshotCleanAccountInexMeter.Mark(1)
    }
    return blob, nil
}
"><code>func (dl <span class="hljs-operator">*</span>diskLayer) AccountRLP(hash common.Hash) ([]<span class="hljs-keyword">byte</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
    dl.lock.RLock()
    defer dl.lock.RUnlock()

    <span class="hljs-comment">// If the layer was flattened into, consider it invalid (any live reference to</span>
    <span class="hljs-comment">// the original should be marked as unusable).</span>
    <span class="hljs-keyword">if</span> dl.stale {
        <span class="hljs-keyword">return</span> nil, ErrSnapshotStale
    }
    <span class="hljs-comment">// If the layer is being generated, ensure the requested hash has already been</span>
    <span class="hljs-comment">// covered by the generator.</span>
    <span class="hljs-keyword">if</span> dl.genMarker <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> <span class="hljs-built_in">bytes</span>.Compare(hash[:], dl.genMarker) <span class="hljs-operator">></span> <span class="hljs-number">0</span> {
        <span class="hljs-keyword">return</span> nil, ErrNotCoveredYet
    }
    <span class="hljs-comment">// If we're in the disk layer, all diff layers missed</span>
    snapshotDirtyAccountMissMeter.Mark(<span class="hljs-number">1</span>)

    <span class="hljs-comment">// Try to retrieve the account from the memory cache</span>
        <span class="hljs-comment">//从缓存中读</span>
    <span class="hljs-keyword">if</span> blob, found :<span class="hljs-operator">=</span> dl.cache.HasGet(nil, hash[:]); found {
        snapshotCleanAccountHitMeter.Mark(<span class="hljs-number">1</span>)
        snapshotCleanAccountReadMeter.Mark(<span class="hljs-keyword">int64</span>(len(blob)))
        <span class="hljs-keyword">return</span> blob, nil
    }
    <span class="hljs-comment">// Cache doesn't contain account, pull from disk and cache for later</span>
        <span class="hljs-comment">//从硬盘中读</span>
    blob :<span class="hljs-operator">=</span> rawdb.ReadAccountSnapshot(dl.diskdb, hash)
    dl.cache.Set(hash[:], blob)

    snapshotCleanAccountMissMeter.Mark(<span class="hljs-number">1</span>)
    <span class="hljs-keyword">if</span> n :<span class="hljs-operator">=</span> len(blob); n <span class="hljs-operator">></span> <span class="hljs-number">0</span> {
        snapshotCleanAccountWriteMeter.Mark(<span class="hljs-keyword">int64</span>(n))
    } <span class="hljs-keyword">else</span> {
        snapshotCleanAccountInexMeter.Mark(<span class="hljs-number">1</span>)
    }
    <span class="hljs-keyword">return</span> blob, nil
}
</code></pre><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">总结</h2><p>get balance和传统项目的思路差不多，一层层读缓存，最终读数据库，然后再保存缓存，返回前端</p><ol><li><p>getBalance要先获取最新区块的root（stateRoot）来换stateDB</p></li><li><p>然后从stateDB的stateObject中获取</p></li><li><p>没有的话好像从snap中获取</p><ol><li><p>先读缓存</p></li><li><p>再读levelDB</p></li><li><p>命中的话缓存至快照</p></li></ol></li><li><p>没有的话再从trie中获取</p></li><li><p>命中则缓存至stateObject</p></li><li><p>返回前端</p></li></ol><p>从上面了解到stateDB这个东西很重要，所有的操作都是围绕他来完成的。stateDB又是靠BlockChain里的数据来创建的</p><ol><li><p>最新区块的state root</p></li><li><p>snaps</p></li><li><p>stateCache</p></li></ol><p>这样就有了个大致的影响，然后通过后面的内容，更加深入的了解eth的架构</p>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[APE土拍代码分析]]></title>
            <link>https://paragraph.com/@point/ape</link>
            <guid>3YeRvRsEth0NtGqgIgaq</guid>
            <pubDate>Fri, 06 May 2022 06:18:37 GMT</pubDate>
            <description><![CDATA[纯属个人收集整理资料+揣测理解，我也不是NFT玩家，也没这个资金实力玩NFT。所以，理解上没有专业人士准确。业务梳理固定305APE价格售卖售卖分为kyc pre-mint(Contributor才行，应该就是白名单吧)、公售（大家抢）、持有无聊猿和变异猿的人可以再mint5.1买的是盲盒，5.2拆盒operator操作记录operator也是操作了这些事情，业务理解应该没太大问题代码注释删掉了一些业务无关、重复、无需理解的代码 代码还算简单，只有通过随机数将土地和nft721的metadata关联起来有点疑问 通过链上操作可以知道tokenurl是在土拍前就设置了，并且再也没有改过，所以不是其他项目那样卖完后通过改url拆盒 项目方也上传了metadatahashes，所以所有nft metadata是事先就准备好了的 链上的随机数并没有参与链上业务的处理，比如通过随机数+tokenid获取metadata url 既然链上的方法都没有使用，那么这个关联操作应该是放在了链下， api.otherside.xyz/lands/27893 访问这个地址后，通过他们未开源的算法，将2...]]></description>
            <content:encoded><![CDATA[<p>纯属个人收集整理资料+揣测理解，我也不是NFT玩家，也没这个资金实力玩NFT。所以，理解上没有专业人士准确。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">业务梳理</h2><ol><li><p>固定305APE价格售卖</p></li><li><p>售卖分为kyc pre-mint(Contributor才行，应该就是白名单吧)、公售（大家抢）、持有无聊猿和变异猿的人可以再mint</p></li><li><p>5.1买的是盲盒，5.2拆盒</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d58a2f74191644d1c2382f4bdd171a28dcb552acd8238c649a176dfeb9a0b071.png" alt="operator操作记录" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">operator操作记录</figcaption></figure><p>operator也是操作了这些事情，业务理解应该没太大问题</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">代码注释</h2><p>删掉了一些业务无关、重复、无需理解的代码</p><p>代码还算简单，只有通过随机数将土地和nft721的metadata关联起来有点疑问</p><p>通过链上操作可以知道tokenurl是在土拍前就设置了，并且再也没有改过，所以不是其他项目那样卖完后通过改url拆盒</p><p>项目方也上传了metadatahashes，所以所有nft metadata是事先就准备好了的</p><p>链上的随机数并没有参与链上业务的处理，比如通过随机数+tokenid获取metadata url</p><p>既然链上的方法都没有使用，那么这个关联操作应该是放在了链下，</p><p>api.otherside.xyz/lands/27893 访问这个地址后，通过他们未开源的算法，将27893+随机数指向了metaurl。但是这样是不是就能够通过修改算法，改变用户的nft了。。。。。。。这点没有想明白，如果有好心人愿意指导下我，可以<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="mailto:zn978740431@gmail.com">zn978740431@gmail.com</a>给我发邮件。</p><p>网上找到两个还算靠谱的实现文章，看起来都是在链上完成这个操作的，搞不明白ape是怎么想的。因为很多nft项目方在这上面做手脚，还是很想理解透这个事情。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://vocus.cc/article/6212f73ffd89780001687585">https://vocus.cc/article/6212f73ffd89780001687585</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zombit.info/write-to-the-nft-project/">https://zombit.info/write-to-the-nft-project/</a></p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

contract Land  {
using SafeERC20 for IERC20;
// attributes
string private baseURI;
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> 0.8.10;</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Land</span>  </span>{
<span class="hljs-keyword">using</span> <span class="hljs-title">SafeERC20</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">IERC20</span>;
<span class="hljs-comment">// attributes</span>
<span class="hljs-keyword">string</span> <span class="hljs-keyword">private</span> baseURI;
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7c14b330a7de2577e3e77db313a4cce12f53d209d34e7881b693114ca42acac9.png" alt="url" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">url</figcaption></figure><pre data-type="codeBlock" text="address public operator;
//公售开关
bool public publicSaleActive;
//公售时间
uint256 public publicSaleStartTime;
//荷兰拍用的
uint256 public publicSalePriceLoweringDuration;
uint256 public publicSaleStartPrice;
uint256 public publicSaleEndingPrice;
//荷兰拍用的

//记录当前的土地数量
uint256 public currentNumLandsMintedPublicSale;
//当前mint tokenId
uint256 public mintIndexPublicSaleAndContributors;
//ape地址，用于交钱
address public tokenContract;
//是否需要验证kyc
bool private isKycCheckRequired;
//验证kyc的hash root， Merkle tree验证
bytes32 public kycMerkleRoot;
"><code><span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> operator;
<span class="hljs-comment">//公售开关</span>
<span class="hljs-keyword">bool</span> <span class="hljs-keyword">public</span> publicSaleActive;
<span class="hljs-comment">//公售时间</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> publicSaleStartTime;
<span class="hljs-comment">//荷兰拍用的</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> publicSalePriceLoweringDuration;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> publicSaleStartPrice;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> publicSaleEndingPrice;
<span class="hljs-comment">//荷兰拍用的</span>

<span class="hljs-comment">//记录当前的土地数量</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> currentNumLandsMintedPublicSale;
<span class="hljs-comment">//当前mint tokenId</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> mintIndexPublicSaleAndContributors;
<span class="hljs-comment">//ape地址，用于交钱</span>
<span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> tokenContract;
<span class="hljs-comment">//是否需要验证kyc</span>
<span class="hljs-keyword">bool</span> <span class="hljs-keyword">private</span> isKycCheckRequired;
<span class="hljs-comment">//验证kyc的hash root， Merkle tree验证</span>
<span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">public</span> kycMerkleRoot;
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/493410298735c77260bf04ea2c6f33947391993fd23bc354443ce54d88c31cbb.png" alt="kyc hash" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">kyc hash</figcaption></figure><pre data-type="codeBlock" text="//每次最大可以mint的上限
uint256 public maxMintPerTx;
//每个地址总共mint的上限
uint256 public maxMintPerAddress;
//每个地址mint的数量
mapping(address =&gt; uint256) public mintedPerAddress;
//无聊猿和变异猿是否可以额外mint
bool public claimableActive; 
bool public adminClaimStarted;

//无聊猿mint的信息
address public alphaContract; 
mapping(uint256 =&gt; bool) public alphaClaimed;
uint256 public alphaClaimedAmount;
//变异mint的信息
address public betaContract; 
mapping(uint256 =&gt; bool) public betaClaimed;
uint256 public betaClaimedAmount;
uint256 public betaNftIdCurrent;

//贡献者mint的信息
bool public contributorsClaimActive;
mapping(address =&gt; uint256) public contributors;
uint256 public futureLandsNftIdCurrent;

//未来还可以再mint
address public futureMinter;

//保证官方没有作弊，用于所有人拿这里面的数据进行验证
Metadata[] public metadataHashes;
//随机数seed, 线上结果       0xaa77729d3466ca35ae8d28b3bbac7cc36a5031efdc430821c02bc31a238af445
bytes32 public keyHash;
//请求随机数的费用
uint256 public fee;
//下面三个获得的随机数然后计算出来的偏移量 线上结果 35117
uint256 public publicSaleAndContributorsOffset;
//线上结果 5491
uint256 public alphaOffset;
//线上结果 15491
uint256 public betaOffset;
//是否请求过随机数
mapping(bytes32 =&gt; bool) public isRandomRequestForPublicSaleAndContributors;
bool public publicSaleAndContributorsRandomnessRequested;
bool public ownerClaimRandomnessRequested;

// constants
uint256 immutable public MAX_LANDS;
uint256 immutable public MAX_LANDS_WITH_FUTURE;
uint256 immutable public MAX_ALPHA_NFT_AMOUNT;
uint256 immutable public MAX_BETA_NFT_AMOUNT;
uint256 immutable public MAX_PUBLIC_SALE_AMOUNT;
uint256 immutable public RESERVED_CONTRIBUTORS_AMOUNT;
uint256 immutable public MAX_FUTURE_LANDS;
uint256 constant public MAX_MINT_PER_BLOCK = 150;

// structs
struct LandAmount {
    uint256 alpha;
    uint256 beta;
    uint256 publicSale;
    uint256 future;
}
struct ContributorAmount {
    address contributor;
    uint256 amount;
}

struct Metadata {
    bytes32 metadataHash;
    bytes32 shuffledArrayHash;
    uint256 startIndex;
    uint256 endIndex;
}
"><code><span class="hljs-comment">//每次最大可以mint的上限</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> maxMintPerTx;
<span class="hljs-comment">//每个地址总共mint的上限</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> maxMintPerAddress;
<span class="hljs-comment">//每个地址mint的数量</span>
<span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>) <span class="hljs-keyword">public</span> mintedPerAddress;
<span class="hljs-comment">//无聊猿和变异猿是否可以额外mint</span>
<span class="hljs-keyword">bool</span> <span class="hljs-keyword">public</span> claimableActive; 
<span class="hljs-keyword">bool</span> <span class="hljs-keyword">public</span> adminClaimStarted;

<span class="hljs-comment">//无聊猿mint的信息</span>
<span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> alphaContract; 
<span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">bool</span>) <span class="hljs-keyword">public</span> alphaClaimed;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> alphaClaimedAmount;
<span class="hljs-comment">//变异mint的信息</span>
<span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> betaContract; 
<span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">bool</span>) <span class="hljs-keyword">public</span> betaClaimed;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> betaClaimedAmount;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> betaNftIdCurrent;

<span class="hljs-comment">//贡献者mint的信息</span>
<span class="hljs-keyword">bool</span> <span class="hljs-keyword">public</span> contributorsClaimActive;
<span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>) <span class="hljs-keyword">public</span> contributors;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> futureLandsNftIdCurrent;

<span class="hljs-comment">//未来还可以再mint</span>
<span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> futureMinter;

<span class="hljs-comment">//保证官方没有作弊，用于所有人拿这里面的数据进行验证</span>
Metadata[] <span class="hljs-keyword">public</span> metadataHashes;
<span class="hljs-comment">//随机数seed, 线上结果       0xaa77729d3466ca35ae8d28b3bbac7cc36a5031efdc430821c02bc31a238af445</span>
<span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">public</span> keyHash;
<span class="hljs-comment">//请求随机数的费用</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> fee;
<span class="hljs-comment">//下面三个获得的随机数然后计算出来的偏移量 线上结果 35117</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> publicSaleAndContributorsOffset;
<span class="hljs-comment">//线上结果 5491</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> alphaOffset;
<span class="hljs-comment">//线上结果 15491</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> betaOffset;
<span class="hljs-comment">//是否请求过随机数</span>
<span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">bytes32</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">bool</span>) <span class="hljs-keyword">public</span> isRandomRequestForPublicSaleAndContributors;
<span class="hljs-keyword">bool</span> <span class="hljs-keyword">public</span> publicSaleAndContributorsRandomnessRequested;
<span class="hljs-keyword">bool</span> <span class="hljs-keyword">public</span> ownerClaimRandomnessRequested;

<span class="hljs-comment">// constants</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">immutable</span> <span class="hljs-keyword">public</span> MAX_LANDS;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">immutable</span> <span class="hljs-keyword">public</span> MAX_LANDS_WITH_FUTURE;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">immutable</span> <span class="hljs-keyword">public</span> MAX_ALPHA_NFT_AMOUNT;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">immutable</span> <span class="hljs-keyword">public</span> MAX_BETA_NFT_AMOUNT;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">immutable</span> <span class="hljs-keyword">public</span> MAX_PUBLIC_SALE_AMOUNT;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">immutable</span> <span class="hljs-keyword">public</span> RESERVED_CONTRIBUTORS_AMOUNT;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">immutable</span> <span class="hljs-keyword">public</span> MAX_FUTURE_LANDS;
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">constant</span> <span class="hljs-keyword">public</span> MAX_MINT_PER_BLOCK <span class="hljs-operator">=</span> <span class="hljs-number">150</span>;

<span class="hljs-comment">// structs</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title">LandAmount</span> {
    <span class="hljs-keyword">uint256</span> alpha;
    <span class="hljs-keyword">uint256</span> beta;
    <span class="hljs-keyword">uint256</span> publicSale;
    <span class="hljs-keyword">uint256</span> future;
}
<span class="hljs-keyword">struct</span> <span class="hljs-title">ContributorAmount</span> {
    <span class="hljs-keyword">address</span> contributor;
    <span class="hljs-keyword">uint256</span> amount;
}

<span class="hljs-keyword">struct</span> <span class="hljs-title">Metadata</span> {
    <span class="hljs-keyword">bytes32</span> metadataHash;
    <span class="hljs-keyword">bytes32</span> shuffledArrayHash;
    <span class="hljs-keyword">uint256</span> startIndex;
    <span class="hljs-keyword">uint256</span> endIndex;
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/142498e63d9cfe25212a2324b86c972bca2a357f0c8c067531fffdb63589db5f.png" alt="metadata" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">metadata</figcaption></figure><pre data-type="codeBlock" text="struct ContractAddresses {
    address alphaContract;
    address betaContract;
    address tokenContract;
}

// modifiers
。。。。。。。。。。

// events
。。。。。。。。。。

constructor(string memory name, string memory symbol,
    ContractAddresses memory addresses,
    LandAmount memory amount,
    ContributorAmount[] memory _contributors,
    address _vrfCoordinator, address _linkTokenAddress,
    bytes32 _vrfKeyHash, uint256 _vrfFee,
    address _operator
) ERC721(name, symbol) VRFConsumerBase(_vrfCoordinator, _linkTokenAddress) {
    //无聊猿和变异猿的地址，持有这个的能够mint（具体的业务忘记了）
    alphaContract = addresses.alphaContract;
    betaContract = addresses.betaContract;
    tokenContract = addresses.tokenContract;

    MAX_ALPHA_NFT_AMOUNT = amount.alpha;
    MAX_BETA_NFT_AMOUNT = amount.beta;
    MAX_PUBLIC_SALE_AMOUNT = amount.publicSale;
    MAX_FUTURE_LANDS = amount.future;

    betaNftIdCurrent = amount.alpha; //beta starts after alpha
    mintIndexPublicSaleAndContributors = amount.alpha + amount.beta; //public sale starts after beta

    //贡献者mint数量
    uint256 tempSum;
    for(uint256 i; i&lt;_contributors.length; ++i){
        contributors[_contributors[i].contributor] = _contributors[i].amount;
        tempSum += _contributors[i].amount;
    }
    RESERVED_CONTRIBUTORS_AMOUNT = tempSum;
    //总数量
    MAX_LANDS = amount.alpha + amount.beta + amount.publicSale + RESERVED_CONTRIBUTORS_AMOUNT;
    //以后还可以mint的数量
    MAX_LANDS_WITH_FUTURE = MAX_LANDS + amount.future;
    futureLandsNftIdCurrent = MAX_LANDS; //future starts after public sale
    //用于随机数请求
    keyHash  = _vrfKeyHash;
    fee = _vrfFee;
    //操作者地址
    operator = _operator;
}

//一些set函数
。。。。。。。。。。

// Public Sale Methods
//公售
function startPublicSale(
    uint256 _publicSalePriceLoweringDuration, 
    uint256 _publicSaleStartPrice, 
    uint256 _publicSaleEndingPrice,
    uint256 _maxMintPerTx,
    uint256 _maxMintPerAddress,
    bool _isKycCheckRequired
) external onlyOperator {
    require(!publicSaleActive, &quot;Public sale has already begun&quot;);
    //不用管，荷兰拍取消了
    publicSalePriceLoweringDuration = _publicSalePriceLoweringDuration;
    publicSaleStartPrice = _publicSaleStartPrice;
    publicSaleEndingPrice = _publicSaleEndingPrice;
    publicSaleStartTime = block.timestamp;
    publicSaleActive = true;
    //每次，每个地址的mint限制
    maxMintPerTx = _maxMintPerTx;
    maxMintPerAddress = _maxMintPerAddress;

    isKycCheckRequired = _isKycCheckRequired;

    emit LandPublicSaleStart(publicSalePriceLoweringDuration, publicSaleStartTime);
}
"><code><span class="hljs-keyword">struct</span> <span class="hljs-title">ContractAddresses</span> {
    <span class="hljs-keyword">address</span> alphaContract;
    <span class="hljs-keyword">address</span> betaContract;
    <span class="hljs-keyword">address</span> tokenContract;
}

<span class="hljs-comment">// modifiers</span>
。。。。。。。。。。

<span class="hljs-comment">// events</span>
。。。。。。。。。。

<span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> name, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> symbol,
    ContractAddresses <span class="hljs-keyword">memory</span> addresses,
    LandAmount <span class="hljs-keyword">memory</span> amount,
    ContributorAmount[] <span class="hljs-keyword">memory</span> _contributors,
    <span class="hljs-keyword">address</span> _vrfCoordinator, <span class="hljs-keyword">address</span> _linkTokenAddress,
    <span class="hljs-keyword">bytes32</span> _vrfKeyHash, <span class="hljs-keyword">uint256</span> _vrfFee,
    <span class="hljs-keyword">address</span> _operator
</span>) <span class="hljs-title">ERC721</span>(<span class="hljs-params">name, symbol</span>) <span class="hljs-title">VRFConsumerBase</span>(<span class="hljs-params">_vrfCoordinator, _linkTokenAddress</span>) </span>{
    <span class="hljs-comment">//无聊猿和变异猿的地址，持有这个的能够mint（具体的业务忘记了）</span>
    alphaContract <span class="hljs-operator">=</span> addresses.alphaContract;
    betaContract <span class="hljs-operator">=</span> addresses.betaContract;
    tokenContract <span class="hljs-operator">=</span> addresses.tokenContract;

    MAX_ALPHA_NFT_AMOUNT <span class="hljs-operator">=</span> amount.alpha;
    MAX_BETA_NFT_AMOUNT <span class="hljs-operator">=</span> amount.beta;
    MAX_PUBLIC_SALE_AMOUNT <span class="hljs-operator">=</span> amount.publicSale;
    MAX_FUTURE_LANDS <span class="hljs-operator">=</span> amount.future;

    betaNftIdCurrent <span class="hljs-operator">=</span> amount.alpha; <span class="hljs-comment">//beta starts after alpha</span>
    mintIndexPublicSaleAndContributors <span class="hljs-operator">=</span> amount.alpha <span class="hljs-operator">+</span> amount.beta; <span class="hljs-comment">//public sale starts after beta</span>

    <span class="hljs-comment">//贡献者mint数量</span>
    <span class="hljs-keyword">uint256</span> tempSum;
    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">uint256</span> i; i<span class="hljs-operator">&#x3C;</span>_contributors.<span class="hljs-built_in">length</span>; <span class="hljs-operator">+</span><span class="hljs-operator">+</span>i){
        contributors[_contributors[i].contributor] <span class="hljs-operator">=</span> _contributors[i].amount;
        tempSum <span class="hljs-operator">+</span><span class="hljs-operator">=</span> _contributors[i].amount;
    }
    RESERVED_CONTRIBUTORS_AMOUNT <span class="hljs-operator">=</span> tempSum;
    <span class="hljs-comment">//总数量</span>
    MAX_LANDS <span class="hljs-operator">=</span> amount.alpha <span class="hljs-operator">+</span> amount.beta <span class="hljs-operator">+</span> amount.publicSale <span class="hljs-operator">+</span> RESERVED_CONTRIBUTORS_AMOUNT;
    <span class="hljs-comment">//以后还可以mint的数量</span>
    MAX_LANDS_WITH_FUTURE <span class="hljs-operator">=</span> MAX_LANDS <span class="hljs-operator">+</span> amount.future;
    futureLandsNftIdCurrent <span class="hljs-operator">=</span> MAX_LANDS; <span class="hljs-comment">//future starts after public sale</span>
    <span class="hljs-comment">//用于随机数请求</span>
    keyHash  <span class="hljs-operator">=</span> _vrfKeyHash;
    fee <span class="hljs-operator">=</span> _vrfFee;
    <span class="hljs-comment">//操作者地址</span>
    operator <span class="hljs-operator">=</span> _operator;
}

<span class="hljs-comment">//一些set函数</span>
。。。。。。。。。。

<span class="hljs-comment">// Public Sale Methods</span>
<span class="hljs-comment">//公售</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">startPublicSale</span>(<span class="hljs-params">
    <span class="hljs-keyword">uint256</span> _publicSalePriceLoweringDuration, 
    <span class="hljs-keyword">uint256</span> _publicSaleStartPrice, 
    <span class="hljs-keyword">uint256</span> _publicSaleEndingPrice,
    <span class="hljs-keyword">uint256</span> _maxMintPerTx,
    <span class="hljs-keyword">uint256</span> _maxMintPerAddress,
    <span class="hljs-keyword">bool</span> _isKycCheckRequired
</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOperator</span> </span>{
    <span class="hljs-built_in">require</span>(<span class="hljs-operator">!</span>publicSaleActive, <span class="hljs-string">"Public sale has already begun"</span>);
    <span class="hljs-comment">//不用管，荷兰拍取消了</span>
    publicSalePriceLoweringDuration <span class="hljs-operator">=</span> _publicSalePriceLoweringDuration;
    publicSaleStartPrice <span class="hljs-operator">=</span> _publicSaleStartPrice;
    publicSaleEndingPrice <span class="hljs-operator">=</span> _publicSaleEndingPrice;
    publicSaleStartTime <span class="hljs-operator">=</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>;
    publicSaleActive <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    <span class="hljs-comment">//每次，每个地址的mint限制</span>
    maxMintPerTx <span class="hljs-operator">=</span> _maxMintPerTx;
    maxMintPerAddress <span class="hljs-operator">=</span> _maxMintPerAddress;

    isKycCheckRequired <span class="hljs-operator">=</span> _isKycCheckRequired;

    <span class="hljs-keyword">emit</span> LandPublicSaleStart(publicSalePriceLoweringDuration, publicSaleStartTime);
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d20d55308bf2acc7c38cd99f3c272293d74b2497cd1375e95216b6d757c57ae6.png" alt="startPublicSale function params" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">startPublicSale function params</figcaption></figure><pre data-type="codeBlock" text="
//停止公售
function stopPublicSale() external onlyOperator whenPublicSaleActive {
    emit LandPublicSaleStop(getMintPrice(), getElapsedSaleTime());
    publicSaleActive = false;
}
//本来是荷兰拍的，取消了，改成固定价格
function getElapsedSaleTime() private view returns (uint256) {
    return publicSaleStartTime &gt; 0 ? block.timestamp - publicSaleStartTime : 0;
}

function getMintPrice() public view whenPublicSaleActive returns (uint256) {
    uint256 elapsed = getElapsedSaleTime();
    uint256 price;

    if(elapsed &lt; publicSalePriceLoweringDuration) {
        // Linear decreasing function
        price =
            publicSaleStartPrice -
                ( ( publicSaleStartPrice - publicSaleEndingPrice ) * elapsed ) / publicSalePriceLoweringDuration ;
    } else {
        price = publicSaleEndingPrice;
    }

    return price;
}

//mint land
function mintLands(uint256 numLands, bytes32[] calldata merkleProof) external whenPublicSaleActive nonReentrant {
    //最少一块土地
    require(numLands &gt; 0, &quot;Must mint at least one beta&quot;);
    //mint的土地有没有超过最大限制
    require(currentNumLandsMintedPublicSale + numLands &lt;= MAX_PUBLIC_SALE_AMOUNT, &quot;Minting would exceed max supply&quot;);
    //每次mint限制
    require(numLands &lt;= maxMintPerTx, &quot;numLands should not exceed maxMintPerTx&quot;);
    //每个人的mint限制
    require(numLands + mintedPerAddress[msg.sender] &lt;= maxMintPerAddress, &quot;sender address cannot mint more than maxMintPerAddress lands&quot;);
    //是否需要kyc验证，贡献者需要，公售的不要
    if(isKycCheckRequired) {
        require(MerkleProof.verify(merkleProof, kycMerkleRoot, keccak256(abi.encodePacked(msg.sender))), &quot;Sender address is not in KYC allowlist&quot;);
    } else {
        //不允许合约等间接调用，老老实实正常操作
        require(msg.sender == tx.origin, &quot;Minting from smart contracts is disallowed&quot;);
    }
 
    //获得土地价格
    uint256 mintPrice = getMintPrice();
    //交钱
    IERC20(tokenContract).safeTransferFrom(msg.sender, address(this), mintPrice * numLands);
    //记录当前的土地数量
    currentNumLandsMintedPublicSale += numLands;
    //记录每个用户的的土地数量
    mintedPerAddress[msg.sender] += numLands;
    emit PublicSaleMint(msg.sender, numLands, mintPrice);
    //mint land
    mintLandsCommon(numLands, msg.sender);
}
//调用erc721 mint
function mintLandsCommon(uint256 numLands, address recipient) private {
    for (uint256 i; i &lt; numLands; ++i) {
        _safeMint(recipient, mintIndexPublicSaleAndContributors++);
    }
}
//赚钱喽，取钱
function withdraw() external onlyOwner {
    uint256 balance = address(this).balance;
    if(balance &gt; 0){
        Address.sendValue(payable(owner()), balance);
    }

    balance = IERC20(tokenContract).balanceOf(address(this));
    if(balance &gt; 0){
        IERC20(tokenContract).safeTransfer(owner(), balance);
    }
}

// Alpha/Beta Claim Methods
//无聊猿和变异猿持有者是否可以再mint
function flipClaimableState() external onlyOperator {
    claimableActive = !claimableActive;
    emit ClaimableStateChanged(claimableActive);
}

//无聊猿和变异猿持有者再mint
function nftOwnerClaimLand(uint256[] calldata alphaTokenIds, uint256[] calldata betaTokenIds) external whenClaimableActive {
    require(alphaTokenIds.length &gt; 0 || betaTokenIds.length &gt; 0, &quot;Should claim at least one land&quot;);
    require(alphaTokenIds.length + betaTokenIds.length &lt;= MAX_MINT_PER_BLOCK, &quot;Input length should be &lt;= MAX_MINT_PER_BLOCK&quot;);
    //无聊猿持有者mint
    alphaClaimLand(alphaTokenIds);
    //变异猿持有者mint
    betaClaimLand(betaTokenIds);
}

function alphaClaimLand(uint256[] calldata alphaTokenIds) private {
    for(uint256 i; i &lt; alphaTokenIds.length; ++i){
        uint256 alphaTokenId = alphaTokenIds[i];
        require(!alphaClaimed[alphaTokenId], &quot;ALPHA NFT already claimed&quot;);
        require(ERC721(alphaContract).ownerOf(alphaTokenId) == msg.sender, &quot;Must own all of the alpha defined by alphaTokenIds&quot;);
        
        alphaClaimLandByTokenId(alphaTokenId);    
    }
}

function alphaClaimLandByTokenId(uint256 alphaTokenId) private {
    alphaClaimed[alphaTokenId] = true;
    ++alphaClaimedAmount;        
    _safeMint(msg.sender, alphaTokenId);
}


// Contributors Claim Methods
//开始贡献者mint
function startContributorsClaimPeriod() onlyOperator external {
    require(!contributorsClaimActive, &quot;Contributors claim is already active&quot;);
    contributorsClaimActive = true;
    emit ContributorsClaimStart(block.timestamp);
}
//结束贡献者mint
function stopContributorsClaimPeriod() onlyOperator external whenContributorsClaimActive {
    contributorsClaimActive = false;
    emit ContributorsClaimStop(block.timestamp);
}

function contributorsClaimLand(uint256 amount, address recipient) external onlyContributors(msg.sender) whenContributorsClaimActive {
    require(amount &gt; 0, &quot;Must mint at least one land&quot;);
    require(amount &lt;= MAX_MINT_PER_BLOCK, &quot;amount should not exceed MAX_MINT_PER_BLOCK&quot;);
    require(amount &lt;= contributors[msg.sender], &quot;Contributor cannot claim other lands&quot;);

    contributors[msg.sender] -= amount;
    mintLandsCommon(amount, recipient);
}
//应该是把没卖完的土地mint掉，但是不可能出现这种事情的吧。。。。留个后手总归好的
function claimUnclaimedAndUnsoldLands(address recipient) external onlyOwner {
    claimUnclaimedAndUnsoldLandsWithAmount(recipient, MAX_MINT_PER_BLOCK);
}
。。。。。。。。。

// metadata
//用于给别人验证，保证我的土拍是没有作弊的
function loadLandMetadata(Metadata memory _landMetadata)
    external onlyOperator checkMetadataRange(_landMetadata)
    checkFirstMetadataRange(metadataHashes.length, _landMetadata.startIndex, _landMetadata.endIndex)
{
    metadataHashes.push(_landMetadata);
} 

function putLandMetadataAtIndex(uint256 index, Metadata memory _landMetadata)
    external onlyOperator checkMetadataRange(_landMetadata)
    checkFirstMetadataRange(index, _landMetadata.startIndex, _landMetadata.endIndex)
{
    metadataHashes[index] = _landMetadata;
}     

// randomness
//用link vrf生成随机数，这个随机数给公售和贡献者用
function requestRandomnessForPublicSaleAndContributors() external onlyOperator returns (bytes32 requestId) {
    require(!publicSaleAndContributorsRandomnessRequested, &quot;Public Sale And Contributors Offset already requested&quot;);
    publicSaleAndContributorsRandomnessRequested = true;
    requestId = requestRandomnessPrivate();
    isRandomRequestForPublicSaleAndContributors[requestId] = true;
}

//用link vrf生成随机数，这个随机数给猿持有者用
function requestRandomnessForOwnerClaim() external onlyOperator returns (bytes32 requestId) {
    require(!ownerClaimRandomnessRequested, &quot;Owner Claim Offset already requested&quot;);
    ownerClaimRandomnessRequested = true;
    requestId = requestRandomnessPrivate();
    isRandomRequestForPublicSaleAndContributors[requestId] = false;
}

function requestRandomnessPrivate() private returns (bytes32 requestId) {
    require(
        LINK.balanceOf(address(this)) &gt;= fee,
        &quot;Not enough LINK&quot;
    );
    return requestRandomness(keyHash, fee);
}
//link会把产生的随机数回调这个函数，然后项目方进行业务处理，这里猜测应该是概率不同，我持有nft，那拿到好土地的概率会更大
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
    //区分是公售、贡献者还是猿持有者随机数
    if(isRandomRequestForPublicSaleAndContributors[requestId]){
        publicSaleAndContributorsOffset = (randomness % (MAX_PUBLIC_SALE_AMOUNT + RESERVED_CONTRIBUTORS_AMOUNT));
        emit StartingIndexSetPublicSale(publicSaleAndContributorsOffset);
    } else {
        alphaOffset = (randomness % MAX_ALPHA_NFT_AMOUNT);
        betaOffset = (randomness % MAX_BETA_NFT_AMOUNT);
        emit StartingIndexSetAlphaBeta(alphaOffset, betaOffset);
    }
}
"><code>
<span class="hljs-comment">//停止公售</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">stopPublicSale</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOperator</span> <span class="hljs-title">whenPublicSaleActive</span> </span>{
    <span class="hljs-keyword">emit</span> LandPublicSaleStop(getMintPrice(), getElapsedSaleTime());
    publicSaleActive <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
}
<span class="hljs-comment">//本来是荷兰拍的，取消了，改成固定价格</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getElapsedSaleTime</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">private</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
    <span class="hljs-keyword">return</span> publicSaleStartTime <span class="hljs-operator">></span> <span class="hljs-number">0</span> ? <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">-</span> publicSaleStartTime : <span class="hljs-number">0</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMintPrice</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title">whenPublicSaleActive</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
    <span class="hljs-keyword">uint256</span> elapsed <span class="hljs-operator">=</span> getElapsedSaleTime();
    <span class="hljs-keyword">uint256</span> price;

    <span class="hljs-keyword">if</span>(elapsed <span class="hljs-operator">&#x3C;</span> publicSalePriceLoweringDuration) {
        <span class="hljs-comment">// Linear decreasing function</span>
        price <span class="hljs-operator">=</span>
            publicSaleStartPrice <span class="hljs-operator">-</span>
                ( ( publicSaleStartPrice <span class="hljs-operator">-</span> publicSaleEndingPrice ) <span class="hljs-operator">*</span> elapsed ) <span class="hljs-operator">/</span> publicSalePriceLoweringDuration ;
    } <span class="hljs-keyword">else</span> {
        price <span class="hljs-operator">=</span> publicSaleEndingPrice;
    }

    <span class="hljs-keyword">return</span> price;
}

<span class="hljs-comment">//mint land</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mintLands</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> numLands, <span class="hljs-keyword">bytes32</span>[] <span class="hljs-keyword">calldata</span> merkleProof</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">whenPublicSaleActive</span> <span class="hljs-title">nonReentrant</span> </span>{
    <span class="hljs-comment">//最少一块土地</span>
    <span class="hljs-built_in">require</span>(numLands <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"Must mint at least one beta"</span>);
    <span class="hljs-comment">//mint的土地有没有超过最大限制</span>
    <span class="hljs-built_in">require</span>(currentNumLandsMintedPublicSale <span class="hljs-operator">+</span> numLands <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> MAX_PUBLIC_SALE_AMOUNT, <span class="hljs-string">"Minting would exceed max supply"</span>);
    <span class="hljs-comment">//每次mint限制</span>
    <span class="hljs-built_in">require</span>(numLands <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> maxMintPerTx, <span class="hljs-string">"numLands should not exceed maxMintPerTx"</span>);
    <span class="hljs-comment">//每个人的mint限制</span>
    <span class="hljs-built_in">require</span>(numLands <span class="hljs-operator">+</span> mintedPerAddress[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> maxMintPerAddress, <span class="hljs-string">"sender address cannot mint more than maxMintPerAddress lands"</span>);
    <span class="hljs-comment">//是否需要kyc验证，贡献者需要，公售的不要</span>
    <span class="hljs-keyword">if</span>(isKycCheckRequired) {
        <span class="hljs-built_in">require</span>(MerkleProof.verify(merkleProof, kycMerkleRoot, <span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>))), <span class="hljs-string">"Sender address is not in KYC allowlist"</span>);
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">//不允许合约等间接调用，老老实实正常操作</span>
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.<span class="hljs-built_in">origin</span>, <span class="hljs-string">"Minting from smart contracts is disallowed"</span>);
    }
 
    <span class="hljs-comment">//获得土地价格</span>
    <span class="hljs-keyword">uint256</span> mintPrice <span class="hljs-operator">=</span> getMintPrice();
    <span class="hljs-comment">//交钱</span>
    IERC20(tokenContract).safeTransferFrom(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), mintPrice <span class="hljs-operator">*</span> numLands);
    <span class="hljs-comment">//记录当前的土地数量</span>
    currentNumLandsMintedPublicSale <span class="hljs-operator">+</span><span class="hljs-operator">=</span> numLands;
    <span class="hljs-comment">//记录每个用户的的土地数量</span>
    mintedPerAddress[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> numLands;
    <span class="hljs-keyword">emit</span> PublicSaleMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, numLands, mintPrice);
    <span class="hljs-comment">//mint land</span>
    mintLandsCommon(numLands, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
}
<span class="hljs-comment">//调用erc721 mint</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mintLandsCommon</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> numLands, <span class="hljs-keyword">address</span> recipient</span>) <span class="hljs-title"><span class="hljs-keyword">private</span></span> </span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i; i <span class="hljs-operator">&#x3C;</span> numLands; <span class="hljs-operator">+</span><span class="hljs-operator">+</span>i) {
        _safeMint(recipient, mintIndexPublicSaleAndContributors<span class="hljs-operator">+</span><span class="hljs-operator">+</span>);
    }
}
<span class="hljs-comment">//赚钱喽，取钱</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
    <span class="hljs-keyword">uint256</span> balance <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>;
    <span class="hljs-keyword">if</span>(balance <span class="hljs-operator">></span> <span class="hljs-number">0</span>){
        Address.sendValue(<span class="hljs-keyword">payable</span>(owner()), balance);
    }

    balance <span class="hljs-operator">=</span> IERC20(tokenContract).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
    <span class="hljs-keyword">if</span>(balance <span class="hljs-operator">></span> <span class="hljs-number">0</span>){
        IERC20(tokenContract).safeTransfer(owner(), balance);
    }
}

<span class="hljs-comment">// Alpha/Beta Claim Methods</span>
<span class="hljs-comment">//无聊猿和变异猿持有者是否可以再mint</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flipClaimableState</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOperator</span> </span>{
    claimableActive <span class="hljs-operator">=</span> <span class="hljs-operator">!</span>claimableActive;
    <span class="hljs-keyword">emit</span> ClaimableStateChanged(claimableActive);
}

<span class="hljs-comment">//无聊猿和变异猿持有者再mint</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">nftOwnerClaimLand</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">calldata</span> alphaTokenIds, <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">calldata</span> betaTokenIds</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">whenClaimableActive</span> </span>{
    <span class="hljs-built_in">require</span>(alphaTokenIds.<span class="hljs-built_in">length</span> <span class="hljs-operator">></span> <span class="hljs-number">0</span> <span class="hljs-operator">|</span><span class="hljs-operator">|</span> betaTokenIds.<span class="hljs-built_in">length</span> <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"Should claim at least one land"</span>);
    <span class="hljs-built_in">require</span>(alphaTokenIds.<span class="hljs-built_in">length</span> <span class="hljs-operator">+</span> betaTokenIds.<span class="hljs-built_in">length</span> <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> MAX_MINT_PER_BLOCK, <span class="hljs-string">"Input length should be &#x3C;= MAX_MINT_PER_BLOCK"</span>);
    <span class="hljs-comment">//无聊猿持有者mint</span>
    alphaClaimLand(alphaTokenIds);
    <span class="hljs-comment">//变异猿持有者mint</span>
    betaClaimLand(betaTokenIds);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">alphaClaimLand</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">calldata</span> alphaTokenIds</span>) <span class="hljs-title"><span class="hljs-keyword">private</span></span> </span>{
    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">uint256</span> i; i <span class="hljs-operator">&#x3C;</span> alphaTokenIds.<span class="hljs-built_in">length</span>; <span class="hljs-operator">+</span><span class="hljs-operator">+</span>i){
        <span class="hljs-keyword">uint256</span> alphaTokenId <span class="hljs-operator">=</span> alphaTokenIds[i];
        <span class="hljs-built_in">require</span>(<span class="hljs-operator">!</span>alphaClaimed[alphaTokenId], <span class="hljs-string">"ALPHA NFT already claimed"</span>);
        <span class="hljs-built_in">require</span>(ERC721(alphaContract).ownerOf(alphaTokenId) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-string">"Must own all of the alpha defined by alphaTokenIds"</span>);
        
        alphaClaimLandByTokenId(alphaTokenId);    
    }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">alphaClaimLandByTokenId</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> alphaTokenId</span>) <span class="hljs-title"><span class="hljs-keyword">private</span></span> </span>{
    alphaClaimed[alphaTokenId] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    <span class="hljs-operator">+</span><span class="hljs-operator">+</span>alphaClaimedAmount;        
    _safeMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, alphaTokenId);
}


<span class="hljs-comment">// Contributors Claim Methods</span>
<span class="hljs-comment">//开始贡献者mint</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">startContributorsClaimPeriod</span>(<span class="hljs-params"></span>) <span class="hljs-title">onlyOperator</span> <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
    <span class="hljs-built_in">require</span>(<span class="hljs-operator">!</span>contributorsClaimActive, <span class="hljs-string">"Contributors claim is already active"</span>);
    contributorsClaimActive <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    <span class="hljs-keyword">emit</span> ContributorsClaimStart(<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>);
}
<span class="hljs-comment">//结束贡献者mint</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">stopContributorsClaimPeriod</span>(<span class="hljs-params"></span>) <span class="hljs-title">onlyOperator</span> <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">whenContributorsClaimActive</span> </span>{
    contributorsClaimActive <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">emit</span> ContributorsClaimStop(<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">contributorsClaimLand</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount, <span class="hljs-keyword">address</span> recipient</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyContributors</span>(<span class="hljs-params"><span class="hljs-built_in">msg</span>.sender</span>) <span class="hljs-title">whenContributorsClaimActive</span> </span>{
    <span class="hljs-built_in">require</span>(amount <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"Must mint at least one land"</span>);
    <span class="hljs-built_in">require</span>(amount <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> MAX_MINT_PER_BLOCK, <span class="hljs-string">"amount should not exceed MAX_MINT_PER_BLOCK"</span>);
    <span class="hljs-built_in">require</span>(amount <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> contributors[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>], <span class="hljs-string">"Contributor cannot claim other lands"</span>);

    contributors[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> amount;
    mintLandsCommon(amount, recipient);
}
<span class="hljs-comment">//应该是把没卖完的土地mint掉，但是不可能出现这种事情的吧。。。。留个后手总归好的</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">claimUnclaimedAndUnsoldLands</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> recipient</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
    claimUnclaimedAndUnsoldLandsWithAmount(recipient, MAX_MINT_PER_BLOCK);
}
。。。。。。。。。

<span class="hljs-comment">// metadata</span>
<span class="hljs-comment">//用于给别人验证，保证我的土拍是没有作弊的</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadLandMetadata</span>(<span class="hljs-params">Metadata <span class="hljs-keyword">memory</span> _landMetadata</span>)
    <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOperator</span> <span class="hljs-title">checkMetadataRange</span>(<span class="hljs-params">_landMetadata</span>)
    <span class="hljs-title">checkFirstMetadataRange</span>(<span class="hljs-params">metadataHashes.length, _landMetadata.startIndex, _landMetadata.endIndex</span>)
</span>{
    metadataHashes.<span class="hljs-built_in">push</span>(_landMetadata);
} 

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">putLandMetadataAtIndex</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> index, Metadata <span class="hljs-keyword">memory</span> _landMetadata</span>)
    <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOperator</span> <span class="hljs-title">checkMetadataRange</span>(<span class="hljs-params">_landMetadata</span>)
    <span class="hljs-title">checkFirstMetadataRange</span>(<span class="hljs-params">index, _landMetadata.startIndex, _landMetadata.endIndex</span>)
</span>{
    metadataHashes[index] <span class="hljs-operator">=</span> _landMetadata;
}     

<span class="hljs-comment">// randomness</span>
<span class="hljs-comment">//用link vrf生成随机数，这个随机数给公售和贡献者用</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">requestRandomnessForPublicSaleAndContributors</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOperator</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes32</span> requestId</span>) </span>{
    <span class="hljs-built_in">require</span>(<span class="hljs-operator">!</span>publicSaleAndContributorsRandomnessRequested, <span class="hljs-string">"Public Sale And Contributors Offset already requested"</span>);
    publicSaleAndContributorsRandomnessRequested <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    requestId <span class="hljs-operator">=</span> requestRandomnessPrivate();
    isRandomRequestForPublicSaleAndContributors[requestId] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
}

<span class="hljs-comment">//用link vrf生成随机数，这个随机数给猿持有者用</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">requestRandomnessForOwnerClaim</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOperator</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes32</span> requestId</span>) </span>{
    <span class="hljs-built_in">require</span>(<span class="hljs-operator">!</span>ownerClaimRandomnessRequested, <span class="hljs-string">"Owner Claim Offset already requested"</span>);
    ownerClaimRandomnessRequested <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    requestId <span class="hljs-operator">=</span> requestRandomnessPrivate();
    isRandomRequestForPublicSaleAndContributors[requestId] <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">requestRandomnessPrivate</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">private</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes32</span> requestId</span>) </span>{
    <span class="hljs-built_in">require</span>(
        LINK.balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)) <span class="hljs-operator">></span><span class="hljs-operator">=</span> fee,
        <span class="hljs-string">"Not enough LINK"</span>
    );
    <span class="hljs-keyword">return</span> requestRandomness(keyHash, fee);
}
<span class="hljs-comment">//link会把产生的随机数回调这个函数，然后项目方进行业务处理，这里猜测应该是概率不同，我持有nft，那拿到好土地的概率会更大</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fulfillRandomness</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> requestId, <span class="hljs-keyword">uint256</span> randomness</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{
    <span class="hljs-comment">//区分是公售、贡献者还是猿持有者随机数</span>
    <span class="hljs-keyword">if</span>(isRandomRequestForPublicSaleAndContributors[requestId]){
        publicSaleAndContributorsOffset <span class="hljs-operator">=</span> (randomness <span class="hljs-operator">%</span> (MAX_PUBLIC_SALE_AMOUNT <span class="hljs-operator">+</span> RESERVED_CONTRIBUTORS_AMOUNT));
        <span class="hljs-keyword">emit</span> StartingIndexSetPublicSale(publicSaleAndContributorsOffset);
    } <span class="hljs-keyword">else</span> {
        alphaOffset <span class="hljs-operator">=</span> (randomness <span class="hljs-operator">%</span> MAX_ALPHA_NFT_AMOUNT);
        betaOffset <span class="hljs-operator">=</span> (randomness <span class="hljs-operator">%</span> MAX_BETA_NFT_AMOUNT);
        <span class="hljs-keyword">emit</span> StartingIndexSetAlphaBeta(alphaOffset, betaOffset);
    }
}
</code></pre>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[Gas消耗统计]]></title>
            <link>https://paragraph.com/@point/gas</link>
            <guid>zRqLbHqNlAhOtXEmT4l6</guid>
            <pubDate>Tue, 03 May 2022 08:09:53 GMT</pubDate>
            <description><![CDATA[对常规操作的gas消耗有个概念，不求网上的精确计算，只是有个基础的概念 统计完之后感觉极度不准空函数, 21186 gas// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract TestMap{ function set1() external {} } 加变量一个变量，408 gas function set1(uint8 a) external {} 两个变量，721 gas 一个bytes[] memory 2196 gas 一个bytes[] calldata 1030 gas 一个uint[] calldata 750 gas 函数内部执行操作function set1() pure external { uint a=1+1;//13 gas } 换成uint a=10324132421421424+143214214;gas费用相同 //赋值storage，22114 gas uint a; function set1() external { a=10324132421421424; } /...]]></description>
            <content:encoded><![CDATA[<p>对常规操作的gas消耗有个概念，不求网上的精确计算，只是有个基础的概念</p><p>统计完之后感觉极度不准</p><ol><li><p>空函数, 21186 gas</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TestMap{
    function set1()  external {}
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">TestMap</span></span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">set1</span>(<span class="hljs-params"></span>)  <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{}
}
</code></pre></li><li><p>加变量</p><pre data-type="codeBlock" text="一个变量，408 gas
function set1(uint8 a)  external {}
两个变量，721 gas
一个bytes[] memory 2196 gas
一个bytes[] calldata 1030 gas
一个uint[] calldata 750 gas
"><code>一个变量，<span class="hljs-number">408</span> gas
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">set1</span>(<span class="hljs-params"><span class="hljs-keyword">uint8</span> a</span>)  <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{}
两个变量，<span class="hljs-number">721</span> gas
一个<span class="hljs-keyword">bytes</span>[] <span class="hljs-keyword">memory</span> <span class="hljs-number">2196</span> gas
一个<span class="hljs-keyword">bytes</span>[] <span class="hljs-keyword">calldata</span> <span class="hljs-number">1030</span> gas
一个<span class="hljs-keyword">uint</span>[] <span class="hljs-keyword">calldata</span> <span class="hljs-number">750</span> gas
</code></pre></li><li><p>函数内部执行操作</p><pre data-type="codeBlock" text="function set1() pure external {
        uint a=1+1;//13 gas
    }
换成uint a=10324132421421424+143214214;gas费用相同

//赋值storage，22114 gas
uint a;
    function set1() external {
        a=10324132421421424;
    }
//在增加个address成员地址 22167 gas

//storage变量的计算，2307 gas
uint a=1;
    function set1() view external {
        uint c=a+1;
    }
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">set1</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-keyword">uint</span> a<span class="hljs-operator">=</span><span class="hljs-number">1</span><span class="hljs-operator">+</span><span class="hljs-number">1</span>;<span class="hljs-comment">//13 gas</span>
    }
换成<span class="hljs-keyword">uint</span> a<span class="hljs-operator">=</span><span class="hljs-number">10324132421421424</span><span class="hljs-operator">+</span><span class="hljs-number">143214214</span>;gas费用相同

<span class="hljs-comment">//赋值storage，22114 gas</span>
<span class="hljs-keyword">uint</span> a;
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">set1</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        a<span class="hljs-operator">=</span><span class="hljs-number">10324132421421424</span>;
    }
<span class="hljs-comment">//在增加个address成员地址 22167 gas</span>

<span class="hljs-comment">//storage变量的计算，2307 gas</span>
<span class="hljs-keyword">uint</span> a<span class="hljs-operator">=</span><span class="hljs-number">1</span>;
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">set1</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-keyword">uint</span> c<span class="hljs-operator">=</span>a<span class="hljs-operator">+</span><span class="hljs-number">1</span>;
    }
</code></pre></li><li><p>操作map</p><pre data-type="codeBlock" text="22192 gas
mapping(uint=&gt; uint) public resultMap;
    function set1() external {
        resultMap[1]=2;
    }
"><code><span class="hljs-number">22192</span> gas
<span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint</span><span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint</span>) <span class="hljs-keyword">public</span> resultMap;
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">set1</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        resultMap[<span class="hljs-number">1</span>]<span class="hljs-operator">=</span><span class="hljs-number">2</span>;
    }
</code></pre></li><li><p>转账eth</p><pre data-type="codeBlock" text="transfer/call 9380 gas 不是说好的固定2300么
function set1(address to) payable external {
        payable(to).transfer(msg.value);
}
call 9586 gas
"><code>transfer<span class="hljs-operator">/</span>call <span class="hljs-number">9380</span> gas 不是说好的固定<span class="hljs-number">2300</span>么
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">set1</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to</span>) <span class="hljs-title"><span class="hljs-keyword">payable</span></span> <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-keyword">payable</span>(to).<span class="hljs-built_in">transfer</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>);
}
call <span class="hljs-number">9586</span> gas
</code></pre></li><li><p>调用别的contract的函数 4493 gas</p></li><li><p>keccak256+abi.encode 863 gas 根据内容多少gas变化</p></li></ol>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[solidity安全，测试时对照使用]]></title>
            <link>https://paragraph.com/@point/solidity</link>
            <guid>IndIMUcme4ajZiy6VC1h</guid>
            <pubDate>Tue, 03 May 2022 07:59:01 GMT</pubDate>
            <description><![CDATA[防止重入，涉及到交易的功能加锁先，反正solidity的锁开销很小，先加锁再说防止重入，要记录状态的，先记录再转账权限，每个函数都要想好给谁用的只要是计算，就用safeMath每个函数的调用要想想是否要校验成功，特别是转账前端和后端的数据源只能以solidity为准，保证数据一致性，后端利用事件做到考虑每个函数的可见性，能不暴露的就不暴露能复制绝不手写，能抄袭绝不原创如果可以的话用weth代替eth付款检查-生效-交互solidity本身的坑可能因不了解solidity的某些机制而导致的bug1. 重入这个老生常谈了，加锁，在重要步骤（如转账）前先修改状态2.计算溢出这个说是已经修复了，但还是老老实实用safeMath吧3.call()和delegatecall()call() 切换上下文,我->合约1->合约2,msg.sender的结果是合约1 delegatecall() 保持上下文,我->合约1->合约2,msg.sender的结果是合约我4.随机数区块链理论上无法产生随机数，要用的话用link提供的随机数5.call返回值首先不要用transfer和send，因为这两个...]]></description>
            <content:encoded><![CDATA[<ol><li><p>防止重入，涉及到交易的功能加锁先，反正solidity的锁开销很小，先加锁再说</p></li><li><p>防止重入，要记录状态的，先记录再转账</p></li><li><p>权限，每个函数都要想好给谁用的</p></li><li><p>只要是计算，就用safeMath</p></li><li><p>每个函数的调用要想想是否要校验成功，特别是转账</p></li><li><p>前端和后端的数据源只能以solidity为准，保证数据一致性，后端利用事件做到</p></li><li><p>考虑每个函数的可见性，能不暴露的就不暴露</p></li><li><p>能复制绝不手写，能抄袭绝不原创</p></li><li><p>如果可以的话用weth代替eth付款</p></li><li><p>检查-生效-交互</p></li></ol><h2 id="h-solidity" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">solidity本身的坑</h2><p>可能因不了解solidity的某些机制而导致的bug</p><h3 id="h-1" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. 重入</h3><p>这个老生常谈了，加锁，在重要步骤（如转账）前先修改状态</p><h3 id="h-2" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.计算溢出</h3><p>这个说是已经修复了，但还是老老实实用safeMath吧</p><h3 id="h-3calldelegatecall" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.call()和delegatecall()</h3><p>call() 切换上下文,我-&gt;合约1-&gt;合约2,msg.sender的结果是合约1</p><p>delegatecall() 保持上下文,我-&gt;合约1-&gt;合约2,msg.sender的结果是合约我</p><h3 id="h-4" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4.随机数</h3><p>区块链理论上无法产生随机数，要用的话用link提供的随机数</p><h3 id="h-5call" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">5.call返回值</h3><p>首先不要用transfer和send，因为这两个函数本来就是为了解决重入问题的，不完美。</p><p>call有个问题，失败的话不会报错，返回个false，所以一定要检查返回值</p><h3 id="h-6" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">6.时间戳</h3><p>旷工可以改时间，导致bug。要求很高的话可以用block来确定，BAT币就是用的startBlock和endBlock来确定时间的。要求不怎么高的话可以用时间戳</p><h3 id="h-7" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">7.浮点数和精度</h3><p>如果a/b&lt;1,会变成0，所以最好先变成高精度数字再计算</p><h3 id="h-8for" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">8.for循环</h3><p>solidity能不for就千万不要for，能for也尽量不要for，总之就是不要for。solidity的for就像这句话一样又长又臭。首先太贵，贵到最后可能导致超过gas限制，合约就废了。而且效率不高</p><h2 id="h-bug" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">业务bug</h2><p>纯属写代码的人水平问题，开发经验问题。这些bug，很可能靠工具，审计是查不出来的。多了解别人的bug，以史为镜，自我反思</p><h3 id="h-1akutar-nft-34m" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1.Akutar NFT 一行代码34M</h3><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://learnblockchain.cn/article/3946">https://learnblockchain.cn/article/3946</a></p><p>这里讲得很详细，就是判断用错了变量。</p><p>反思：测试用例一定要覆盖全，每个逻辑在构思的时候就用小本子把可能出现的bug记录下来；写代码的过程中，再随时记录；产品，技术，测试最后再头脑风暴，查漏补缺，最后形成文档，case；还要内部code review，讲解，避免这些低级bug</p><h3 id="h-2nbabug" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.NBA签名bug</h3><p>没有验证签名是谁发过来的，并且签名有没有被用过</p><p>反思：这个bug我自己写的时候也没有注意到，还好NBA爆出来了，感恩。要注意所有的内容是否要有一次性的判断</p><h3 id="h-3ape" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.APE空投漏洞</h3><p>空投的要求只需要瞬时拥有NFT，而不是持有一段时间，导致可以通过闪电贷撸走</p><p>反思：这个应该是设计问题，产品没想到可以这么玩。这个就考验产品、编程、测试人员经验了。</p><h3 id="h-4opensea" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4.opensea过期订单</h3><p>有人很久前保存了签名，很久之后NFT涨价了，就把签名直接传到合约里，结果NFT被低价买走。合约没有校验签名的过期时间</p><p>反思：设计问题，和ape正好相反，看来合约开发的思维要比传统项目多一个时间维度。要思考在很短或者很长的时间里合约是否安全。</p><h3 id="h-5sandbox-burn" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">5.Sandbox burn他人土地</h3><p>burn的函数可见性是external而不是internal</p><p>反思：每个关键字都要酌情考虑</p>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[go-zero使用]]></title>
            <link>https://paragraph.com/@point/go-zero</link>
            <guid>41fDvSnm27vxMFQMZYWf</guid>
            <pubDate>Tue, 03 May 2022 04:10:24 GMT</pubDate>
            <description><![CDATA[这个教程看完可以日常使用了 https://www.bilibili.com/video/BV1LS4y1U72n/?spm_id_from=333.788稍微总结下java转过去的感觉很亲切，无脑写业务就行（internal-→logic）里面写就行了，别的地方几乎不用管，框架还贴心的写了todo生成工具很全,一个生成router这样的业务代码，一个生成model层代码，很javagoctl model mysql ddl -src="sql路径" -dir="."goctl api go -api temp/nft.api -dir .可以自己写sql，舒服，ORM怎么用都不习惯单体应用转微服务方便，代价很小使用时注意点自动生成的代码不要动，顶部也会提示，不然下次再自动生成会冲掉，业务逻辑则不会冲掉，可以放心自动生成代码model层的后续加业务则没法自动生成，可以记录下哪里是自己写的代码，方便后面重新生成api文件要分模块的话，service名字要一样，这个点稀奇古怪的。mysql创建表时最好勾上not null，不然model层会出现sql.NullString类型，有点不...]]></description>
            <content:encoded><![CDATA[<p>这个教程看完可以日常使用了</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.bilibili.com/video/BV1LS4y1U72n/?spm_id_from=333.788">https://www.bilibili.com/video/BV1LS4y1U72n/?spm_id_from=333.788</a></p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">稍微总结下</h3><ol><li><p>java转过去的感觉很亲切，无脑写业务就行（internal-→logic）里面写就行了，别的地方几乎不用管，框架还贴心的写了todo</p></li><li><p>生成工具很全,一个生成router这样的业务代码，一个生成model层代码，很java</p><ol><li><p>goctl model mysql ddl -src=&quot;sql路径&quot; -dir=&quot;.&quot;</p></li><li><p>goctl api go -api temp/nft.api -dir .</p></li></ol></li><li><p>可以自己写sql，舒服，ORM怎么用都不习惯</p></li><li><p>单体应用转微服务方便，代价很小</p></li></ol><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">使用时注意点</h3><ol><li><p>自动生成的代码不要动，顶部也会提示，不然下次再自动生成会冲掉，业务逻辑则不会冲掉，可以放心自动生成代码</p></li><li><p>model层的后续加业务则没法自动生成，可以记录下哪里是自己写的代码，方便后面重新生成</p></li><li><p>api文件要分模块的话，service名字要一样，这个点稀奇古怪的。</p></li><li><p>mysql创建表时最好勾上not null，不然model层会出现sql.NullString类型，有点不舒服</p></li><li><p>time相关的会忽略，自己看情况维护</p></li></ol>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[Go获取合约事件日志]]></title>
            <link>https://paragraph.com/@point/go</link>
            <guid>cX6EG5AXWdu5cwclgOQI</guid>
            <pubDate>Tue, 03 May 2022 03:53:44 GMT</pubDate>
            <description><![CDATA[func main() { client, err := ethclient.Dial("https://rinkeby-light.eth.linkpool.io/") if err != nil { log.Fatal(err) } //获取当前的最新区块 header, err := client.HeaderByNumber(context.Background(), nil) if err != nil { log.Fatal(err) } fmt.Println("最新区块", header.Number.String()) // 5671744 //开始查询日志，可以添加过滤条件，查询符合你想要的日志 query := ethereum.FilterQuery{ FromBlock: big.NewInt( 10570948), ToBlock: header.Number, Addresses: []common.Address{ common.HexToAddress("0x40490DF1cc631817D24BA324147c59821C8970BF"), ...]]></description>
            <content:encoded><![CDATA[<pre data-type="codeBlock" text="func main() {
   client, err := ethclient.Dial(&quot;https://rinkeby-light.eth.linkpool.io/&quot;)
   if err != nil {
      log.Fatal(err)
   }
   //获取当前的最新区块
   header, err := client.HeaderByNumber(context.Background(), nil)
   if err != nil {
      log.Fatal(err)
   }
   fmt.Println(&quot;最新区块&quot;, header.Number.String()) // 5671744

   //开始查询日志，可以添加过滤条件，查询符合你想要的日志
   query := ethereum.FilterQuery{
      FromBlock: big.NewInt(
         10570948),
      ToBlock: header.Number,
      Addresses: []common.Address{
         common.HexToAddress(&quot;0x40490DF1cc631817D24BA324147c59821C8970BF&quot;),
         common.HexToAddress(&quot;0xE3a463d743F762D538031BAD3f1E748BB41f96ec&quot;),
      },
   }
   logs, err := client.FilterLogs(context.Background(), query)
   if err != nil {
      log.Fatal(err)
   }

   //解析日志结果
   for _, vLog := range logs {
      fmt.Println(&quot;Data:&quot;, vLog.Data)                 // 0x3404b8c050aa0aacd0223e91b5c32fee6400f357764771d0684fa7b3f448f1a8
      fmt.Println(&quot;contactAddress:&quot;, vLog.Address)    // 0x3404b8c050aa0aacd0223e91b5c32fee6400f357764771d0684fa7b3f448f1a8
      fmt.Println(&quot;BlockHash:&quot;, vLog.BlockHash.Hex()) // 0x3404b8c050aa0aacd0223e91b5c32fee6400f357764771d0684fa7b3f448f1a8
      fmt.Println(&quot;BlockNumber:&quot;, vLog.BlockNumber)   // 2394201
      fmt.Println(&quot;TxHash:&quot;, vLog.TxHash.Hex())       // 0x280201eda63c9ff6f305fcee51d5eb86167fab40ca3108ec784e8652a0e2b1a6

      //具体的内容
      var topics [4]string
      for i := range vLog.Topics {
         //i=0,日志名称的十六进制
         //后续的元素是日志内容
         fmt.Println(&quot;Topics:&quot;, i, vLog.Topics[i])
         topics[i] = vLog.Topics[i].Hex()
      }
   }
   //将方法转成16进制,可用于多种日志判断具体执行什么业务逻辑
   eventSignature := []byte(&quot;Transfer(address,address,uint256)&quot;)
   hash := crypto.Keccak256Hash(eventSignature)
   fmt.Println(&quot;eventSignature:&quot;, hash.Hex())
}
"><code>func main() {
   client, err :<span class="hljs-operator">=</span> ethclient.Dial(<span class="hljs-string">"https://rinkeby-light.eth.linkpool.io/"</span>)
   <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      log.Fatal(err)
   }
   <span class="hljs-comment">//获取当前的最新区块</span>
   header, err :<span class="hljs-operator">=</span> client.HeaderByNumber(context.Background(), nil)
   <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      log.Fatal(err)
   }
   fmt.Println(<span class="hljs-string">"最新区块"</span>, header.Number.String()) <span class="hljs-comment">// 5671744</span>

   <span class="hljs-comment">//开始查询日志，可以添加过滤条件，查询符合你想要的日志</span>
   query :<span class="hljs-operator">=</span> ethereum.FilterQuery{
      FromBlock: big.NewInt(
         <span class="hljs-number">10570948</span>),
      ToBlock: header.Number,
      Addresses: []common.Address{
         common.HexToAddress(<span class="hljs-string">"0x40490DF1cc631817D24BA324147c59821C8970BF"</span>),
         common.HexToAddress(<span class="hljs-string">"0xE3a463d743F762D538031BAD3f1E748BB41f96ec"</span>),
      },
   }
   logs, err :<span class="hljs-operator">=</span> client.FilterLogs(context.Background(), query)
   <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      log.Fatal(err)
   }

   <span class="hljs-comment">//解析日志结果</span>
   <span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span>, vLog :<span class="hljs-operator">=</span> range logs {
      fmt.Println(<span class="hljs-string">"Data:"</span>, vLog.Data)                 <span class="hljs-comment">// 0x3404b8c050aa0aacd0223e91b5c32fee6400f357764771d0684fa7b3f448f1a8</span>
      fmt.Println(<span class="hljs-string">"contactAddress:"</span>, vLog.Address)    <span class="hljs-comment">// 0x3404b8c050aa0aacd0223e91b5c32fee6400f357764771d0684fa7b3f448f1a8</span>
      fmt.Println(<span class="hljs-string">"BlockHash:"</span>, vLog.BlockHash.Hex()) <span class="hljs-comment">// 0x3404b8c050aa0aacd0223e91b5c32fee6400f357764771d0684fa7b3f448f1a8</span>
      fmt.Println(<span class="hljs-string">"BlockNumber:"</span>, vLog.BlockNumber)   <span class="hljs-comment">// 2394201</span>
      fmt.Println(<span class="hljs-string">"TxHash:"</span>, vLog.TxHash.Hex())       <span class="hljs-comment">// 0x280201eda63c9ff6f305fcee51d5eb86167fab40ca3108ec784e8652a0e2b1a6</span>

      <span class="hljs-comment">//具体的内容</span>
      <span class="hljs-keyword">var</span> topics [<span class="hljs-number">4</span>]<span class="hljs-keyword">string</span>
      <span class="hljs-keyword">for</span> i :<span class="hljs-operator">=</span> range vLog.Topics {
         <span class="hljs-comment">//i=0,日志名称的十六进制</span>
         <span class="hljs-comment">//后续的元素是日志内容</span>
         fmt.Println(<span class="hljs-string">"Topics:"</span>, i, vLog.Topics[i])
         topics[i] <span class="hljs-operator">=</span> vLog.Topics[i].Hex()
      }
   }
   <span class="hljs-comment">//将方法转成16进制,可用于多种日志判断具体执行什么业务逻辑</span>
   eventSignature :<span class="hljs-operator">=</span> []<span class="hljs-keyword">byte</span>(<span class="hljs-string">"Transfer(address,address,uint256)"</span>)
   hash :<span class="hljs-operator">=</span> crypto.Keccak256Hash(eventSignature)
   fmt.Println(<span class="hljs-string">"eventSignature:"</span>, hash.Hex())
}
</code></pre>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[签名 登录 验证]]></title>
            <link>https://paragraph.com/@point/9HtpYR6FKdaFW0VMQd40</link>
            <guid>9HtpYR6FKdaFW0VMQd40</guid>
            <pubDate>Tue, 03 May 2022 03:51:17 GMT</pubDate>
            <description><![CDATA[1. etherjs签名async walletSignMessage(message) { const signer = await window.ethProvider.getSigner(); const result = await signer.signMessage(message); return result; } 2.go后端验证func VerifySig(address, signature, msg string) bool { bytes := []byte(msg) fromAddr := common.HexToAddress(address) sig := hexutil.MustDecode(signature) if sig[64] != 27 &#x26;&#x26; sig[64] != 28 { return false } sig[64] -= 27 pubKey, err := crypto.SigToPub(signHash(bytes), sig) if err != nil { return false } recoveredA...]]></description>
            <content:encoded><![CDATA[<h3 id="h-1-etherjs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. etherjs签名</h3><pre data-type="codeBlock" text="async walletSignMessage(message) {
    const signer = await window.ethProvider.getSigner();
    const result = await signer.signMessage(message);
    return result;
  }
"><code><span class="hljs-keyword">async</span> <span class="hljs-title function_">walletSignMessage</span>(<span class="hljs-params">message</span>) {
    <span class="hljs-keyword">const</span> signer = <span class="hljs-keyword">await</span> <span class="hljs-variable language_">window</span>.<span class="hljs-property">ethProvider</span>.<span class="hljs-title function_">getSigner</span>();
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> signer.<span class="hljs-title function_">signMessage</span>(message);
    <span class="hljs-keyword">return</span> result;
  }
</code></pre><h3 id="h-2go" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.go后端验证</h3><pre data-type="codeBlock" text="func VerifySig(address, signature, msg string) bool {
   bytes := []byte(msg)
   fromAddr := common.HexToAddress(address)
   sig := hexutil.MustDecode(signature)
   if sig[64] != 27 &amp;&amp; sig[64] != 28 {
      return false
   }
   sig[64] -= 27
   pubKey, err := crypto.SigToPub(signHash(bytes), sig)
   if err != nil {
      return false
   }
   recoveredAddr := crypto.PubkeyToAddress(*pubKey)
   fmt.Println(recoveredAddr)
   result := fromAddr == recoveredAddr
   return result
}

func signHash(data []byte) []byte {
    msg := fmt.Sprintf(&quot;\x19Ethereum Signed Message:\n%d%s&quot;, len(data), data)
    return crypto.Keccak256([]byte(msg))
}
"><code>func VerifySig(<span class="hljs-keyword">address</span>, signature, <span class="hljs-built_in">msg</span> <span class="hljs-keyword">string</span>) <span class="hljs-keyword">bool</span> {
   <span class="hljs-keyword">bytes</span> :<span class="hljs-operator">=</span> []<span class="hljs-keyword">byte</span>(<span class="hljs-built_in">msg</span>)
   fromAddr :<span class="hljs-operator">=</span> common.HexToAddress(<span class="hljs-keyword">address</span>)
   sig :<span class="hljs-operator">=</span> hexutil.MustDecode(signature)
   <span class="hljs-keyword">if</span> sig[<span class="hljs-number">64</span>] <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">27</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> sig[<span class="hljs-number">64</span>] <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">28</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
   }
   sig[<span class="hljs-number">64</span>] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> <span class="hljs-number">27</span>
   pubKey, err :<span class="hljs-operator">=</span> crypto.SigToPub(signHash(<span class="hljs-keyword">bytes</span>), sig)
   <span class="hljs-keyword">if</span> err <span class="hljs-operator">!</span><span class="hljs-operator">=</span> nil {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
   }
   recoveredAddr :<span class="hljs-operator">=</span> crypto.PubkeyToAddress(<span class="hljs-operator">*</span>pubKey)
   fmt.Println(recoveredAddr)
   result :<span class="hljs-operator">=</span> fromAddr <span class="hljs-operator">=</span><span class="hljs-operator">=</span> recoveredAddr
   <span class="hljs-keyword">return</span> result
}

func signHash(data []<span class="hljs-keyword">byte</span>) []<span class="hljs-keyword">byte</span> {
    <span class="hljs-built_in">msg</span> :<span class="hljs-operator">=</span> fmt.Sprintf(<span class="hljs-string">"\x19Ethereum Signed Message:\n%d%s"</span>, len(data), data)
    <span class="hljs-keyword">return</span> crypto.Keccak256([]<span class="hljs-keyword">byte</span>(<span class="hljs-built_in">msg</span>))
}
</code></pre><p>为了提高安全性，还可以在前端生成时加入salt，时间戳之类的限制</p>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[Merkle Tree Airdrop]]></title>
            <link>https://paragraph.com/@point/merkle-tree-airdrop</link>
            <guid>rZuc1VkebQ49ZpgZc10l</guid>
            <pubDate>Tue, 03 May 2022 03:36:57 GMT</pubDate>
            <description><![CDATA[现在都用merkle tree 空投，记录下merkle tree原理用leaves一层又一层计算出root hash，验证时拿leaf+和该leaf有关的leaf再计算一遍root hash是否一样 具体说明看这里，肯定能懂 https://www.npmjs.com/package/merkletreejs步骤：1.生成root hashconst { MerkleTree } = require("merkletreejs"); const keccak256 = require("keccak256"); //获取hash root const leaves = ["地址1","地址2".....].map((x) => keccak256(x)); const tree = new MerkleTree(leaves, keccak256); const root = tree.getHexRoot(); //获取leaf验证的路径 const leaf = keccak256("a"); const proof = tree.getProof(leaf); 2.sol...]]></description>
            <content:encoded><![CDATA[<p>现在都用merkle tree 空投，记录下</p><h2 id="h-merkle-tree" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">merkle tree原理</h2><p>用leaves一层又一层计算出root hash，验证时拿leaf+和该leaf有关的leaf再计算一遍root hash是否一样</p><p>具体说明看这里，肯定能懂</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.npmjs.com/package/merkletreejs">https://www.npmjs.com/package/merkletreejs</a></p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">步骤：</h2><h3 id="h-1root-hash" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1.生成root hash</h3><pre data-type="codeBlock" text="const { MerkleTree } = require(&quot;merkletreejs&quot;);
const keccak256 = require(&quot;keccak256&quot;);
//获取hash root
const leaves = [&quot;地址1&quot;,&quot;地址2&quot;.....].map((x) =&gt; keccak256(x));
const tree = new MerkleTree(leaves, keccak256);
const root = tree.getHexRoot();

//获取leaf验证的路径
const leaf = keccak256(&quot;a&quot;);
const proof = tree.getProof(leaf);
"><code>const { MerkleTree } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"merkletreejs"</span>);
const <span class="hljs-built_in">keccak256</span> <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"keccak256"</span>);
<span class="hljs-comment">//获取hash root</span>
const leaves <span class="hljs-operator">=</span> [<span class="hljs-string">"地址1"</span>,<span class="hljs-string">"地址2"</span>.....].map((x) <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-built_in">keccak256</span>(x));
const tree <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MerkleTree(leaves, <span class="hljs-built_in">keccak256</span>);
const root <span class="hljs-operator">=</span> tree.getHexRoot();

<span class="hljs-comment">//获取leaf验证的路径</span>
const leaf <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"a"</span>);
const proof <span class="hljs-operator">=</span> tree.getProof(leaf);
</code></pre><h3 id="h-2solidityairdorp" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.solidity校验，airdorp</h3><pre data-type="codeBlock" text="//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;
import &quot;@openzeppelin/contracts/utils/cryptography/MerkleProof.sol&quot;;
import &quot;hardhat/console.sol&quot;;
contract TestMPT is Ownable {
    //记录root hash
    bytes32 public saleMerkleRoot;
    //是否领取过空投
    mapping(address =&gt; bool) public claimed;
    //设置root hash
    function setSaleMerkleRoot(bytes32 merkleRoot) external onlyOwner {
        saleMerkleRoot = merkleRoot;
    }
    //获取root hash
    function getSaleMerkleRoot() external view returns(bytes32) {
        return saleMerkleRoot;
    }
    
    //校验是否合法
    modifier isValidMerkleProof(bytes32[] calldata merkleProof, bytes32 root,bytes memory leaf) {
        require(
            MerkleProof.verify(
                merkleProof,
                root,
                keccak256(abi.encodePacked(leaf))
            ),
            &quot;Address does not exist in list&quot;
        );
        _;
    }
    //获取airdrop
    function getDrop(bytes32[] calldata merkleProof,bytes memory leaf,uint256 _amount)
        external
        isValidMerkleProof(merkleProof, saleMerkleRoot,leaf)
    {
        require(!claimed[msg.sender], &quot;Address already claimed&quot;);
        claimed[msg.sender] = true;
        //todo 业务处理
    }
}
"><code><span class="hljs-comment">//SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"hardhat/console.sol"</span>;
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">TestMPT</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Ownable</span> </span>{
    <span class="hljs-comment">//记录root hash</span>
    <span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">public</span> saleMerkleRoot;
    <span class="hljs-comment">//是否领取过空投</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">bool</span>) <span class="hljs-keyword">public</span> claimed;
    <span class="hljs-comment">//设置root hash</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setSaleMerkleRoot</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span> merkleRoot</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        saleMerkleRoot <span class="hljs-operator">=</span> merkleRoot;
    }
    <span class="hljs-comment">//获取root hash</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSaleMerkleRoot</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span></span>) </span>{
        <span class="hljs-keyword">return</span> saleMerkleRoot;
    }
    
    <span class="hljs-comment">//校验是否合法</span>
    <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">isValidMerkleProof</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span>[] <span class="hljs-keyword">calldata</span> merkleProof, <span class="hljs-keyword">bytes32</span> root,<span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> leaf</span>) </span>{
        <span class="hljs-built_in">require</span>(
            MerkleProof.verify(
                merkleProof,
                root,
                <span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(leaf))
            ),
            <span class="hljs-string">"Address does not exist in list"</span>
        );
        <span class="hljs-keyword">_</span>;
    }
    <span class="hljs-comment">//获取airdrop</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDrop</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span>[] <span class="hljs-keyword">calldata</span> merkleProof,<span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> leaf,<span class="hljs-keyword">uint256</span> _amount</span>)
        <span class="hljs-title"><span class="hljs-keyword">external</span></span>
        <span class="hljs-title">isValidMerkleProof</span>(<span class="hljs-params">merkleProof, saleMerkleRoot,leaf</span>)
    </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-operator">!</span>claimed[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>], <span class="hljs-string">"Address already claimed"</span>);
        claimed[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
        <span class="hljs-comment">//todo 业务处理</span>
    }
}
</code></pre><h3 id="h-3etherjsairdrop" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.etherjs调用airdrop合约</h3><pre data-type="codeBlock" text="const { ethers, artifacts, network } = require(&quot;hardhat&quot;);
const { writeAbiAddr } = require(&quot;./artifact_saver.js&quot;);

async function main() {

  const contactName = &quot;TestMPT&quot;;
  const Greeter = await ethers.getContractFactory(contactName);
  const greeter = await Greeter.deploy();
  await greeter.deployed();

  // 将abi address等信息保存到文件
  const artifact = await artifacts.readArtifact(contactName);
  await writeAbiAddr(artifact, greeter.address, contactName, network.name);

  //设置root hash
  const [owner] = await ethers.getSigners();
  const counter = await ethers.getContractAt(
    contactName,
    greeter.address,
    owner
  );
  await counter.setSaleMerkleRoot(   &quot;0xc7ec7ffb250de2b95a1c690751b2826ec9d2999dd9f5c6f8816655b1590ca544&quot;
  );
  const merkleRoot = await counter.getSaleMerkleRoot();
//数组里的是获取leaf的验证路径，第二个参数是要验证的内容
//特殊情况，你只有一个leaf，那要传空数组
  const result = await counter.getDrop(
    [
      &quot;0x1575cc1dded49f942913392f94716824d29b8fa45876b2db6295d16a606533a4&quot;,
      &quot;0x6c42c6099e51e28eef8f19f71765bb42c571d5c7177996f177606138f65c0c2b&quot;,
      &quot;0xb0d6f760008340e3f60414d84b305702faa6418f44f31de07b10e05bf369eb3b&quot;,
      &quot;0x4c880bf401add28c4e51270dfe16b28c3ca1b3d263ff7c5863fc8214b4046364&quot;,
    ],
    &quot;0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33&quot;
  );
}

main().catch((error) =&gt; {
  console.error(error);
  process.exitCode = 1;
});
"><code>const { ethers, artifacts, network } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);
const { writeAbiAddr } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"./artifact_saver.js"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{

  const contactName <span class="hljs-operator">=</span> <span class="hljs-string">"TestMPT"</span>;
  const Greeter <span class="hljs-operator">=</span> await ethers.getContractFactory(contactName);
  const greeter <span class="hljs-operator">=</span> await Greeter.deploy();
  await greeter.deployed();

  <span class="hljs-comment">// 将abi address等信息保存到文件</span>
  const artifact <span class="hljs-operator">=</span> await artifacts.readArtifact(contactName);
  await writeAbiAddr(artifact, greeter.<span class="hljs-built_in">address</span>, contactName, network.<span class="hljs-built_in">name</span>);

  <span class="hljs-comment">//设置root hash</span>
  const [owner] <span class="hljs-operator">=</span> await ethers.getSigners();
  const counter <span class="hljs-operator">=</span> await ethers.getContractAt(
    contactName,
    greeter.<span class="hljs-built_in">address</span>,
    owner
  );
  await counter.setSaleMerkleRoot(   <span class="hljs-string">"0xc7ec7ffb250de2b95a1c690751b2826ec9d2999dd9f5c6f8816655b1590ca544"</span>
  );
  const merkleRoot <span class="hljs-operator">=</span> await counter.getSaleMerkleRoot();
<span class="hljs-comment">//数组里的是获取leaf的验证路径，第二个参数是要验证的内容</span>
<span class="hljs-comment">//特殊情况，你只有一个leaf，那要传空数组</span>
  const result <span class="hljs-operator">=</span> await counter.getDrop(
    [
      <span class="hljs-string">"0x1575cc1dded49f942913392f94716824d29b8fa45876b2db6295d16a606533a4"</span>,
      <span class="hljs-string">"0x6c42c6099e51e28eef8f19f71765bb42c571d5c7177996f177606138f65c0c2b"</span>,
      <span class="hljs-string">"0xb0d6f760008340e3f60414d84b305702faa6418f44f31de07b10e05bf369eb3b"</span>,
      <span class="hljs-string">"0x4c880bf401add28c4e51270dfe16b28c3ca1b3d263ff7c5863fc8214b4046364"</span>,
    ],
    <span class="hljs-string">"0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33"</span>
  );
}

main().catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
  console.error(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
  process.exitCode <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
});
</code></pre>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[签名：EIP712]]></title>
            <link>https://paragraph.com/@point/eip712</link>
            <guid>TKWz0fgEyEBDDcV2LS2b</guid>
            <pubDate>Tue, 03 May 2022 02:48:47 GMT</pubDate>
            <description><![CDATA[//使用web3.js和eth-sig-util生成签名 import Web3 from "web3"; var sigUtil = require("eth-sig-util") const typedData = { //定义数据格式 types: { //固定格式,不要动,并且字段一个都不能少 //name:contract name //vaersion:版本号,如1.0随意 //chainId:当前链chainId //verifyingContract:合约地址 EIP712Domain: [ {name: 'name', type: 'string'}, {name: 'version', type: 'string'}, {name: 'chainId', type: 'uint256' }, {name: 'verifyingContract', type: 'address' }, ], //自定义自己业务的数据格式 Mail: [ {name: 'from', type: 'address'}, {name: 'to', type: 'address'},...]]></description>
            <content:encoded><![CDATA[<pre data-type="codeBlock" text="//使用web3.js和eth-sig-util生成签名
import Web3 from &quot;web3&quot;;
var sigUtil = require(&quot;eth-sig-util&quot;)

const typedData = {
        //定义数据格式
        types: {
          //固定格式,不要动,并且字段一个都不能少
          //name:contract name
          //vaersion:版本号,如1.0随意
          //chainId:当前链chainId
          //verifyingContract:合约地址
          EIP712Domain: [
            {name: &apos;name&apos;, type: &apos;string&apos;},
            {name: &apos;version&apos;, type: &apos;string&apos;},
            {name: &apos;chainId&apos;, type: &apos;uint256&apos; },
            {name: &apos;verifyingContract&apos;, type: &apos;address&apos; },
          ],
          //自定义自己业务的数据格式
          Mail: [
            {name: &apos;from&apos;, type: &apos;address&apos;},
            {name: &apos;to&apos;, type: &apos;address&apos;},
            {name: &apos;value&apos;, type: &apos;uint256&apos;},
          ],
        },
          //上面定义的数据格式具体内容
        domain: {
          name: &apos;Demo&apos;,
          version: &apos;1.0&apos;,
          chainId:&apos;1&apos;,
          verifyingContract:&apos;0xf8e81D47203A594245E36C48e151709F0C19fBe8&apos;
        },
        primaryType: &apos;Mail&apos;,
          //具体的业务字段,需要和上面的Mail格式对应
        message: {
          from: &quot;0xE3a463d743F762D538031BAD3f1E748BB41f96ec&quot;,
          to: &quot;0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29&quot;,
          value: 1234234145789,
        },
      }
      //生成签名 privateKey 是钱包账户的秘钥
      var privateKeyHex = Buffer.from(privateKey, &apos;hex&apos;)
      var signature = sigUtil.signTypedData_v4(privateKeyHex, {data: typedData})
}
"><code><span class="hljs-comment">//使用web3.js和eth-sig-util生成签名</span>
<span class="hljs-keyword">import</span> <span class="hljs-title">Web3</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"web3"</span>;
<span class="hljs-keyword">var</span> sigUtil <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"eth-sig-util"</span>)

const typedData <span class="hljs-operator">=</span> {
        <span class="hljs-comment">//定义数据格式</span>
        types: {
          <span class="hljs-comment">//固定格式,不要动,并且字段一个都不能少</span>
          <span class="hljs-comment">//name:contract name</span>
          <span class="hljs-comment">//vaersion:版本号,如1.0随意</span>
          <span class="hljs-comment">//chainId:当前链chainId</span>
          <span class="hljs-comment">//verifyingContract:合约地址</span>
          EIP712Domain: [
            {name: <span class="hljs-string">'name'</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">'string'</span>},
            {name: <span class="hljs-string">'version'</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">'string'</span>},
            {name: <span class="hljs-string">'chainId'</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">'uint256'</span> },
            {name: <span class="hljs-string">'verifyingContract'</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">'address'</span> },
          ],
          <span class="hljs-comment">//自定义自己业务的数据格式</span>
          Mail: [
            {name: <span class="hljs-string">'from'</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">'address'</span>},
            {name: <span class="hljs-string">'to'</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">'address'</span>},
            {name: <span class="hljs-string">'value'</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">'uint256'</span>},
          ],
        },
          <span class="hljs-comment">//上面定义的数据格式具体内容</span>
        domain: {
          name: <span class="hljs-string">'Demo'</span>,
          version: <span class="hljs-string">'1.0'</span>,
          chainId:<span class="hljs-string">'1'</span>,
          verifyingContract:<span class="hljs-string">'0xf8e81D47203A594245E36C48e151709F0C19fBe8'</span>
        },
        primaryType: <span class="hljs-string">'Mail'</span>,
          <span class="hljs-comment">//具体的业务字段,需要和上面的Mail格式对应</span>
        message: {
          <span class="hljs-keyword">from</span>: <span class="hljs-string">"0xE3a463d743F762D538031BAD3f1E748BB41f96ec"</span>,
          to: <span class="hljs-string">"0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29"</span>,
          <span class="hljs-built_in">value</span>: <span class="hljs-number">1234234145789</span>,
        },
      }
      <span class="hljs-comment">//生成签名 privateKey 是钱包账户的秘钥</span>
      <span class="hljs-keyword">var</span> privateKeyHex <span class="hljs-operator">=</span> Buffer.from(privateKey, <span class="hljs-string">'hex'</span>)
      <span class="hljs-keyword">var</span> signature <span class="hljs-operator">=</span> sigUtil.signTypedData_v4(privateKeyHex, {data: typedData})
}
</code></pre><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import &quot;https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol&quot;;
import &quot;https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/draft-EIP712.sol&quot;;

contract MyContract is EIP712 {
       
    constructor(string memory name, string memory version) EIP712(name, version) {}
    
     //生成签名的业务参数和生成的签名
    function recoverV4(
        address from,
        address to,
        uint256 value,
        bytes memory signature
    ) public view returns (address) {
        bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
        //需要和js中的type中业务的数据格式对应
        keccak256(&quot;Mail(address from,address to,uint256 value)&quot;),
           from,
            to,
            value
        )));
        return ECDSA.recover(digest, signature);
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/draft-EIP712.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MyContract</span> <span class="hljs-keyword">is</span> <span class="hljs-title">EIP712</span> </span>{
       
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> name, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> version</span>) <span class="hljs-title">EIP712</span>(<span class="hljs-params">name, version</span>) </span>{}
    
     <span class="hljs-comment">//生成签名的业务参数和生成的签名</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">recoverV4</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span>,
        <span class="hljs-keyword">address</span> to,
        <span class="hljs-keyword">uint256</span> value,
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> signature
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        <span class="hljs-keyword">bytes32</span> digest <span class="hljs-operator">=</span> _hashTypedDataV4(<span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(
        <span class="hljs-comment">//需要和js中的type中业务的数据格式对应</span>
        <span class="hljs-built_in">keccak256</span>(<span class="hljs-string">"Mail(address from,address to,uint256 value)"</span>),
           <span class="hljs-keyword">from</span>,
            to,
            value
        )));
        <span class="hljs-keyword">return</span> ECDSA.recover(digest, signature);
    }
}
</code></pre>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[Looksrare合约讲解]]></title>
            <link>https://paragraph.com/@point/looksrare</link>
            <guid>54KAEEuiE7sIAp63Mrfj</guid>
            <pubDate>Mon, 02 May 2022 15:35:59 GMT</pubDate>
            <description><![CDATA[因为做了一个NFT市场，所以对opensea，rarible，looksrare合约都研究了下，记录如下 合约架构 https://etherscan.io/accounts/label/looksrare 三家的合约高度相似，只不过是将代码重构的好看一些。再加些最近才有的EIP。looksare基本上是把能拆的都拆了，比如手续费、版权（EIP2981）、transfer，对比下另外两家，舒服很多 业务逻辑基本都是链下操作，只有在真正mint nft，撮合交易成功，取消交易才会上链。甚至现在mint nft都不上链，第一次transfer时才会mint nft。进一步减少gas消耗。创建订单：使用订单参数+salt进行EIP712 signature，transfer时在合约中验证签名，这样能减少Gas和保证订单没问题。。。。。才怪，open就出了有人拿到signature后，等订单过期了再去调用合约，把人家的nft低价撸走的bug。取消订单：链上不保存创建订单。就像上面说的，防止bug出现，就要链上保存取消的订单，但是opensea没有判断 currentTime>expire...]]></description>
            <content:encoded><![CDATA[<p>因为做了一个NFT市场，所以对opensea，rarible，looksrare合约都研究了下，记录如下</p><p>合约架构</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/accounts/label/looksrare">https://etherscan.io/accounts/label/looksrare</a></p><p>三家的合约高度相似，只不过是将代码重构的好看一些。再加些最近才有的EIP。looksare基本上是把能拆的都拆了，比如手续费、版权（EIP2981）、transfer，对比下另外两家，舒服很多</p><p>业务逻辑</p><ol><li><p>基本都是链下操作，只有在真正mint nft，撮合交易成功，取消交易才会上链。甚至现在mint nft都不上链，第一次transfer时才会mint nft。进一步减少gas消耗。</p></li><li><p>创建订单：使用订单参数+salt进行EIP712 signature，transfer时在合约中验证签名，这样能减少Gas和保证订单没问题。。。。。才怪，open就出了有人拿到signature后，等订单过期了再去调用合约，把人家的nft低价撸走的bug。</p></li><li><p>取消订单：链上不保存创建订单。就像上面说的，防止bug出现，就要链上保存取消的订单，但是opensea没有判断 currentTime&gt;expireTime 所以拿过期的signature还是可以完成交易</p></li><li><p>取消所有订单，减少gas，理论上也不是取消所有，一次的上限是500000</p></li><li><p>版权费：使用EIP2981，原因是像opensea，有版权费上限，如果我要收100%的版权费，没辙，所以要一个链上保存版权费的功能</p></li></ol><p>代码结构</p><ol><li><p>共两个cancelOrder</p></li><li><p>三个撮合函数</p></li><li><p>好几个update周边合约函数</p></li><li><p>两个transfer token函数</p></li><li><p>一个transfer nft函数</p></li><li><p>一个校验函数</p></li></ol><pre data-type="codeBlock" text="//取消订单
cancelAllOrdersForSender
cancelMultipleMakerOrders
//撮合函数
//卖家挂单
matchAskWithTakerBidUsingETHAndWETH
matchAskWithTakerBid
//买家询价
matchBidWithTakerAsk
//转钱
_transferFeesAndFunds
_transferFeesAndFundsWithWETH
//转nft
_transferNonFungibleToken
//校验订单
_validateOrder
"><code><span class="hljs-comment">//取消订单</span>
cancelAllOrdersForSender
cancelMultipleMakerOrders
<span class="hljs-comment">//撮合函数</span>
<span class="hljs-comment">//卖家挂单</span>
matchAskWithTakerBidUsingETHAndWETH
matchAskWithTakerBid
<span class="hljs-comment">//买家询价</span>
matchBidWithTakerAsk
<span class="hljs-comment">//转钱</span>
_transferFeesAndFunds
_transferFeesAndFundsWithWETH
<span class="hljs-comment">//转nft</span>
_transferNonFungibleToken
<span class="hljs-comment">//校验订单</span>
_validateOrder
</code></pre><p>从上面函数名可以看出，好几个都是相似的内容，所以合约还是比较容易看懂的</p><h2 id="h-exchange" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Exchange合约注释</h2><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// OpenZeppelin contracts
import {Ownable} from &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;
import {ReentrancyGuard} from &quot;@openzeppelin/contracts/security/ReentrancyGuard.sol&quot;;
import {IERC20, SafeERC20} from &quot;@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol&quot;;

// LooksRare interfaces
import {ICurrencyManager} from &quot;./interfaces/ICurrencyManager.sol&quot;;
import {IExecutionManager} from &quot;./interfaces/IExecutionManager.sol&quot;;
import {IExecutionStrategy} from &quot;./interfaces/IExecutionStrategy.sol&quot;;
import {IRoyaltyFeeManager} from &quot;./interfaces/IRoyaltyFeeManager.sol&quot;;
import {ILooksRareExchange} from &quot;./interfaces/ILooksRareExchange.sol&quot;;
import {ITransferManagerNFT} from &quot;./interfaces/ITransferManagerNFT.sol&quot;;
import {ITransferSelectorNFT} from &quot;./interfaces/ITransferSelectorNFT.sol&quot;;
import {IWETH} from &quot;./interfaces/IWETH.sol&quot;;

// LooksRare libraries
import {OrderTypes} from &quot;./libraries/OrderTypes.sol&quot;;
import {SignatureChecker} from &quot;./libraries/SignatureChecker.sol&quot;;


contract LooksRareExchange is ILooksRareExchange, ReentrancyGuard, Ownable {
    using SafeERC20 for IERC20;
    //订单的卖家和买家信息
    using OrderTypes for OrderTypes.MakerOrder;
    using OrderTypes for OrderTypes.TakerOrder;
    //weth的地址
    address public immutable WETH;
    、、EIP712的信息
    bytes32 public immutable DOMAIN_SEPARATOR;
    //手续费地址
    address public protocolFeeRecipient;
    //looksrare的周边合约地址
    ICurrencyManager public currencyManager;
    IExecutionManager public executionManager;
    IRoyaltyFeeManager public royaltyFeeManager;
    ITransferSelectorNFT public transferSelectorNFT;
    //用于取消订单和校验订单是否合法的逻辑
    mapping(address =&gt; uint256) public userMinOrderNonce;
    mapping(address =&gt; mapping(uint256 =&gt; bool)) private _isUserOrderNonceExecutedOrCancelled;

    event CancelAllOrders(address indexed user, uint256 newMinNonce);
    event CancelMultipleOrders(address indexed user, uint256[] orderNonces);
    event NewCurrencyManager(address indexed currencyManager);
    event NewExecutionManager(address indexed executionManager);
    event NewProtocolFeeRecipient(address indexed protocolFeeRecipient);
    event NewRoyaltyFeeManager(address indexed royaltyFeeManager);
    event NewTransferSelectorNFT(address indexed transferSelectorNFT);

    event RoyaltyPayment(
        address indexed collection,
        uint256 indexed tokenId,
        address indexed royaltyRecipient,
        address currency,
        uint256 amount
    );

    event TakerAsk(
        bytes32 orderHash, // bid hash of the maker order
        uint256 orderNonce, // user order nonce
        address indexed taker, // sender address for the taker ask order
        address indexed maker, // maker address of the initial bid order
        address indexed strategy, // strategy that defines the execution
        address currency, // currency address
        address collection, // collection address
        uint256 tokenId, // tokenId transferred
        uint256 amount, // amount of tokens transferred
        uint256 price // final transacted price
    );

    event TakerBid(
        bytes32 orderHash, // ask hash of the maker order
        uint256 orderNonce, // user order nonce
        address indexed taker, // sender address for the taker bid order
        address indexed maker, // maker address of the initial ask order
        address indexed strategy, // strategy that defines the execution
        address currency, // currency address
        address collection, // collection address
        uint256 tokenId, // tokenId transferred
        uint256 amount, // amount of tokens transferred
        uint256 price // final transacted price
    );

    /**
     * @notice Constructor 保存好签名校验需要的参数，保存周边合约的地址
     * @param _currencyManager currency manager address
     * @param _executionManager execution manager address
     * @param _royaltyFeeManager royalty fee manager address
     * @param _WETH wrapped ether address (for other chains, use wrapped native asset)
     * @param _protocolFeeRecipient protocol fee recipient
     */
    constructor(
        address _currencyManager,
        address _executionManager,
        address _royaltyFeeManager,
        address _WETH,
        address _protocolFeeRecipient
    ) {
        // Calculate the domain separator
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, // keccak256(&quot;EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)&quot;)
                0xda9101ba92939daf4bb2e18cd5f942363b9297fbc3232c9dd964abb1fb70ed71, // keccak256(&quot;LooksRareExchange&quot;)
                0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6, // keccak256(bytes(&quot;1&quot;)) for versionId = 1
                block.chainid,
                address(this)
            )
        );

        currencyManager = ICurrencyManager(_currencyManager);
        executionManager = IExecutionManager(_executionManager);
        royaltyFeeManager = IRoyaltyFeeManager(_royaltyFeeManager);
        WETH = _WETH;
        protocolFeeRecipient = _protocolFeeRecipient;
    }

    /**
     * @notice Cancel all pending orders for a sender
        取消所有订单
     * @param minNonce minimum user nonce
     */
    function cancelAllOrdersForSender(uint256 minNonce) external {
        require(minNonce &gt; userMinOrderNonce[msg.sender], &quot;Cancel: Order nonce lower than current&quot;);
        require(minNonce &lt; userMinOrderNonce[msg.sender] + 500000, &quot;Cancel: Cannot cancel more orders&quot;);
        userMinOrderNonce[msg.sender] = minNonce;

        emit CancelAllOrders(msg.sender, minNonce);
    }

    /**
     * @notice Cancel maker orders
      根据需要取消订单
     * @param orderNonces array of order nonces
     */
    function cancelMultipleMakerOrders(uint256[] calldata orderNonces) external {
        require(orderNonces.length &gt; 0, &quot;Cancel: Cannot be empty&quot;);

        for (uint256 i = 0; i &lt; orderNonces.length; i++) {
            require(orderNonces[i] &gt;= userMinOrderNonce[msg.sender], &quot;Cancel: Order nonce lower than current&quot;);
            _isUserOrderNonceExecutedOrCancelled[msg.sender][orderNonces[i]] = true;
        }

        emit CancelMultipleOrders(msg.sender, orderNonces);
    }

    /**
     * @notice Match ask with a taker bid order using ETH
        核心exchange业务逻辑并且eth和weth混合支付，可能你的weth不足，不要紧，再用点eth，这里面会将eth换成weth，就不用你先去兑换了，节省gas
     * @param takerBid taker bid order
     * @param makerAsk maker ask order
     */
    function matchAskWithTakerBidUsingETHAndWETH(
        OrderTypes.TakerOrder calldata takerBid,
        OrderTypes.MakerOrder calldata makerAsk
    ) external payable override nonReentrant {
     //确定是卖家挂单才行，因为买家询价是不能用eth支付的
        require((makerAsk.isOrderAsk) &amp;&amp; (!takerBid.isOrderAsk), &quot;Order: Wrong sides&quot;);
        require(makerAsk.currency == WETH, &quot;Order: Currency must be WETH&quot;);
        require(msg.sender == takerBid.taker, &quot;Order: Taker must be the sender&quot;);
    //eth不足，先将weth转移给卖家
        // If not enough ETH to cover the price, use WETH
        if (takerBid.price &gt; msg.value) {
            IERC20(WETH).safeTransferFrom(msg.sender, address(this), (takerBid.price - msg.value));
        } else {
            require(takerBid.price == msg.value, &quot;Order: Msg.value too high&quot;);
        }
        //将eth兑换成weth来交易
        // Wrap ETH sent to this contract
        IWETH(WETH).deposit{value: msg.value}();
        //使用eip712校验订单
        // Check the maker ask order
        bytes32 askHash = makerAsk.hash();
        _validateOrder(makerAsk, askHash);
         //挂单，拍卖有不同的交易费费率，从这取出来
        // Retrieve execution parameters
        (bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerAsk.strategy)
            .canExecuteTakerBid(takerBid, makerAsk);

        require(isExecutionValid, &quot;Strategy: Execution invalid&quot;);

        // Update maker ask order status to true (prevents replay)
        //先将订单置为已完成，防止重入
        _isUserOrderNonceExecutedOrCancelled[makerAsk.signer][makerAsk.nonce] = true;

        // Execution part 1/2
        //真正开始交易的地方，分两步，转移钱，再转移nft
        _transferFeesAndFundsWithWETH(
            makerAsk.strategy,
            makerAsk.collection,
            tokenId,
            makerAsk.signer,
            takerBid.price,
            makerAsk.minPercentageToAsk
        );

        // Execution part 2/2
        _transferNonFungibleToken(makerAsk.collection, makerAsk.signer, takerBid.taker, tokenId, amount);

        emit TakerBid(
            askHash,
            makerAsk.nonce,
            takerBid.taker,
            makerAsk.signer,
            makerAsk.strategy,
            makerAsk.currency,
            makerAsk.collection,
            tokenId,
            amount,
            takerBid.price
        );
    }

    /**
     * @notice Match a takerBid with a matchAsk
       基础撮合订单，和上面的区别就是这里只能用weth
     * @param takerBid taker bid order
     * @param makerAsk maker ask order
     */
    function matchAskWithTakerBid(OrderTypes.TakerOrder calldata takerBid, OrderTypes.MakerOrder calldata makerAsk)
        external
        override
        nonReentrant
    {
        require((makerAsk.isOrderAsk) &amp;&amp; (!takerBid.isOrderAsk), &quot;Order: Wrong sides&quot;);
        require(msg.sender == takerBid.taker, &quot;Order: Taker must be the sender&quot;);

        // Check the maker ask order
        bytes32 askHash = makerAsk.hash();
        _validateOrder(makerAsk, askHash);

        (bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerAsk.strategy)
            .canExecuteTakerBid(takerBid, makerAsk);

        require(isExecutionValid, &quot;Strategy: Execution invalid&quot;);

        // Update maker ask order status to true (prevents replay)
        _isUserOrderNonceExecutedOrCancelled[makerAsk.signer][makerAsk.nonce] = true;

        // Execution part 1/2
        _transferFeesAndFunds(
            makerAsk.strategy,
            makerAsk.collection,
            tokenId,
            makerAsk.currency,
            msg.sender,
            makerAsk.signer,
            takerBid.price,
            makerAsk.minPercentageToAsk
        );

        // Execution part 2/2
        _transferNonFungibleToken(makerAsk.collection, makerAsk.signer, takerBid.taker, tokenId, amount);

        emit TakerBid(
            askHash,
            makerAsk.nonce,
            takerBid.taker,
            makerAsk.signer,
            makerAsk.strategy,
            makerAsk.currency,
            makerAsk.collection,
            tokenId,
            amount,
            takerBid.price
        );
    }

    /**
     * @notice Match a takerAsk with a makerBid
        撮合订单，这里和上面一个的区别是这里是买家询价，上面是卖家挂单，这里的签名者是买家，上面的是卖家
     * @param takerAsk taker ask order
     * @param makerBid maker bid order
     */
    function matchBidWithTakerAsk(OrderTypes.TakerOrder calldata takerAsk, OrderTypes.MakerOrder calldata makerBid)
        external
        override
        nonReentrant
    {
        require((!makerBid.isOrderAsk) &amp;&amp; (takerAsk.isOrderAsk), &quot;Order: Wrong sides&quot;);
        require(msg.sender == takerAsk.taker, &quot;Order: Taker must be the sender&quot;);

        // Check the maker bid order
        bytes32 bidHash = makerBid.hash();
        _validateOrder(makerBid, bidHash);

        (bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerBid.strategy)
            .canExecuteTakerAsk(takerAsk, makerBid);

        require(isExecutionValid, &quot;Strategy: Execution invalid&quot;);

        // Update maker bid order status to true (prevents replay)
        _isUserOrderNonceExecutedOrCancelled[makerBid.signer][makerBid.nonce] = true;

        // Execution part 1/2
        _transferNonFungibleToken(makerBid.collection, msg.sender, makerBid.signer, tokenId, amount);

        // Execution part 2/2
        _transferFeesAndFunds(
            makerBid.strategy,
            makerBid.collection,
            tokenId,
            makerBid.currency,
            makerBid.signer,
            takerAsk.taker,
            takerAsk.price,
            takerAsk.minPercentageToAsk
        );

        emit TakerAsk(
            bidHash,
            makerBid.nonce,
            takerAsk.taker,
            makerBid.signer,
            makerBid.strategy,
            makerBid.currency,
            makerBid.collection,
            tokenId,
            amount,
            takerAsk.price
        );
    }
    //修改周边合约的地址，用于升级或者出bug用
    /**
     * @notice Update currency manager
     * @param _currencyManager new currency manager address
     */
    function updateCurrencyManager(address _currencyManager) external onlyOwner {
        require(_currencyManager != address(0), &quot;Owner: Cannot be null address&quot;);
        currencyManager = ICurrencyManager(_currencyManager);
        emit NewCurrencyManager(_currencyManager);
    }

    /**
     * @notice Update execution manager
     * @param _executionManager new execution manager address
     */
    function updateExecutionManager(address _executionManager) external onlyOwner {
        require(_executionManager != address(0), &quot;Owner: Cannot be null address&quot;);
        executionManager = IExecutionManager(_executionManager);
        emit NewExecutionManager(_executionManager);
    }

    /**
     * @notice Update protocol fee and recipient
     * @param _protocolFeeRecipient new recipient for protocol fees
     */
    function updateProtocolFeeRecipient(address _protocolFeeRecipient) external onlyOwner {
        protocolFeeRecipient = _protocolFeeRecipient;
        emit NewProtocolFeeRecipient(_protocolFeeRecipient);
    }

    /**
     * @notice Update royalty fee manager
     * @param _royaltyFeeManager new fee manager address
     */
    function updateRoyaltyFeeManager(address _royaltyFeeManager) external onlyOwner {
        require(_royaltyFeeManager != address(0), &quot;Owner: Cannot be null address&quot;);
        royaltyFeeManager = IRoyaltyFeeManager(_royaltyFeeManager);
        emit NewRoyaltyFeeManager(_royaltyFeeManager);
    }

    /**
     * @notice Update transfer selector NFT
     * @param _transferSelectorNFT new transfer selector address
     */
    function updateTransferSelectorNFT(address _transferSelectorNFT) external onlyOwner {
        require(_transferSelectorNFT != address(0), &quot;Owner: Cannot be null address&quot;);
        transferSelectorNFT = ITransferSelectorNFT(_transferSelectorNFT);

        emit NewTransferSelectorNFT(_transferSelectorNFT);
    }

    //订单是否取消
    /**
     * @notice Check whether user order nonce is executed or cancelled
     * @param user address of user
     * @param orderNonce nonce of the order
     */
    function isUserOrderNonceExecutedOrCancelled(address user, uint256 orderNonce) external view returns (bool) {
        return _isUserOrderNonceExecutedOrCancelled[user][orderNonce];
    }


    /**
       N个费用处理，转移钱，用传入的代币交易
     * @notice Transfer fees and funds to royalty recipient, protocol, and seller
     * @param strategy address of the execution strategy
     * @param collection non fungible token address for the transfer
     * @param tokenId tokenId
     * @param currency currency being used for the purchase (e.g., WETH/USDC)
     * @param from sender of the funds
     * @param to seller&apos;s recipient
     * @param amount amount being transferred (in currency)
     * @param minPercentageToAsk minimum percentage of the gross amount that goes to ask
     */
    function _transferFeesAndFunds(
        address strategy,
        address collection,
        uint256 tokenId,
        address currency,
        address from,
        address to,
        uint256 amount,
        uint256 minPercentageToAsk
    ) internal {
        // Initialize the final amount that is transferred to seller
        //最终的给卖家的钱，会经过一层又一层的吃拿卡要
        uint256 finalSellerAmount = amount;

        // 1. Protocol fee 官方手续费
        {
            uint256 protocolFeeAmount = _calculateProtocolFee(strategy, amount);

            // Check if the protocol fee is different than 0 for this strategy
            if ((protocolFeeRecipient != address(0)) &amp;&amp; (protocolFeeAmount != 0)) {
                IERC20(currency).safeTransferFrom(from, protocolFeeRecipient, protocolFeeAmount);
                finalSellerAmount -= protocolFeeAmount;
            }
        }

        // 2. Royalty fee 版权费
        {
            (address royaltyFeeRecipient, uint256 royaltyFeeAmount) = royaltyFeeManager
                .calculateRoyaltyFeeAndGetRecipient(collection, tokenId, amount);

            // Check if there is a royalty fee and that it is different to 0
            if ((royaltyFeeRecipient != address(0)) &amp;&amp; (royaltyFeeAmount != 0)) {
                IERC20(currency).safeTransferFrom(from, royaltyFeeRecipient, royaltyFeeAmount);
                finalSellerAmount -= royaltyFeeAmount;

                emit RoyaltyPayment(collection, tokenId, royaltyFeeRecipient, currency, royaltyFeeAmount);
            }
        }

        //保证给卖家的钱的最小值，要有人性，手动狗头
        require((finalSellerAmount * 10000) &gt;= (minPercentageToAsk * amount), &quot;Fees: Higher than expected&quot;);

        // 3. Transfer final amount (post-fees) to seller
        {
            IERC20(currency).safeTransferFrom(from, to, finalSellerAmount);
        }
    }

    /**
      和上面的类似，只是用了weth
     * @notice Transfer fees and funds to royalty recipient, protocol, and seller
     * @param strategy address of the execution strategy
     * @param collection non fungible token address for the transfer
     * @param tokenId tokenId
     * @param to seller&apos;s recipient
     * @param amount amount being transferred (in currency)
     * @param minPercentageToAsk minimum percentage of the gross amount that goes to ask
     */
    function _transferFeesAndFundsWithWETH(
        address strategy,
        address collection,
        uint256 tokenId,
        address to,
        uint256 amount,
        uint256 minPercentageToAsk
    ) internal {
        // Initialize the final amount that is transferred to seller
        uint256 finalSellerAmount = amount;

        // 1. Protocol fee
        {
            uint256 protocolFeeAmount = _calculateProtocolFee(strategy, amount);

            // Check if the protocol fee is different than 0 for this strategy
            if ((protocolFeeRecipient != address(0)) &amp;&amp; (protocolFeeAmount != 0)) {
                IERC20(WETH).safeTransfer(protocolFeeRecipient, protocolFeeAmount);
                finalSellerAmount -= protocolFeeAmount;
            }
        }

        // 2. Royalty fee
        {
            (address royaltyFeeRecipient, uint256 royaltyFeeAmount) = royaltyFeeManager
                .calculateRoyaltyFeeAndGetRecipient(collection, tokenId, amount);

            // Check if there is a royalty fee and that it is different to 0
            if ((royaltyFeeRecipient != address(0)) &amp;&amp; (royaltyFeeAmount != 0)) {
                IERC20(WETH).safeTransfer(royaltyFeeRecipient, royaltyFeeAmount);
                finalSellerAmount -= royaltyFeeAmount;

                emit RoyaltyPayment(collection, tokenId, royaltyFeeRecipient, address(WETH), royaltyFeeAmount);
            }
        }

        require((finalSellerAmount * 10000) &gt;= (minPercentageToAsk * amount), &quot;Fees: Higher than expected&quot;);

        // 3. Transfer final amount (post-fees) to seller
        {
            IERC20(WETH).safeTransfer(to, finalSellerAmount);
        }
    }

    /**
     * @notice Transfer NFT
        那到官方规定的NFT转移合约，转移NFT
     * @param collection address of the token collection
     * @param from address of the sender
     * @param to address of the recipient
     * @param tokenId tokenId
     * @param amount amount of tokens (1 for ERC721, 1+ for ERC1155)
     * @dev For ERC721, amount is not used
     */
    function _transferNonFungibleToken(
        address collection,
        address from,
        address to,
        uint256 tokenId,
        uint256 amount
    ) internal {
        // Retrieve the transfer manager address
        address transferManager = transferSelectorNFT.checkTransferManagerForToken(collection);

        // If no transfer manager found, it returns address(0)
        require(transferManager != address(0), &quot;Transfer: No NFT transfer manager available&quot;);

        // If one is found, transfer the token
        ITransferManagerNFT(transferManager).transferNonFungibleToken(collection, from, to, tokenId, amount);
    }

    /**
     * @notice Calculate protocol fee for an execution strategy
     * @param executionStrategy strategy
     * @param amount amount to transfer
     */
    function _calculateProtocolFee(address executionStrategy, uint256 amount) internal view returns (uint256) {
        uint256 protocolFee = IExecutionStrategy(executionStrategy).viewProtocolFee();
        return (protocolFee * amount) / 10000;
    }

    /**
        校验订单
     * @notice Verify the validity of the maker order
     * @param makerOrder maker order
     * @param orderHash computed hash for the order
     */
    function _validateOrder(OrderTypes.MakerOrder calldata makerOrder, bytes32 orderHash) internal view {
        // Verify whether order nonce has expired
        require(
  //订单是否取消，并且订单的nonce&gt;合约记录的订单签名者的nonce值，因为可能其他人恶意取消订单，这里取签名者保证别人恶意取消没用       (!_isUserOrderNonceExecutedOrCancelled[makerOrder.signer][makerOrder.nonce]) &amp;&amp;
                (makerOrder.nonce &gt;= userMinOrderNonce[makerOrder.signer]),
            &quot;Order: Matching order expired&quot;
        );

        // Verify the signer is not address(0)
        require(makerOrder.signer != address(0), &quot;Order: Invalid signer&quot;);

        // Verify the amount is not 0
        require(makerOrder.amount &gt; 0, &quot;Order: Amount cannot be 0&quot;);

        // Verify the validity of the signature
        require(
            SignatureChecker.verify(
                orderHash,
                makerOrder.signer,
                makerOrder.v,
                makerOrder.r,
                makerOrder.s,
                DOMAIN_SEPARATOR
            ),
            &quot;Signature: Invalid&quot;
        );

        // Verify whether the currency is whitelisted
        require(currencyManager.isCurrencyWhitelisted(makerOrder.currency), &quot;Currency: Not whitelisted&quot;);

        // Verify whether strategy can be executed
        require(executionManager.isStrategyWhitelisted(makerOrder.strategy), &quot;Strategy: Not whitelisted&quot;);
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>

<span class="hljs-comment">// OpenZeppelin contracts</span>
<span class="hljs-keyword">import</span> {<span class="hljs-title">Ownable</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">ReentrancyGuard</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/security/ReentrancyGuard.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC20</span>, <span class="hljs-title">SafeERC20</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"</span>;

<span class="hljs-comment">// LooksRare interfaces</span>
<span class="hljs-keyword">import</span> {<span class="hljs-title">ICurrencyManager</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./interfaces/ICurrencyManager.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IExecutionManager</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./interfaces/IExecutionManager.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IExecutionStrategy</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./interfaces/IExecutionStrategy.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IRoyaltyFeeManager</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./interfaces/IRoyaltyFeeManager.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">ILooksRareExchange</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./interfaces/ILooksRareExchange.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">ITransferManagerNFT</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./interfaces/ITransferManagerNFT.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">ITransferSelectorNFT</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./interfaces/ITransferSelectorNFT.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IWETH</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./interfaces/IWETH.sol"</span>;

<span class="hljs-comment">// LooksRare libraries</span>
<span class="hljs-keyword">import</span> {<span class="hljs-title">OrderTypes</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./libraries/OrderTypes.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SignatureChecker</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./libraries/SignatureChecker.sol"</span>;


<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">LooksRareExchange</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ILooksRareExchange</span>, <span class="hljs-title">ReentrancyGuard</span>, <span class="hljs-title">Ownable</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">SafeERC20</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">IERC20</span>;
    <span class="hljs-comment">//订单的卖家和买家信息</span>
    <span class="hljs-keyword">using</span> <span class="hljs-title">OrderTypes</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">OrderTypes</span>.<span class="hljs-title">MakerOrder</span>;
    <span class="hljs-keyword">using</span> <span class="hljs-title">OrderTypes</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">OrderTypes</span>.<span class="hljs-title">TakerOrder</span>;
    <span class="hljs-comment">//weth的地址</span>
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">immutable</span> WETH;
    、、EIP712的信息
    <span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">immutable</span> DOMAIN_SEPARATOR;
    <span class="hljs-comment">//手续费地址</span>
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> protocolFeeRecipient;
    <span class="hljs-comment">//looksrare的周边合约地址</span>
    ICurrencyManager <span class="hljs-keyword">public</span> currencyManager;
    IExecutionManager <span class="hljs-keyword">public</span> executionManager;
    IRoyaltyFeeManager <span class="hljs-keyword">public</span> royaltyFeeManager;
    ITransferSelectorNFT <span class="hljs-keyword">public</span> transferSelectorNFT;
    <span class="hljs-comment">//用于取消订单和校验订单是否合法的逻辑</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>) <span class="hljs-keyword">public</span> userMinOrderNonce;
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">bool</span>)) <span class="hljs-keyword">private</span> _isUserOrderNonceExecutedOrCancelled;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">CancelAllOrders</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> user, <span class="hljs-keyword">uint256</span> newMinNonce</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">CancelMultipleOrders</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> user, <span class="hljs-keyword">uint256</span>[] orderNonces</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">NewCurrencyManager</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> currencyManager</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">NewExecutionManager</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> executionManager</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">NewProtocolFeeRecipient</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> protocolFeeRecipient</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">NewRoyaltyFeeManager</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> royaltyFeeManager</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">NewTransferSelectorNFT</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> transferSelectorNFT</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">RoyaltyPayment</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> collection,
        <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> tokenId,
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> royaltyRecipient,
        <span class="hljs-keyword">address</span> currency,
        <span class="hljs-keyword">uint256</span> amount
    </span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">TakerAsk</span>(<span class="hljs-params">
        <span class="hljs-keyword">bytes32</span> orderHash, <span class="hljs-comment">// bid hash of the maker order</span>
        <span class="hljs-keyword">uint256</span> orderNonce, <span class="hljs-comment">// user order nonce</span>
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> taker, <span class="hljs-comment">// sender address for the taker ask order</span>
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> maker, <span class="hljs-comment">// maker address of the initial bid order</span>
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> strategy, <span class="hljs-comment">// strategy that defines the execution</span>
        <span class="hljs-keyword">address</span> currency, <span class="hljs-comment">// currency address</span>
        <span class="hljs-keyword">address</span> collection, <span class="hljs-comment">// collection address</span>
        <span class="hljs-keyword">uint256</span> tokenId, <span class="hljs-comment">// tokenId transferred</span>
        <span class="hljs-keyword">uint256</span> amount, <span class="hljs-comment">// amount of tokens transferred</span>
        <span class="hljs-keyword">uint256</span> price <span class="hljs-comment">// final transacted price</span>
    </span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">TakerBid</span>(<span class="hljs-params">
        <span class="hljs-keyword">bytes32</span> orderHash, <span class="hljs-comment">// ask hash of the maker order</span>
        <span class="hljs-keyword">uint256</span> orderNonce, <span class="hljs-comment">// user order nonce</span>
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> taker, <span class="hljs-comment">// sender address for the taker bid order</span>
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> maker, <span class="hljs-comment">// maker address of the initial ask order</span>
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> strategy, <span class="hljs-comment">// strategy that defines the execution</span>
        <span class="hljs-keyword">address</span> currency, <span class="hljs-comment">// currency address</span>
        <span class="hljs-keyword">address</span> collection, <span class="hljs-comment">// collection address</span>
        <span class="hljs-keyword">uint256</span> tokenId, <span class="hljs-comment">// tokenId transferred</span>
        <span class="hljs-keyword">uint256</span> amount, <span class="hljs-comment">// amount of tokens transferred</span>
        <span class="hljs-keyword">uint256</span> price <span class="hljs-comment">// final transacted price</span>
    </span>)</span>;

    <span class="hljs-comment">/**
     * @notice Constructor 保存好签名校验需要的参数，保存周边合约的地址
     * @param _currencyManager currency manager address
     * @param _executionManager execution manager address
     * @param _royaltyFeeManager royalty fee manager address
     * @param _WETH wrapped ether address (for other chains, use wrapped native asset)
     * @param _protocolFeeRecipient protocol fee recipient
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> _currencyManager,
        <span class="hljs-keyword">address</span> _executionManager,
        <span class="hljs-keyword">address</span> _royaltyFeeManager,
        <span class="hljs-keyword">address</span> _WETH,
        <span class="hljs-keyword">address</span> _protocolFeeRecipient
    </span>) </span>{
        <span class="hljs-comment">// Calculate the domain separator</span>
        DOMAIN_SEPARATOR <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(
            <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(
                <span class="hljs-number">0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f</span>, <span class="hljs-comment">// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")</span>
                <span class="hljs-number">0xda9101ba92939daf4bb2e18cd5f942363b9297fbc3232c9dd964abb1fb70ed71</span>, <span class="hljs-comment">// keccak256("LooksRareExchange")</span>
                <span class="hljs-number">0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6</span>, <span class="hljs-comment">// keccak256(bytes("1")) for versionId = 1</span>
                <span class="hljs-built_in">block</span>.<span class="hljs-built_in">chainid</span>,
                <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)
            )
        );

        currencyManager <span class="hljs-operator">=</span> ICurrencyManager(_currencyManager);
        executionManager <span class="hljs-operator">=</span> IExecutionManager(_executionManager);
        royaltyFeeManager <span class="hljs-operator">=</span> IRoyaltyFeeManager(_royaltyFeeManager);
        WETH <span class="hljs-operator">=</span> _WETH;
        protocolFeeRecipient <span class="hljs-operator">=</span> _protocolFeeRecipient;
    }

    <span class="hljs-comment">/**
     * @notice Cancel all pending orders for a sender
        取消所有订单
     * @param minNonce minimum user nonce
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">cancelAllOrdersForSender</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> minNonce</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">require</span>(minNonce <span class="hljs-operator">></span> userMinOrderNonce[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>], <span class="hljs-string">"Cancel: Order nonce lower than current"</span>);
        <span class="hljs-built_in">require</span>(minNonce <span class="hljs-operator">&#x3C;</span> userMinOrderNonce[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">+</span> <span class="hljs-number">500000</span>, <span class="hljs-string">"Cancel: Cannot cancel more orders"</span>);
        userMinOrderNonce[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">=</span> minNonce;

        <span class="hljs-keyword">emit</span> CancelAllOrders(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, minNonce);
    }

    <span class="hljs-comment">/**
     * @notice Cancel maker orders
      根据需要取消订单
     * @param orderNonces array of order nonces
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">cancelMultipleMakerOrders</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">calldata</span> orderNonces</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">require</span>(orderNonces.<span class="hljs-built_in">length</span> <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"Cancel: Cannot be empty"</span>);

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> orderNonces.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-built_in">require</span>(orderNonces[i] <span class="hljs-operator">></span><span class="hljs-operator">=</span> userMinOrderNonce[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>], <span class="hljs-string">"Cancel: Order nonce lower than current"</span>);
            _isUserOrderNonceExecutedOrCancelled[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>][orderNonces[i]] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
        }

        <span class="hljs-keyword">emit</span> CancelMultipleOrders(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, orderNonces);
    }

    <span class="hljs-comment">/**
     * @notice Match ask with a taker bid order using ETH
        核心exchange业务逻辑并且eth和weth混合支付，可能你的weth不足，不要紧，再用点eth，这里面会将eth换成weth，就不用你先去兑换了，节省gas
     * @param takerBid taker bid order
     * @param makerAsk maker ask order
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matchAskWithTakerBidUsingETHAndWETH</span>(<span class="hljs-params">
        OrderTypes.TakerOrder <span class="hljs-keyword">calldata</span> takerBid,
        OrderTypes.MakerOrder <span class="hljs-keyword">calldata</span> makerAsk
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">nonReentrant</span> </span>{
     <span class="hljs-comment">//确定是卖家挂单才行，因为买家询价是不能用eth支付的</span>
        <span class="hljs-built_in">require</span>((makerAsk.isOrderAsk) <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> (<span class="hljs-operator">!</span>takerBid.isOrderAsk), <span class="hljs-string">"Order: Wrong sides"</span>);
        <span class="hljs-built_in">require</span>(makerAsk.currency <span class="hljs-operator">=</span><span class="hljs-operator">=</span> WETH, <span class="hljs-string">"Order: Currency must be WETH"</span>);
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> takerBid.taker, <span class="hljs-string">"Order: Taker must be the sender"</span>);
    <span class="hljs-comment">//eth不足，先将weth转移给卖家</span>
        <span class="hljs-comment">// If not enough ETH to cover the price, use WETH</span>
        <span class="hljs-keyword">if</span> (takerBid.price <span class="hljs-operator">></span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>) {
            IERC20(WETH).safeTransferFrom(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), (takerBid.price <span class="hljs-operator">-</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>));
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">require</span>(takerBid.price <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>, <span class="hljs-string">"Order: Msg.value too high"</span>);
        }
        <span class="hljs-comment">//将eth兑换成weth来交易</span>
        <span class="hljs-comment">// Wrap ETH sent to this contract</span>
        IWETH(WETH).deposit{<span class="hljs-built_in">value</span>: <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>}();
        <span class="hljs-comment">//使用eip712校验订单</span>
        <span class="hljs-comment">// Check the maker ask order</span>
        <span class="hljs-keyword">bytes32</span> askHash <span class="hljs-operator">=</span> makerAsk.hash();
        _validateOrder(makerAsk, askHash);
         <span class="hljs-comment">//挂单，拍卖有不同的交易费费率，从这取出来</span>
        <span class="hljs-comment">// Retrieve execution parameters</span>
        (<span class="hljs-keyword">bool</span> isExecutionValid, <span class="hljs-keyword">uint256</span> tokenId, <span class="hljs-keyword">uint256</span> amount) <span class="hljs-operator">=</span> IExecutionStrategy(makerAsk.strategy)
            .canExecuteTakerBid(takerBid, makerAsk);

        <span class="hljs-built_in">require</span>(isExecutionValid, <span class="hljs-string">"Strategy: Execution invalid"</span>);

        <span class="hljs-comment">// Update maker ask order status to true (prevents replay)</span>
        <span class="hljs-comment">//先将订单置为已完成，防止重入</span>
        _isUserOrderNonceExecutedOrCancelled[makerAsk.signer][makerAsk.nonce] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;

        <span class="hljs-comment">// Execution part 1/2</span>
        <span class="hljs-comment">//真正开始交易的地方，分两步，转移钱，再转移nft</span>
        _transferFeesAndFundsWithWETH(
            makerAsk.strategy,
            makerAsk.collection,
            tokenId,
            makerAsk.signer,
            takerBid.price,
            makerAsk.minPercentageToAsk
        );

        <span class="hljs-comment">// Execution part 2/2</span>
        _transferNonFungibleToken(makerAsk.collection, makerAsk.signer, takerBid.taker, tokenId, amount);

        <span class="hljs-keyword">emit</span> TakerBid(
            askHash,
            makerAsk.nonce,
            takerBid.taker,
            makerAsk.signer,
            makerAsk.strategy,
            makerAsk.currency,
            makerAsk.collection,
            tokenId,
            amount,
            takerBid.price
        );
    }

    <span class="hljs-comment">/**
     * @notice Match a takerBid with a matchAsk
       基础撮合订单，和上面的区别就是这里只能用weth
     * @param takerBid taker bid order
     * @param makerAsk maker ask order
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matchAskWithTakerBid</span>(<span class="hljs-params">OrderTypes.TakerOrder <span class="hljs-keyword">calldata</span> takerBid, OrderTypes.MakerOrder <span class="hljs-keyword">calldata</span> makerAsk</span>)
        <span class="hljs-title"><span class="hljs-keyword">external</span></span>
        <span class="hljs-title"><span class="hljs-keyword">override</span></span>
        <span class="hljs-title">nonReentrant</span>
    </span>{
        <span class="hljs-built_in">require</span>((makerAsk.isOrderAsk) <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> (<span class="hljs-operator">!</span>takerBid.isOrderAsk), <span class="hljs-string">"Order: Wrong sides"</span>);
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> takerBid.taker, <span class="hljs-string">"Order: Taker must be the sender"</span>);

        <span class="hljs-comment">// Check the maker ask order</span>
        <span class="hljs-keyword">bytes32</span> askHash <span class="hljs-operator">=</span> makerAsk.hash();
        _validateOrder(makerAsk, askHash);

        (<span class="hljs-keyword">bool</span> isExecutionValid, <span class="hljs-keyword">uint256</span> tokenId, <span class="hljs-keyword">uint256</span> amount) <span class="hljs-operator">=</span> IExecutionStrategy(makerAsk.strategy)
            .canExecuteTakerBid(takerBid, makerAsk);

        <span class="hljs-built_in">require</span>(isExecutionValid, <span class="hljs-string">"Strategy: Execution invalid"</span>);

        <span class="hljs-comment">// Update maker ask order status to true (prevents replay)</span>
        _isUserOrderNonceExecutedOrCancelled[makerAsk.signer][makerAsk.nonce] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;

        <span class="hljs-comment">// Execution part 1/2</span>
        _transferFeesAndFunds(
            makerAsk.strategy,
            makerAsk.collection,
            tokenId,
            makerAsk.currency,
            <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>,
            makerAsk.signer,
            takerBid.price,
            makerAsk.minPercentageToAsk
        );

        <span class="hljs-comment">// Execution part 2/2</span>
        _transferNonFungibleToken(makerAsk.collection, makerAsk.signer, takerBid.taker, tokenId, amount);

        <span class="hljs-keyword">emit</span> TakerBid(
            askHash,
            makerAsk.nonce,
            takerBid.taker,
            makerAsk.signer,
            makerAsk.strategy,
            makerAsk.currency,
            makerAsk.collection,
            tokenId,
            amount,
            takerBid.price
        );
    }

    <span class="hljs-comment">/**
     * @notice Match a takerAsk with a makerBid
        撮合订单，这里和上面一个的区别是这里是买家询价，上面是卖家挂单，这里的签名者是买家，上面的是卖家
     * @param takerAsk taker ask order
     * @param makerBid maker bid order
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matchBidWithTakerAsk</span>(<span class="hljs-params">OrderTypes.TakerOrder <span class="hljs-keyword">calldata</span> takerAsk, OrderTypes.MakerOrder <span class="hljs-keyword">calldata</span> makerBid</span>)
        <span class="hljs-title"><span class="hljs-keyword">external</span></span>
        <span class="hljs-title"><span class="hljs-keyword">override</span></span>
        <span class="hljs-title">nonReentrant</span>
    </span>{
        <span class="hljs-built_in">require</span>((<span class="hljs-operator">!</span>makerBid.isOrderAsk) <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> (takerAsk.isOrderAsk), <span class="hljs-string">"Order: Wrong sides"</span>);
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> takerAsk.taker, <span class="hljs-string">"Order: Taker must be the sender"</span>);

        <span class="hljs-comment">// Check the maker bid order</span>
        <span class="hljs-keyword">bytes32</span> bidHash <span class="hljs-operator">=</span> makerBid.hash();
        _validateOrder(makerBid, bidHash);

        (<span class="hljs-keyword">bool</span> isExecutionValid, <span class="hljs-keyword">uint256</span> tokenId, <span class="hljs-keyword">uint256</span> amount) <span class="hljs-operator">=</span> IExecutionStrategy(makerBid.strategy)
            .canExecuteTakerAsk(takerAsk, makerBid);

        <span class="hljs-built_in">require</span>(isExecutionValid, <span class="hljs-string">"Strategy: Execution invalid"</span>);

        <span class="hljs-comment">// Update maker bid order status to true (prevents replay)</span>
        _isUserOrderNonceExecutedOrCancelled[makerBid.signer][makerBid.nonce] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;

        <span class="hljs-comment">// Execution part 1/2</span>
        _transferNonFungibleToken(makerBid.collection, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, makerBid.signer, tokenId, amount);

        <span class="hljs-comment">// Execution part 2/2</span>
        _transferFeesAndFunds(
            makerBid.strategy,
            makerBid.collection,
            tokenId,
            makerBid.currency,
            makerBid.signer,
            takerAsk.taker,
            takerAsk.price,
            takerAsk.minPercentageToAsk
        );

        <span class="hljs-keyword">emit</span> TakerAsk(
            bidHash,
            makerBid.nonce,
            takerAsk.taker,
            makerBid.signer,
            makerBid.strategy,
            makerBid.currency,
            makerBid.collection,
            tokenId,
            amount,
            takerAsk.price
        );
    }
    <span class="hljs-comment">//修改周边合约的地址，用于升级或者出bug用</span>
    <span class="hljs-comment">/**
     * @notice Update currency manager
     * @param _currencyManager new currency manager address
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateCurrencyManager</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _currencyManager</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-built_in">require</span>(_currencyManager <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>), <span class="hljs-string">"Owner: Cannot be null address"</span>);
        currencyManager <span class="hljs-operator">=</span> ICurrencyManager(_currencyManager);
        <span class="hljs-keyword">emit</span> NewCurrencyManager(_currencyManager);
    }

    <span class="hljs-comment">/**
     * @notice Update execution manager
     * @param _executionManager new execution manager address
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateExecutionManager</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _executionManager</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-built_in">require</span>(_executionManager <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>), <span class="hljs-string">"Owner: Cannot be null address"</span>);
        executionManager <span class="hljs-operator">=</span> IExecutionManager(_executionManager);
        <span class="hljs-keyword">emit</span> NewExecutionManager(_executionManager);
    }

    <span class="hljs-comment">/**
     * @notice Update protocol fee and recipient
     * @param _protocolFeeRecipient new recipient for protocol fees
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateProtocolFeeRecipient</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _protocolFeeRecipient</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        protocolFeeRecipient <span class="hljs-operator">=</span> _protocolFeeRecipient;
        <span class="hljs-keyword">emit</span> NewProtocolFeeRecipient(_protocolFeeRecipient);
    }

    <span class="hljs-comment">/**
     * @notice Update royalty fee manager
     * @param _royaltyFeeManager new fee manager address
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateRoyaltyFeeManager</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _royaltyFeeManager</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-built_in">require</span>(_royaltyFeeManager <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>), <span class="hljs-string">"Owner: Cannot be null address"</span>);
        royaltyFeeManager <span class="hljs-operator">=</span> IRoyaltyFeeManager(_royaltyFeeManager);
        <span class="hljs-keyword">emit</span> NewRoyaltyFeeManager(_royaltyFeeManager);
    }

    <span class="hljs-comment">/**
     * @notice Update transfer selector NFT
     * @param _transferSelectorNFT new transfer selector address
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateTransferSelectorNFT</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _transferSelectorNFT</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-built_in">require</span>(_transferSelectorNFT <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>), <span class="hljs-string">"Owner: Cannot be null address"</span>);
        transferSelectorNFT <span class="hljs-operator">=</span> ITransferSelectorNFT(_transferSelectorNFT);

        <span class="hljs-keyword">emit</span> NewTransferSelectorNFT(_transferSelectorNFT);
    }

    <span class="hljs-comment">//订单是否取消</span>
    <span class="hljs-comment">/**
     * @notice Check whether user order nonce is executed or cancelled
     * @param user address of user
     * @param orderNonce nonce of the order
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isUserOrderNonceExecutedOrCancelled</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> user, <span class="hljs-keyword">uint256</span> orderNonce</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">return</span> _isUserOrderNonceExecutedOrCancelled[user][orderNonce];
    }


    <span class="hljs-comment">/**
       N个费用处理，转移钱，用传入的代币交易
     * @notice Transfer fees and funds to royalty recipient, protocol, and seller
     * @param strategy address of the execution strategy
     * @param collection non fungible token address for the transfer
     * @param tokenId tokenId
     * @param currency currency being used for the purchase (e.g., WETH/USDC)
     * @param from sender of the funds
     * @param to seller's recipient
     * @param amount amount being transferred (in currency)
     * @param minPercentageToAsk minimum percentage of the gross amount that goes to ask
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_transferFeesAndFunds</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> strategy,
        <span class="hljs-keyword">address</span> collection,
        <span class="hljs-keyword">uint256</span> tokenId,
        <span class="hljs-keyword">address</span> currency,
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span>,
        <span class="hljs-keyword">address</span> to,
        <span class="hljs-keyword">uint256</span> amount,
        <span class="hljs-keyword">uint256</span> minPercentageToAsk
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        <span class="hljs-comment">// Initialize the final amount that is transferred to seller</span>
        <span class="hljs-comment">//最终的给卖家的钱，会经过一层又一层的吃拿卡要</span>
        <span class="hljs-keyword">uint256</span> finalSellerAmount <span class="hljs-operator">=</span> amount;

        <span class="hljs-comment">// 1. Protocol fee 官方手续费</span>
        {
            <span class="hljs-keyword">uint256</span> protocolFeeAmount <span class="hljs-operator">=</span> _calculateProtocolFee(strategy, amount);

            <span class="hljs-comment">// Check if the protocol fee is different than 0 for this strategy</span>
            <span class="hljs-keyword">if</span> ((protocolFeeRecipient <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>)) <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> (protocolFeeAmount <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>)) {
                IERC20(currency).safeTransferFrom(<span class="hljs-keyword">from</span>, protocolFeeRecipient, protocolFeeAmount);
                finalSellerAmount <span class="hljs-operator">-</span><span class="hljs-operator">=</span> protocolFeeAmount;
            }
        }

        <span class="hljs-comment">// 2. Royalty fee 版权费</span>
        {
            (<span class="hljs-keyword">address</span> royaltyFeeRecipient, <span class="hljs-keyword">uint256</span> royaltyFeeAmount) <span class="hljs-operator">=</span> royaltyFeeManager
                .calculateRoyaltyFeeAndGetRecipient(collection, tokenId, amount);

            <span class="hljs-comment">// Check if there is a royalty fee and that it is different to 0</span>
            <span class="hljs-keyword">if</span> ((royaltyFeeRecipient <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>)) <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> (royaltyFeeAmount <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>)) {
                IERC20(currency).safeTransferFrom(<span class="hljs-keyword">from</span>, royaltyFeeRecipient, royaltyFeeAmount);
                finalSellerAmount <span class="hljs-operator">-</span><span class="hljs-operator">=</span> royaltyFeeAmount;

                <span class="hljs-keyword">emit</span> RoyaltyPayment(collection, tokenId, royaltyFeeRecipient, currency, royaltyFeeAmount);
            }
        }

        <span class="hljs-comment">//保证给卖家的钱的最小值，要有人性，手动狗头</span>
        <span class="hljs-built_in">require</span>((finalSellerAmount <span class="hljs-operator">*</span> <span class="hljs-number">10000</span>) <span class="hljs-operator">></span><span class="hljs-operator">=</span> (minPercentageToAsk <span class="hljs-operator">*</span> amount), <span class="hljs-string">"Fees: Higher than expected"</span>);

        <span class="hljs-comment">// 3. Transfer final amount (post-fees) to seller</span>
        {
            IERC20(currency).safeTransferFrom(<span class="hljs-keyword">from</span>, to, finalSellerAmount);
        }
    }

    <span class="hljs-comment">/**
      和上面的类似，只是用了weth
     * @notice Transfer fees and funds to royalty recipient, protocol, and seller
     * @param strategy address of the execution strategy
     * @param collection non fungible token address for the transfer
     * @param tokenId tokenId
     * @param to seller's recipient
     * @param amount amount being transferred (in currency)
     * @param minPercentageToAsk minimum percentage of the gross amount that goes to ask
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_transferFeesAndFundsWithWETH</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> strategy,
        <span class="hljs-keyword">address</span> collection,
        <span class="hljs-keyword">uint256</span> tokenId,
        <span class="hljs-keyword">address</span> to,
        <span class="hljs-keyword">uint256</span> amount,
        <span class="hljs-keyword">uint256</span> minPercentageToAsk
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        <span class="hljs-comment">// Initialize the final amount that is transferred to seller</span>
        <span class="hljs-keyword">uint256</span> finalSellerAmount <span class="hljs-operator">=</span> amount;

        <span class="hljs-comment">// 1. Protocol fee</span>
        {
            <span class="hljs-keyword">uint256</span> protocolFeeAmount <span class="hljs-operator">=</span> _calculateProtocolFee(strategy, amount);

            <span class="hljs-comment">// Check if the protocol fee is different than 0 for this strategy</span>
            <span class="hljs-keyword">if</span> ((protocolFeeRecipient <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>)) <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> (protocolFeeAmount <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>)) {
                IERC20(WETH).safeTransfer(protocolFeeRecipient, protocolFeeAmount);
                finalSellerAmount <span class="hljs-operator">-</span><span class="hljs-operator">=</span> protocolFeeAmount;
            }
        }

        <span class="hljs-comment">// 2. Royalty fee</span>
        {
            (<span class="hljs-keyword">address</span> royaltyFeeRecipient, <span class="hljs-keyword">uint256</span> royaltyFeeAmount) <span class="hljs-operator">=</span> royaltyFeeManager
                .calculateRoyaltyFeeAndGetRecipient(collection, tokenId, amount);

            <span class="hljs-comment">// Check if there is a royalty fee and that it is different to 0</span>
            <span class="hljs-keyword">if</span> ((royaltyFeeRecipient <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>)) <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> (royaltyFeeAmount <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>)) {
                IERC20(WETH).safeTransfer(royaltyFeeRecipient, royaltyFeeAmount);
                finalSellerAmount <span class="hljs-operator">-</span><span class="hljs-operator">=</span> royaltyFeeAmount;

                <span class="hljs-keyword">emit</span> RoyaltyPayment(collection, tokenId, royaltyFeeRecipient, <span class="hljs-keyword">address</span>(WETH), royaltyFeeAmount);
            }
        }

        <span class="hljs-built_in">require</span>((finalSellerAmount <span class="hljs-operator">*</span> <span class="hljs-number">10000</span>) <span class="hljs-operator">></span><span class="hljs-operator">=</span> (minPercentageToAsk <span class="hljs-operator">*</span> amount), <span class="hljs-string">"Fees: Higher than expected"</span>);

        <span class="hljs-comment">// 3. Transfer final amount (post-fees) to seller</span>
        {
            IERC20(WETH).safeTransfer(to, finalSellerAmount);
        }
    }

    <span class="hljs-comment">/**
     * @notice Transfer NFT
        那到官方规定的NFT转移合约，转移NFT
     * @param collection address of the token collection
     * @param from address of the sender
     * @param to address of the recipient
     * @param tokenId tokenId
     * @param amount amount of tokens (1 for ERC721, 1+ for ERC1155)
     * @dev For ERC721, amount is not used
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_transferNonFungibleToken</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> collection,
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span>,
        <span class="hljs-keyword">address</span> to,
        <span class="hljs-keyword">uint256</span> tokenId,
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        <span class="hljs-comment">// Retrieve the transfer manager address</span>
        <span class="hljs-keyword">address</span> transferManager <span class="hljs-operator">=</span> transferSelectorNFT.checkTransferManagerForToken(collection);

        <span class="hljs-comment">// If no transfer manager found, it returns address(0)</span>
        <span class="hljs-built_in">require</span>(transferManager <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>), <span class="hljs-string">"Transfer: No NFT transfer manager available"</span>);

        <span class="hljs-comment">// If one is found, transfer the token</span>
        ITransferManagerNFT(transferManager).transferNonFungibleToken(collection, <span class="hljs-keyword">from</span>, to, tokenId, amount);
    }

    <span class="hljs-comment">/**
     * @notice Calculate protocol fee for an execution strategy
     * @param executionStrategy strategy
     * @param amount amount to transfer
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_calculateProtocolFee</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> executionStrategy, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">uint256</span> protocolFee <span class="hljs-operator">=</span> IExecutionStrategy(executionStrategy).viewProtocolFee();
        <span class="hljs-keyword">return</span> (protocolFee <span class="hljs-operator">*</span> amount) <span class="hljs-operator">/</span> <span class="hljs-number">10000</span>;
    }

    <span class="hljs-comment">/**
        校验订单
     * @notice Verify the validity of the maker order
     * @param makerOrder maker order
     * @param orderHash computed hash for the order
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_validateOrder</span>(<span class="hljs-params">OrderTypes.MakerOrder <span class="hljs-keyword">calldata</span> makerOrder, <span class="hljs-keyword">bytes32</span> orderHash</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> </span>{
        <span class="hljs-comment">// Verify whether order nonce has expired</span>
        <span class="hljs-built_in">require</span>(
  <span class="hljs-comment">//订单是否取消，并且订单的nonce>合约记录的订单签名者的nonce值，因为可能其他人恶意取消订单，这里取签名者保证别人恶意取消没用       (!_isUserOrderNonceExecutedOrCancelled[makerOrder.signer][makerOrder.nonce]) &#x26;&#x26;</span>
                (makerOrder.nonce <span class="hljs-operator">></span><span class="hljs-operator">=</span> userMinOrderNonce[makerOrder.signer]),
            <span class="hljs-string">"Order: Matching order expired"</span>
        );

        <span class="hljs-comment">// Verify the signer is not address(0)</span>
        <span class="hljs-built_in">require</span>(makerOrder.signer <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>), <span class="hljs-string">"Order: Invalid signer"</span>);

        <span class="hljs-comment">// Verify the amount is not 0</span>
        <span class="hljs-built_in">require</span>(makerOrder.amount <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"Order: Amount cannot be 0"</span>);

        <span class="hljs-comment">// Verify the validity of the signature</span>
        <span class="hljs-built_in">require</span>(
            SignatureChecker.verify(
                orderHash,
                makerOrder.signer,
                makerOrder.v,
                makerOrder.r,
                makerOrder.s,
                DOMAIN_SEPARATOR
            ),
            <span class="hljs-string">"Signature: Invalid"</span>
        );

        <span class="hljs-comment">// Verify whether the currency is whitelisted</span>
        <span class="hljs-built_in">require</span>(currencyManager.isCurrencyWhitelisted(makerOrder.currency), <span class="hljs-string">"Currency: Not whitelisted"</span>);

        <span class="hljs-comment">// Verify whether strategy can be executed</span>
        <span class="hljs-built_in">require</span>(executionManager.isStrategyWhitelisted(makerOrder.strategy), <span class="hljs-string">"Strategy: Not whitelisted"</span>);
    }
}
</code></pre>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
        <item>
            <title><![CDATA[Ether.js+Web3modal基础使用]]></title>
            <link>https://paragraph.com/@point/ether-js-web3modal</link>
            <guid>XOgYMQIK2V86Elh0fSJY</guid>
            <pubDate>Mon, 02 May 2022 15:00:40 GMT</pubDate>
            <description><![CDATA[1.说明现在网站会提供很多种钱包，web3modal可以提供统一的provider，不需要你操心太多东西用ether.js而不是web3.js的原因是简单，爽2.安装npm i web3modal npm i ethers //另外还需要安装对应wallet的包，自行搜索就行 3.连接钱包// MM默认就有，无需显式加入 const providerOptions = { walletconnect: { package: walletconnectProvider, options: { infuraId: "", }, }, }; //构建Web3Modal对象 const web3Modal = new Web3Modal({ //缓存provider cacheProvider: true, providerOptions, }); //连接wallet async function connect() { try { const web3ModalProvider = await web3Modal.connect(); provider = new ethers.pr...]]></description>
            <content:encoded><![CDATA[<h2 id="h-1" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1.说明</h2><ol><li><p>现在网站会提供很多种钱包，web3modal可以提供统一的provider，不需要你操心太多东西</p></li><li><p>用ether.js而不是web3.js的原因是简单，爽</p></li></ol><h2 id="h-2" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2.安装</h2><pre data-type="codeBlock" text="npm i web3modal
npm i ethers
//另外还需要安装对应wallet的包，自行搜索就行
"><code><span class="hljs-selector-tag">npm</span> <span class="hljs-selector-tag">i</span> <span class="hljs-selector-tag">web3modal</span>
<span class="hljs-selector-tag">npm</span> <span class="hljs-selector-tag">i</span> <span class="hljs-selector-tag">ethers</span>
<span class="hljs-comment">//另外还需要安装对应wallet的包，自行搜索就行</span>
</code></pre><h2 id="h-3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">3.连接钱包</h2><pre data-type="codeBlock" text="// MM默认就有，无需显式加入
const providerOptions = {
  walletconnect: {
    package: walletconnectProvider,
    options: {
      infuraId: &quot;&quot;,
    },
  },
};
//构建Web3Modal对象
const web3Modal = new Web3Modal({
  //缓存provider
  cacheProvider: true,
  providerOptions,
});

//连接wallet
async function connect() {
  try {
    const web3ModalProvider = await web3Modal.connect();
    provider = new ethers.providers.Web3Provider(web3ModalProvider);
    //注册监听，比如disconnect，accountsChanged，chainChanged
    registerEthListener(web3ModalProvider);
    //主要是处理业务上的需求
    updateCurrentStatus(await provider.listAccounts());
  } catch (error) {
    console.log(error);
  }
}
"><code><span class="hljs-comment">// MM默认就有，无需显式加入</span>
const providerOptions <span class="hljs-operator">=</span> {
  walletconnect: {
    package: walletconnectProvider,
    options: {
      infuraId: <span class="hljs-string">""</span>,
    },
  },
};
<span class="hljs-comment">//构建Web3Modal对象</span>
const web3Modal <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Web3Modal({
  <span class="hljs-comment">//缓存provider</span>
  cacheProvider: <span class="hljs-literal">true</span>,
  providerOptions,
});

<span class="hljs-comment">//连接wallet</span>
async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">connect</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">try</span> {
    const web3ModalProvider <span class="hljs-operator">=</span> await web3Modal.connect();
    provider <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ethers.providers.Web3Provider(web3ModalProvider);
    <span class="hljs-comment">//注册监听，比如disconnect，accountsChanged，chainChanged</span>
    registerEthListener(web3ModalProvider);
    <span class="hljs-comment">//主要是处理业务上的需求</span>
    updateCurrentStatus(await provider.listAccounts());
  } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
    console.log(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
  }
}
</code></pre><h2 id="h-4registerethlistener" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">4.上面的registerEthListener</h2><pre data-type="codeBlock" text="//可以根据自己的需要些具体的内容
function registerEthListener(web3ModalProvider) {
  web3ModalProvider
    .on(&quot;disconnect&quot;, (error) =&gt; {
      
    })
    .on(&quot;accountsChanged&quot;, (accounts) =&gt; {
      
    })
    .on(&quot;chainChanged&quot;, (chainId) =&gt; {
      
    });
}
"><code><span class="hljs-comment">//可以根据自己的需要些具体的内容</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">registerEthListener</span>(<span class="hljs-params">web3ModalProvider</span>) </span>{
  web3ModalProvider
    .on(<span class="hljs-string">"disconnect"</span>, (<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
      
    })
    .on(<span class="hljs-string">"accountsChanged"</span>, (accounts) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
      
    })
    .on(<span class="hljs-string">"chainChanged"</span>, (chainId) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
      
    });
}
</code></pre><h2 id="h-5and" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">5.切换网络&amp;添加网络</h2><pre data-type="codeBlock" text="//你需要的network信息
const networkInfo = {
  localhardhat: {
    //这里一定要16进制，不然报错
    chainId: &quot;0x539&quot;,
    chainName: &quot;LOCALHARDHAT&quot;,
    nativeCurrency: {
      name: &quot;LOCALHARDHAT&quot;,
      symbol: &quot;LH&quot;,
      decimals: 18,
    },
    rpcUrls: [&quot;http://192.168.50.36:8545/&quot;],
    blockExplorerUrls: [&quot;http://192.168.50.36:8545/&quot;],
  },
  matic: {
    chainId: &quot;0x89&quot;,
    chainName: &quot;Ploygon&quot;,
    nativeCurrency: {
      name: &quot;MATIC&quot;,
      symbol: &quot;MATIC&quot;,
      decimals: 18,
    },
    rpcUrls: [&quot;https://rpc-mainnet.maticvigil.com/&quot;],
    blockExplorerUrls: [&quot;https://polygonscan.com/&quot;],
  },
};
//切换网络
//walletProvider是上面的web3ModalProvider，而不是ethers获取的provider
async function switchNetwork(network, walletProvider) {
  try {
    await walletProvider.request({
      method: &quot;wallet_switchEthereumChain&quot;,
      params: [{ chainId: networkInfo[network].chainId }],
    });
  } catch (error) {
    console.log(error);
    if ((error.code = 4902)) {
      return await addNetwork(network, walletProvider);
    }
    throw error;
  }
}
//添加网络
async function addNetwork(network, walletProvider) {
  try {
    return await walletProvider.request({
      method: &quot;wallet_addEthereumChain&quot;,
      params: [networkInfo[network]],
    });
  } catch (error) {
    console.log(error);
  }
}
"><code><span class="hljs-comment">//你需要的network信息</span>
const networkInfo <span class="hljs-operator">=</span> {
  localhardhat: {
    <span class="hljs-comment">//这里一定要16进制，不然报错</span>
    chainId: <span class="hljs-string">"0x539"</span>,
    chainName: <span class="hljs-string">"LOCALHARDHAT"</span>,
    nativeCurrency: {
      name: <span class="hljs-string">"LOCALHARDHAT"</span>,
      symbol: <span class="hljs-string">"LH"</span>,
      decimals: <span class="hljs-number">18</span>,
    },
    rpcUrls: [<span class="hljs-string">"http://192.168.50.36:8545/"</span>],
    blockExplorerUrls: [<span class="hljs-string">"http://192.168.50.36:8545/"</span>],
  },
  matic: {
    chainId: <span class="hljs-string">"0x89"</span>,
    chainName: <span class="hljs-string">"Ploygon"</span>,
    nativeCurrency: {
      name: <span class="hljs-string">"MATIC"</span>,
      symbol: <span class="hljs-string">"MATIC"</span>,
      decimals: <span class="hljs-number">18</span>,
    },
    rpcUrls: [<span class="hljs-string">"https://rpc-mainnet.maticvigil.com/"</span>],
    blockExplorerUrls: [<span class="hljs-string">"https://polygonscan.com/"</span>],
  },
};
<span class="hljs-comment">//切换网络</span>
<span class="hljs-comment">//walletProvider是上面的web3ModalProvider，而不是ethers获取的provider</span>
async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">switchNetwork</span>(<span class="hljs-params">network, walletProvider</span>) </span>{
  <span class="hljs-keyword">try</span> {
    await walletProvider.request({
      method: <span class="hljs-string">"wallet_switchEthereumChain"</span>,
      params: [{ chainId: networkInfo[network].chainId }],
    });
  } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
    console.log(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    <span class="hljs-keyword">if</span> ((<span class="hljs-keyword">error</span>.<span class="hljs-built_in">code</span> <span class="hljs-operator">=</span> <span class="hljs-number">4902</span>)) {
      <span class="hljs-keyword">return</span> await addNetwork(network, walletProvider);
    }
    <span class="hljs-keyword">throw</span> <span class="hljs-function"><span class="hljs-keyword">error</span></span>;
  }
}
<span class="hljs-comment">//添加网络</span>
async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addNetwork</span>(<span class="hljs-params">network, walletProvider</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">return</span> await walletProvider.request({
      method: <span class="hljs-string">"wallet_addEthereumChain"</span>,
      params: [networkInfo[network]],
    });
  } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
    console.log(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
  }
}
</code></pre><h2 id="h-6gas" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">6.预估Gas调用合约写方法</h2><ol><li><p>这里要引入一个number包用于计算</p></li></ol><pre data-type="codeBlock" text="npm i big-number
//引入项目
import BigNumber from &quot;bignumber.js&quot;;
"><code>npm i big-<span class="hljs-built_in">number</span>
<span class="hljs-comment">//引入项目</span>
<span class="hljs-keyword">import</span> <span class="hljs-title class_">BigNumber</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"bignumber.js"</span>;
</code></pre><pre data-type="codeBlock" text="//预估gas,并且调用合约
//functionName:你要调用的方法，如safeMint
//args：调用合约的参数，array类型，例如[arg1, arg2, arg3, {}],最后的{}不能少，函数中会将计算出来的gas要放里面，如果要传入eth，则最后的{}为{value:xxx}
async function executeContractMethosWithEstimatedGas(
  functionName,
  args
) {
//合约信息,provider是连接ethers时的
  const contract=new ethers.Contract(contractAddress,contractAbi,provider);

  const estimatedGas = new BigNumber(
    ethersOf(
      await contract.estimateGasfunctionName
        .then((value) =&gt; {
          const minimumGas = ethers.BigNumber.from(&quot;300000&quot;);
          if (value.lt(minimumGas)) {
            return minimumGas;
          }
          return values;
        })
        .catch((error) =&gt; {
          //出错时给个固定值
          return ethers.BigNumber.from(&quot;700000&quot;);
        })
    )
  );
//将计算结果放入参数中
  const argsForOverridden = args.pop();
  args.gasLimit = parseEthers(estimatedGas.times(1.2).toString());
  args.push(argsForOverridden);
  return contract.connect(getSigner())functionName;
}
//以下是周边函数
function getSigner(){
  if(!provider){
    console.log(&quot;please connect awallet first&quot;);
    return;
  }
  return provider.getSigner();
}

function parseUnits(amount,unit){
  const bnAmount=new BigNumber(amount);
  try {
    return ethers.utils.parseUnits(bnAmount.toFixed(unit),unit);
  } catch (error) {
    return ethers.BigNumber.from(bnAmount.times(Math.pow(10,unit)).toFixed(0));
  }
}
const ETHER_DECIMALS=18;
function parseEthers(amount){
  return parseUnits(amount,ETHER_DECIMALS);
}

function ethersOf(amount){
  return ethers.utils.formatEther(amount);
}
"><code><span class="hljs-comment">//预估gas,并且调用合约</span>
<span class="hljs-comment">//functionName:你要调用的方法，如safeMint</span>
<span class="hljs-comment">//args：调用合约的参数，array类型，例如[arg1, arg2, arg3, {}],最后的{}不能少，函数中会将计算出来的gas要放里面，如果要传入eth，则最后的{}为{value:xxx}</span>
async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">executeContractMethosWithEstimatedGas</span>(<span class="hljs-params">
  functionName,
  args
</span>) </span>{
<span class="hljs-comment">//合约信息,provider是连接ethers时的</span>
  const <span class="hljs-class"><span class="hljs-keyword">contract</span>=<span class="hljs-title"><span class="hljs-keyword">new</span></span> <span class="hljs-title">ethers</span>.<span class="hljs-title">Contract</span>(<span class="hljs-params">contractAddress,contractAbi,provider</span>);

  <span class="hljs-title">const</span> <span class="hljs-title">estimatedGas</span> = <span class="hljs-title"><span class="hljs-keyword">new</span></span> <span class="hljs-title">BigNumber</span>(<span class="hljs-params">
    ethersOf(<span class="hljs-params">
      await <span class="hljs-keyword">contract</span>.estimateGasfunctionName
        .then(<span class="hljs-params">(<span class="hljs-params">value</span>) => {
          const minimumGas = ethers.BigNumber.<span class="hljs-keyword">from</span>(<span class="hljs-params"><span class="hljs-string">"300000"</span></span>);
          <span class="hljs-keyword">if</span> (<span class="hljs-params">value.lt(<span class="hljs-params">minimumGas</span>)</span>) {
            <span class="hljs-keyword">return</span> minimumGas;
          }
          <span class="hljs-keyword">return</span> values;
        }</span>)
        .<span class="hljs-keyword">catch</span>(<span class="hljs-params">(<span class="hljs-params"><span class="hljs-keyword">error</span></span>) => {
          <span class="hljs-comment">//出错时给个固定值</span>
          <span class="hljs-keyword">return</span> ethers.BigNumber.<span class="hljs-keyword">from</span>(<span class="hljs-params"><span class="hljs-string">"700000"</span></span>);
        }</span>)
    </span>)
  </span>);
<span class="hljs-comment">//将计算结果放入参数中</span>
  <span class="hljs-title">const</span> <span class="hljs-title">argsForOverridden</span> = <span class="hljs-title">args</span>.<span class="hljs-title">pop</span>(<span class="hljs-params"></span>);
  <span class="hljs-title">args</span>.<span class="hljs-title">gasLimit</span> = <span class="hljs-title">parseEthers</span>(<span class="hljs-params">estimatedGas.times(<span class="hljs-params"><span class="hljs-number">1.2</span></span>).toString(<span class="hljs-params"></span>)</span>);
  <span class="hljs-title">args</span>.<span class="hljs-title">push</span>(<span class="hljs-params">argsForOverridden</span>);
  <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span>.<span class="hljs-title">connect</span>(<span class="hljs-params">getSigner(<span class="hljs-params"></span>)</span>)<span class="hljs-title">functionName</span>;
}
<span class="hljs-comment">//以下是周边函数</span>
<span class="hljs-title"><span class="hljs-keyword">function</span></span> <span class="hljs-title">getSigner</span>(<span class="hljs-params"></span>)</span>{
  <span class="hljs-keyword">if</span>(<span class="hljs-operator">!</span>provider){
    console.log(<span class="hljs-string">"please connect awallet first"</span>);
    <span class="hljs-keyword">return</span>;
  }
  <span class="hljs-keyword">return</span> provider.getSigner();
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">parseUnits</span>(<span class="hljs-params">amount,unit</span>)</span>{
  const bnAmount<span class="hljs-operator">=</span><span class="hljs-keyword">new</span> BigNumber(amount);
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">return</span> ethers.utils.parseUnits(bnAmount.toFixed(unit),unit);
  } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
    <span class="hljs-keyword">return</span> ethers.BigNumber.from(bnAmount.times(Math.pow(<span class="hljs-number">10</span>,unit)).toFixed(<span class="hljs-number">0</span>));
  }
}
const ETHER_DECIMALS<span class="hljs-operator">=</span><span class="hljs-number">18</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">parseEthers</span>(<span class="hljs-params">amount</span>)</span>{
  <span class="hljs-keyword">return</span> parseUnits(amount,ETHER_DECIMALS);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ethersOf</span>(<span class="hljs-params">amount</span>)</span>{
  <span class="hljs-keyword">return</span> ethers.utils.formatEther(amount);
}
</code></pre><h2 id="h-7" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">7.网络确认</h2><p>说明：一个写操作分为两步。1.提交执行，2.网络确认。类似uni页面操作后会有pending</p><pre data-type="codeBlock" text="//tx是上面executeContractMethosWithEstimatedGas会返回的结果
async function waitForTransaction(tx){
  //2是网络确认数量，自定义
  return await provider.waitForTransaction(tx.hash,2);
}
"><code><span class="hljs-comment">//tx是上面executeContractMethosWithEstimatedGas会返回的结果</span>
async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">waitForTransaction</span>(<span class="hljs-params"><span class="hljs-built_in">tx</span></span>)</span>{
  <span class="hljs-comment">//2是网络确认数量，自定义</span>
  <span class="hljs-keyword">return</span> await provider.waitForTransaction(<span class="hljs-built_in">tx</span>.hash,<span class="hljs-number">2</span>);
}
</code></pre><p>以上就是一些基础操作，还有个读合约，那个别的网站都有，就没贴了</p><p>签名：</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0xE3a463d743F762D538031BAD3f1E748BB41f96ec/S1CbvnSfUIMKk1CkUr9nQDiT_YN1iHMGsL2IlJE3KGw">https://mirror.xyz/0xE3a463d743F762D538031BAD3f1E748BB41f96ec/S1CbvnSfUIMKk1CkUr9nQDiT_YN1iHMGsL2IlJE3KGw</a></p>]]></content:encoded>
            <author>point@newsletter.paragraph.com (point)</author>
        </item>
    </channel>
</rss>