<?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>0xCQHG</title>
        <link>https://paragraph.com/@0xcqhg</link>
        <description>Smart Contract Developer</description>
        <lastBuildDate>Thu, 25 Jun 2026 10:21:26 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>0xCQHG</title>
            <url>https://storage.googleapis.com/papyrus_images/3d1057a87fe524f0f462c8909fbb2e599fa976eeaa2a733ebebc0c5b35bcb165.jpg</url>
            <link>https://paragraph.com/@0xcqhg</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[EVM Opcode MUL 与 MULMOD 的 区别]]></title>
            <link>https://paragraph.com/@0xcqhg/evm-opcode-mul-mulmod</link>
            <guid>b1Rg1vKBwvXU2jjOAg2n</guid>
            <pubDate>Tue, 16 May 2023 08:01:05 GMT</pubDate>
            <description><![CDATA[前置知识：Go、Geth、EVM、Solidity、操作系统首先我们来简单介绍一下标题中的名词：EVM：由GO语言实现的虚拟机，封装在Ethereum的客户端Geth中。Opcode：EVM中字节操作码，是编写Solidity的低级语言。MUL：操作码中的乘法操作。MULMOD：操作码中的乘法取模操作。MUL首先我们来看一下MUL的大致用法，MUL接收两个整数参数，然后再将它们做乘法操作： mul(2,5) = 10； 输出的是一个取模2^256的整数。注意，取模后的位数被限制在了256位以内，也就是如果乘法出现了溢出的情况，高于256位会被截断丢弃，只保留低位的256位。 我们可以使用该示例代码测试一下：Mul在cal函数中，我们使用三个变量来接收MUL计算的结果：result0 = 0x ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff result1 = 0x 0001 ffff ffff ffff ffff ffff ffff ffff ffff ffff fff...]]></description>
            <content:encoded><![CDATA[<blockquote><p>前置知识：Go、Geth、EVM、Solidity、操作系统</p></blockquote><p>首先我们来简单介绍一下标题中的名词：</p><ul><li><p>EVM：由GO语言实现的虚拟机，封装在Ethereum的客户端<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum/go-ethereum">Geth</a>中。</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.evm.codes/?fork=shanghai">Opcode</a>：EVM中字节操作码，是编写Solidity的低级语言。</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.evm.codes/#02?fork=shanghai">MUL</a>：操作码中的乘法操作。</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.evm.codes/#09?fork=shanghai">MULMOD</a>：操作码中的乘法取模操作。</p></li></ul><h2 id="h-mul" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">MUL</h2><p>首先我们来看一下MUL的大致用法，MUL接收两个整数参数，然后再将它们做乘法操作：</p><p>mul(2,5) = 10；</p><p><code>输出的是一个取模2^256的整数</code>。注意，取模后的位数被限制在了256位以内，也就是如果乘法出现了溢出的情况，高于256位会被截断丢弃，只保留低位的256位。</p><p>我们可以使用该示例代码测试一下：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/767c8173c20502605407c3e7f8a701de28f329691d6f1d409e45b653724e6b0e.png" alt="Mul" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Mul</figcaption></figure><p>在cal函数中，我们使用三个变量来接收MUL计算的结果：</p><pre data-type="codeBlock" text="result0 = 0x      ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff

result1 = 0x 0001 ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff fffe

result2 = 0x      ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff fffe 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
"><code><span class="hljs-string">result0</span> <span class="hljs-string">=</span> <span class="hljs-string">0x</span>      <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span>

<span class="hljs-string">result1</span> <span class="hljs-string">=</span> <span class="hljs-string">0x</span> <span class="hljs-number">0001 </span><span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">fffe</span>

<span class="hljs-string">result2</span> <span class="hljs-string">=</span> <span class="hljs-string">0x</span>      <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">fffe</span> <span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0001</span>
</code></pre><p>但由于MUL的结果只保留低位256，所以：</p><pre data-type="codeBlock" text="result0 保持不变

result1 = 0x  ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff fffe

result2 = 0x  0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
"><code><span class="hljs-string">result0</span> <span class="hljs-string">保持不变</span>

<span class="hljs-string">result1</span> <span class="hljs-string">=</span> <span class="hljs-string">0x</span>  <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">ffff</span> <span class="hljs-string">fffe</span>

<span class="hljs-string">result2</span> <span class="hljs-string">=</span> <span class="hljs-string">0x</span>  <span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0000 </span><span class="hljs-number">0001</span>
</code></pre><p>最后的结果：</p><pre data-type="codeBlock" text="result0 = 2^256 - 1
result1 = 2^256 - 2
result2 = 2^256 - (2^256 - 1) = 1
"><code>result0 <span class="hljs-operator">=</span> <span class="hljs-number">2</span><span class="hljs-operator">^</span><span class="hljs-number">256</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>
result1 <span class="hljs-operator">=</span> <span class="hljs-number">2</span><span class="hljs-operator">^</span><span class="hljs-number">256</span> <span class="hljs-operator">-</span> <span class="hljs-number">2</span>
result2 <span class="hljs-operator">=</span> <span class="hljs-number">2</span><span class="hljs-operator">^</span><span class="hljs-number">256</span> <span class="hljs-operator">-</span> (<span class="hljs-number">2</span><span class="hljs-operator">^</span><span class="hljs-number">256</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>) <span class="hljs-operator">=</span> <span class="hljs-number">1</span>
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e10877890afa9e69d2175d8bfa213d69c3cd82c619afcbfd8d775ad727f53983.png" alt="MUL示例" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">MUL示例</figcaption></figure><h2 id="h-mulmod" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">MULMOD</h2><p>介绍完了MUL，我们来看下MULMOD，</p><p>该方法接收三个整数参数，前两个参数做乘法操作，得到结果模上第三个参数：</p><p>mulmod(5,2,3) = 1;</p><p>到目前为止，我们似乎就能发现，MUL与MULMOD的区别就在于MULMOD仅仅是将MUL操作与MOD操作封装在了一个操作码里面。假如使用MUL与MOD似乎也能达到MULMOD的效果，让我们来测试一下。</p><p>首先提供两个函数，分别使用MUL与MOD，MULMOD来进行计算。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fdf4a6ca2b324db51a2988cfb4e8810156567cbc62121e39e1a907086f92fb83.png" alt="示例方法" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">示例方法</figcaption></figure><p>我们分别测试了两组数据，两种函数得到计算结果都相同，并无差异。但是细心的你也许会发现，所有的计算结果均未溢出256位。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/702068b8d7638fd070ea62d4657296df8e05003df3b616cc9e5a8a7715a059be.png" alt="计算" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">计算</figcaption></figure><p>接下来，让我们测试一下256位溢出的情况。</p><p>最后得到的结果大相径庭，可能与很多人预想的不一样。大多数人可能以为EVM支持最高的计算结果位数是256位，但其实不是，最高是512位。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a14a3a0b2a75dbf525268c1508c8a609e348f1ac9f892009da300ff1fe8f4948.png" alt="256位溢出情况" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">256位溢出情况</figcaption></figure><p>在Solidity的汇编操作码中，有两个操作码支持512位以内的运算，分别是<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.evm.codes/#08?fork=shanghai">ADDMOD</a>和<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.evm.codes/#08?fork=shanghai">MULMOD</a>，他们都接收两个最大256位的整数，得到一个512位的数，然后再模上第三个参数，最后得到的始终会是一个256位的数。</p><p>上面的例子中，造成结果不一样的原因是因为<code>mulThenMod</code>会先得到一个256位的数，如果溢出，会先截断高位，再用低位去模上第三个参数。</p><p>而<code>mulMod</code>却不会截断高位。</p><h2 id="h-evm" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">EVM是如何做到这样的差异？</h2><p>想要进一步探究其原理，我们需要查看EVM的源码，也就是<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum/go-ethereum">Geth</a>。</p><p>你可以将代码克隆下来查看：</p><pre data-type="codeBlock" text="git clone https://github.com/ethereum/go-ethereum.git

cd go-ethereum
"><code>git <span class="hljs-built_in">clone</span> https://github.com/ethereum/go-ethereum.git

<span class="hljs-built_in">cd</span> go-ethereum
</code></pre><p>在<code>core/vm/instructions.go</code>这个源码文件中，有所有opcode的源码。</p><p>找到<code>opMulmod</code>，这便是我们在汇编中写的mulmod操作码的源码，</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9523578db6de7dcb8aa8b718078e3d90281257511d9c2697b0d1cd0113e8e97a.png" alt="opMulmod" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">opMulmod</figcaption></figure><p>如果你不想深究EVM的源码，只想了解个大概的底层原理，我们可以直接跳转到<code>z.MulMod()</code>这个函数的源码：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/86763595c606c79621800c63036911ebf573c1029175f404d3d80a666bd3174a.png" alt="MulMod" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">MulMod</figcaption></figure><p>同样，你无需了解全部代码，只需跳转第五行代码<code>umul()</code>函数。</p><p>在下面图片中，我省略了大量计算源码，用cal…代替，如果你对计算过程感兴趣，可以自行查看。</p><p>该函数最后返回一个[8]uint64的数组，该数组的8个uint64整数，组合起来刚好是一个512位的整数。该整数便是操作码<code>mulmod</code>的中间计算结果。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7e3b82fa2eafdb58a041be78e10488e4b57c78f3dfd123791b1493ca87d32ff9.png" alt="umul" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">umul</figcaption></figure><p>最后在<code>z.MulMod()中</code>会用调用<code>udivrem()</code>方法返回一个<code>[4]uint64</code>的数组，该数组在Go的<code>uint256.go</code>中被声明位<code>Int</code>类型，也就是我们在Solidity中使用的<code>uint256</code>。</p><p>到这里，你或许已经知道操作码MUL与MULMOD的区别了。</p><p>如何本文有任何错误，或者你有什么疑问需要咨询，欢迎<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://linktr.ee/cqhg">联系我</a>。</p>]]></content:encoded>
            <author>0xcqhg@newsletter.paragraph.com (0xCQHG)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/f0a2a0afc24f90a6f78ffb50418f616fcb92f421d52cceb6e7099e6fc1cda1b1.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[区块链浏览器智能合约代码查看工具]]></title>
            <link>https://paragraph.com/@0xcqhg/HLwHsR2BgNL2pWDGwfRI</link>
            <guid>HLwHsR2BgNL2pWDGwfRI</guid>
            <pubDate>Mon, 24 Apr 2023 09:34:50 GMT</pubDate>
            <description><![CDATA[前置知识：Smart Contract、Solidity引用该开源工具的一段文本描述：Browsing contracts directly on etherscan sucks! Browsing multi-file contracts on etherscan sucks even more. Limited search, weird syntax highlighting, and many, many more. Finally, it&apos;s often impossible to just git clone repository and browse source code locally because it&apos;s hard to find the exact commit that matches onchain code.在etherscan等浏览器上查看合约的代码是一件十分糟糕的事情，但开源工具**dethcode**带来来全新的体验。 传统查看合约代码的方式：etherscan上的合约代码在这种情况下，查看合约中的代码简直就像地狱，但如果...]]></description>
            <content:encoded><![CDATA[<blockquote><p>前置知识：Smart Contract、Solidity</p></blockquote><p>引用该开源工具的一段文本描述：</p><blockquote><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dethcrypto/dethcode">Browsing contracts directly on etherscan sucks! Browsing multi-file contracts on etherscan sucks even more. Limited search, weird syntax highlighting, and many, many more. Finally, it&apos;s often impossible to just git clone repository and browse source code locally because it&apos;s hard to find the exact commit that matches onchain code.</a></p></blockquote><p>在etherscan等浏览器上查看合约的代码是一件十分糟糕的事情，但开源工具**<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dethcrypto/dethcode">dethcode</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/7e90a26db98cbfaf4c2824df3f042e5f6f3133fe49d0b1cee94eb7de71841601.png" alt="etherscan上的合约代码" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">etherscan上的合约代码</figcaption></figure><p>在这种情况下，查看合约中的代码简直就像地狱，但如果我们将该合约所在的url修改一下，美好的事情将会发生。</p><p>比如该合约地址：</p><pre data-type="codeBlock" text="&quot;https://etherscan.io/token/0x60e4d786628fea6478f785a6d7e704777c86a7c6#code&quot;
"><code><span class="hljs-string">"https://etherscan.io/token/0x60e4d786628fea6478f785a6d7e704777c86a7c6#code"</span>
</code></pre><p>将.io修改成.deth.net</p><pre data-type="codeBlock" text="&quot;https://etherscan.deth.net/token/0x60e4d786628fea6478f785a6d7e704777c86a7c6#code&quot;
"><code><span class="hljs-string">"https://etherscan.deth.net/token/0x60e4d786628fea6478f785a6d7e704777c86a7c6#code"</span>
</code></pre><p>我们就会得到一个vscode风格的代码编辑器页面：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4bc968a53e79e2c532ca4f40178a8706fffe1263dc37a4733dd53529f923293c.png" alt="vscode风格代码页面" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">vscode风格代码页面</figcaption></figure><p>该功能支持以下多个链：</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/">etherscan.io</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ropsten.etherscan.io/">ropsten.etherscan.io</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://rinkeby.etherscan.io/">rinkeby.etherscan.io</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://goerli.etherscan.io/">goerli.etherscan.io</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://kovan.etherscan.io/">kovan.etherscan.io</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bscscan.com/">bscscan.com</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnet.bscscan.com/">testnet.bscscan.com</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hecoinfo.com/">hecoinfo.com</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnet.hecoinfo.com/">testnet.hecoinfo.com</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ftmscan.com/">ftmscan.com</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnet.ftmscan.com/">testnet.ftmscan.com</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://optimistic.etherscan.io/">optimistic.etherscan.io</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://kovan-optimistic.etherscan.io/">kovan-optimistic.etherscan.io</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://polygonscan.com/">polygonscan.com</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnet.polygonscan.com/">testnet.polygonscan.com</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://arbiscan.io/">arbiscan.io</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnet.arbiscan.io/">testnet.arbiscan.io</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://snowtrace.io/">snowtrace.io</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://testnet.snowtrace.io/">testnet.snowtrace.io</a></p></li></ul><p>同时你可以在你的浏览器收藏夹里添加一段这样的js代码：</p><pre data-type="codeBlock" text="javascript: location.href = location.href.replace(/\.\w+(\/)/, &quot;.deth.net/&quot;)
"><code>javascript: location.href <span class="hljs-operator">=</span> location.href.replace(<span class="hljs-operator">/</span>\.\w<span class="hljs-operator">+</span>(\<span class="hljs-operator">/</span>)<span class="hljs-operator">/</span>, <span class="hljs-string">".deth.net/"</span>)
</code></pre><p>之后每当你在区块链浏览器页面想要跳转到deth页面时，点击该收藏便会自动跳转。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c5cada4af0141ab3069c2b2ab7d2cc58968178b449afa6708c770eea171e45f6.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><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dethcrypto/dethcode">快去给该项目star一下吧！</a></p><p>如何本文有任何错误，或者你有什么疑问需要咨询，欢迎<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://linktr.ee/cqhg">联系我</a>。</p>]]></content:encoded>
            <author>0xcqhg@newsletter.paragraph.com (0xCQHG)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/af6bd12b3564eab85a0941a43cec77584d49284965e73c9b876725f34e15725b.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[如何查看智能合约中状态变量的分布]]></title>
            <link>https://paragraph.com/@0xcqhg/bI9pineK2bddVv3PFiGl</link>
            <guid>bI9pineK2bddVv3PFiGl</guid>
            <pubDate>Sun, 23 Apr 2023 07:28:22 GMT</pubDate>
            <description><![CDATA[前置知识：Solidity、Solc、Remix、Ethers.js、Javascript/Typescript 前置条件：Solidity >=0.8.10当我们希望了解EVM智能合约存储分布时，有三种方法可以获得智能合约状态变量的存储信息。1.RemixRemix功能强大，操作简便，我们可以使用Debug功能查看。 首先编写一个测试合约：// SPDX-License-Identifier: MIT pragma solidity >=0.8.10; contract Storage { uint8 a ; constructor() { a = 1; } } Remix页面结构之后我们点击部署按钮，控制台会输出成功部署合约的信息。控制台输出这时我们点击控制台右下角的调试按钮，Remix左侧会弹出合约调试器的界面。调试器页面看到这个页面之后，请用鼠标点击被圈出的按钮，该按钮会直接获得合约最后的运行状态。便于查看合约最后的存储情况。 点击之后让我们下翻调试器页面，会有一个Storage [Completely Loaded]的框体。Storage有了他我们便可以快速查询合约中状...]]></description>
            <content:encoded><![CDATA[<blockquote><p>前置知识：Solidity、Solc、Remix、Ethers.js、Javascript/Typescript</p><p>前置条件：Solidity &gt;=0.8.10</p></blockquote><p>当我们希望了解<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/dashboard/edit/Yij1y3hYHROo55hj9GYw8GXa_DqoHYId36HMAsGdpG8">EVM智能合约存储分布</a>时，有三种方法可以获得智能合约状态变量的存储信息。</p><h3 id="h-1remix" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1.Remix</h3><p>Remix功能强大，操作简便，我们可以使用Debug功能查看。</p><p>首先编写一个测试合约：</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity &gt;=0.8.10;

contract Storage {
    uint8 a ;

    constructor() {
        a = 1;
    }
}
"><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">Storage</span> </span>{
    <span class="hljs-keyword">uint8</span> a ;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        a <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1eb37430698505e0ff7c52cac1184a6e0480535a7a05469f7d98bdfe985c4108.png" alt="Remix页面结构" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Remix页面结构</figcaption></figure><p>之后我们点击<code>部署</code>按钮，控制台会输出成功部署合约的信息。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7982ba11b0246e3b1dbcf6c26646555a6e5a8f554a72fb447cd14f029412c124.png" alt="控制台输出" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">控制台输出</figcaption></figure><p>这时我们点击控制台右下角的<code>调试</code>按钮，Remix左侧会弹出合约调试器的界面。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f45b8cc41f236b2bd8a2109acab4758b54e1adad7048fec28bca5d1fa91916ff.png" alt="调试器页面" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">调试器页面</figcaption></figure><p>看到这个页面之后，请用鼠标点击被圈出的按钮，该按钮会直接获得合约最后的运行状态。便于查看合约最后的存储情况。</p><p>点击之后让我们下翻调试器页面，会有一个<code>Storage [Completely Loaded]</code>的框体。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fc96a48522511ee25d8a49cd4d66200953df3d73f26c54b5d8a30e47fc5b9eb4.png" alt="Storage" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Storage</figcaption></figure><p>有了他我们便可以快速查询合约中状态变量的存储分布情况。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/aae4a7ea32f4eb2f9ee5e07b6b68d553c8cac18357696bb84af3997fcf16b2bf.png" alt="合约状态变量分布" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">合约状态变量分布</figcaption></figure><p>图中的key为变量a的槽位，value为a的值。</p><h3 id="h-2solc" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.Solc</h3><p>另外我们可以通过Solidity编译器查看状态变量的分布。</p><p>首先需要<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/latest/installing-solidity.html#macos-packages">安装Solc</a>（solcjs无法使用该功能，请安装solc）</p><p>安装完成后，进入合约文件所在目录，输入shell命令</p><p><code>solc --storage-layout storage.sol</code></p><p>加上的<code>--storage-layout</code>可以输出合约内变量分布的json信息。</p><pre data-type="codeBlock" text="solc --storage-layout contracts/storage.sol

======= contracts/storage.sol:Storage =======
Contract Storage Layout:
{&quot;storage&quot;:[{&quot;astId&quot;:3,&quot;contract&quot;:&quot;contracts/storage.sol:Storage&quot;,&quot;label&quot;:&quot;a&quot;,&quot;offset&quot;:0,&quot;slot&quot;:&quot;0&quot;,&quot;type&quot;:&quot;t_uint8&quot;}],&quot;types&quot;:{&quot;t_uint8&quot;:{&quot;encoding&quot;:&quot;inplace&quot;,&quot;label&quot;:&quot;uint8&quot;,&quot;numberOfBytes&quot;:&quot;1&quot;}}}
"><code>solc <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-keyword">storage</span><span class="hljs-operator">-</span>layout contracts<span class="hljs-operator">/</span><span class="hljs-keyword">storage</span>.sol

<span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> contracts<span class="hljs-operator">/</span><span class="hljs-keyword">storage</span>.sol:Storage <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span>
Contract Storage Layout:
{<span class="hljs-string">"storage"</span>:[{<span class="hljs-string">"astId"</span>:<span class="hljs-number">3</span>,<span class="hljs-string">"contract"</span>:<span class="hljs-string">"contracts/storage.sol:Storage"</span>,<span class="hljs-string">"label"</span>:<span class="hljs-string">"a"</span>,<span class="hljs-string">"offset"</span>:<span class="hljs-number">0</span>,<span class="hljs-string">"slot"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"type"</span>:<span class="hljs-string">"t_uint8"</span>}],<span class="hljs-string">"types"</span>:{<span class="hljs-string">"t_uint8"</span>:{<span class="hljs-string">"encoding"</span>:<span class="hljs-string">"inplace"</span>,<span class="hljs-string">"label"</span>:<span class="hljs-string">"uint8"</span>,<span class="hljs-string">"numberOfBytes"</span>:<span class="hljs-string">"1"</span>}}}
</code></pre><p>其中的slot便是变量的槽位。</p><h3 id="h-3ethersjs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.Ethers.js</h3><p>使用node.js的ethers.js库也可以查看状态变量的分布，但相对来说比较繁琐。</p><p>首先使用npm/yarn安装ethers.js库</p><p><code>yarn add ethers</code></p><p>编写脚本文件<code>inspect_storage.js</code></p><pre data-type="codeBlock" text="const { ethers } = require(&apos;ethers&apos;);
const provider = new ethers.providers.JsonRpcProvider(&quot;https://mainnet.infura.io/v3/xxxxxxxxxxxx&quot;)

const main = async () =&gt; {
    const slot0 = await provider.getStorageAt(&quot;0xdAC17F958D2ee523a2206206994597C13D831ec7&quot;, 0)
    console.log(`slot0 value: ${slot0}`);
}
main()
"><code>const { ethers } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">'ethers'</span>);
const provider <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ethers.providers.JsonRpcProvider(<span class="hljs-string">"https://mainnet.infura.io/v3/xxxxxxxxxxxx"</span>)

const main <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const slot0 <span class="hljs-operator">=</span> await provider.getStorageAt(<span class="hljs-string">"0xdAC17F958D2ee523a2206206994597C13D831ec7"</span>, <span class="hljs-number">0</span>)
    console.log(`slot0 <span class="hljs-built_in">value</span>: ${slot0}`);
}
main()
</code></pre><p>运行脚本<code>inspect_storage.js</code>输出：</p><pre data-type="codeBlock" text="slot0 value: 0x000000000000000000000000c6cde7c39eb2f0f0095f41570af89efc2c1ea828
"><code><span class="hljs-attr">slot0 value:</span> <span class="hljs-number">0x000000000000000000000000c6cde7c39eb2f0f0095f41570af89efc2c1ea828</span>
</code></pre><p>provider有个getStorageAt方法，第一个参数为已经部署的合约的地址，第二个参数为槽位数。代码中我们查看了该地址的第0x0个槽位，你可以根据自己的需求查看任意地址的任意槽位的值。</p><p>以上便是查看智能合约中状态变量的分布的三种方法。</p><p>如何本文有任何错误，或者你有什么疑问需要咨询，欢迎<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://linktr.ee/cqhg">联系我</a>。</p>]]></content:encoded>
            <author>0xcqhg@newsletter.paragraph.com (0xCQHG)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/0e6cc51e6d59d212a77d22f853e63c232b2b0f004e0796318ae8cd873f9c9354.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[EVM智能合约存储布局]]></title>
            <link>https://paragraph.com/@0xcqhg/evm</link>
            <guid>BIM4m3RtZZ6KeWtEljGP</guid>
            <pubDate>Fri, 21 Apr 2023 10:24:25 GMT</pubDate>
            <description><![CDATA[前置知识：EVM、Smart Contract、Solidity 前置条件：Solidity 0.8.10什么是存储布局（Storage layout）？了解EVM的你应该知道Storage是存储智能合约变量的一段连续空间，合约中状态变量全部存储在这个地方，状态变量有不同的类型，不同的类型存储在Storage中，有着不同规则与分布，便是存储布局（Storage Layout）。了解智能合约存储布局会对开发有什么帮助吗？状态变量不同的排列组合顺序会影响Gas消耗的多少，理解存储分布有助于对Gas优化。开发具有代理模式、钻石模式等功能的合约时需要拥有存储布局的知识。更好的处理和管理合约中的数据，避免数据存储冲突。加强对合约的安全审计，帮助你寻找或避免合约中的安全漏洞。存储（Storage）的结构EVM会为每一个合约分配一个独立的Storage，Storage是一段key-value存储结构，起始地址为0x0，长度为2^256。可以将其理解成为一个key是从0递增到2^256-的map,但实际中并不一定连续。 其中key是状态变量的地址，value是状态变量的值，变量在value上进行...]]></description>
            <content:encoded><![CDATA[<blockquote><p>前置知识：EVM、Smart Contract、Solidity</p><p>前置条件：Solidity 0.8.10</p></blockquote><h2 id="h-storage-layout" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">什么是存储布局（Storage layout）？</h2><p>了解EVM的你应该知道<strong>Storage</strong>是存储智能合约变量的一段连续空间，合约中<strong>状态变量</strong>全部存储在这个地方，<strong>状态变量</strong>有不同的类型，不同的类型存储在<strong>Storage</strong>中，有着不同规则与分布，便是<strong>存储布局</strong>（<strong>Storage Layout</strong>）。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">了解智能合约存储布局会对开发有什么帮助吗？</h2><ul><li><p><strong>状态变量</strong>不同的排列组合顺序会影响Gas消耗的多少，理解存储分布有助于对Gas优化。</p></li><li><p>开发具有代理模式、钻石模式等功能的合约时需要拥有存储布局的知识。</p></li><li><p>更好的处理和管理合约中的数据，避免数据存储冲突。</p></li><li><p>加强对合约的安全审计，帮助你寻找或避免合约中的安全漏洞。</p></li></ul><h2 id="h-storage" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">存储（Storage）的结构</h2><p>EVM会为每一个合约分配一个独立的<strong>Storage</strong>，<strong>Storage</strong>是一段key-value存储结构，起始地址为0x0，长度为2^256。可以将其理解成为一个key是从0递增到2^256-的map,但实际中并不一定连续。</p><p>其中<strong>key</strong>是状态变量的地址，<strong>value</strong>是状态变量的值，变量在<strong>value</strong>上进行读写操作。单个<strong>key-value</strong>称为<strong>槽位</strong>（<strong>Slot</strong>），<strong>槽位</strong>的总数为2^256。</p><p><strong>key</strong>和<strong>value</strong>都是256位，也就是说每个<strong>Slot</strong>最多存储32字节的数据。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/213556f53c368ce7ff9ec0a7e9d7c0cc0caca101f980cd202c0a0b54caeffb1c.png" alt="Storage结构示意图" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Storage结构示意图</figcaption></figure><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">状态变量的存储</h2><p>查看合约中的状态变量存储位置有两种方式。</p><p>1.使用<strong>remix</strong>的<strong>debug</strong>功能，在<strong><em>Storage</em></strong>栏中可以查看槽位信息。</p><p>2.在<strong>ethers.js</strong>中，<strong>Provider</strong>提供了<strong>getStorageAt</strong>方法可以查询已经部署的合约的槽位信息。</p><p>3.通过solc编译添加 --storage-layout 参数可以输出存储信息。</p><h3 id="h-1" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1.布尔类型的存储</h3><p>声明一个简单的合约：</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity &gt;=0.8.10;

contract Storage {
    bool b;

    constructor() {
        b = true;
    }
}
"><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">Storage</span> </span>{
    <span class="hljs-keyword">bool</span> b;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        b <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fda766dfee8becc2f239d8fe8951afb2fbaf542436ac9d07de00734d6f311516.png" alt="布尔的布局" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">布尔的布局</figcaption></figure><p>这里我们通过<strong>remix</strong>查看合约中的存储布局，可以发现<code>bool b</code>在<strong>Storage</strong>中的槽位为：</p><ul><li><p>key:0x0000000000000000000000000000000000000000000000000000000000000000</p></li><li><p>value:0x0000000000000000000000000000000000000000000000000000000000000001</p><p>图中key为变量<code>b</code>的槽位地址，value为变量<code>b</code>的值。</p><p>通过<code>constructor</code>的初始化，<code>bool b</code> 存储在槽位0x0上，并且值为1（<code>true</code>），如果是<code>false</code>则为0，这里的value只占用1字节，剩余的31字节用0补齐。</p></li></ul><h3 id="h-2" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.整型的存储</h3><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity &gt;=0.8.10;

contract Storage {
    uint8 a;
    uint256 b;
    uint16 c;
    // uint32 .....

    constructor() {
        a = 1;
        b = 12;
        c = 123;
    }
}
"><code>// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10<span class="hljs-comment">;</span>

contract Storage {
    uint8 a<span class="hljs-comment">;</span>
    uint256 b<span class="hljs-comment">;</span>
    uint16 c<span class="hljs-comment">;</span>
    // uint32 .....

    constructor() {
        <span class="hljs-attr">a</span> = <span class="hljs-number">1</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">b</span> = <span class="hljs-number">12</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">c</span> = <span class="hljs-number">123</span><span class="hljs-comment">;</span>
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9a5cee17382332d9a6d558b129ae1c69308faf9b7875ea761aad2dc3316a1228.png" alt="整型的存储" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">整型的存储</figcaption></figure><p>不难发现，槽位0x0的value为0x1，槽位0x1的value为0xc，槽位0x2的value为0x7b,对应<code>constructor</code>中初始化的数值。所以a的槽位是0x0，b的槽位是0x1，c的槽位是0x2。即便是只占用一个字节的变量a，也会单独占用一个32字节的槽位。</p><h3 id="h-3" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.定长字节数组类型的存储</h3><p>在<strong>solidity</strong>中，<code>byte1</code><strong>、</strong><code>byte2</code><strong>、…、</strong><code>byte31</code>这些类型被称为定长字节数组，因为他们一旦声明，长度是固定不变的。另外，<code>address</code>本质上是个<code>byte20</code>的定长字节数组，使用 <code>address</code> 类型可以方便地操作地址，<code>address</code> 类型支持许多与地址相关的内置函数，而无需手动处理字节数组。</p><pre data-type="codeBlock" text="contract Storage {
    bytes13 a;
    address b;
    bytes30 c;
    constructor() {
        a = &quot;0xbach&quot;;
        b = address(0xeBF8F38Fc49928A315d9B10889EccE53d58062A9);
        c = &quot;0xrachmaninoff&quot;;
    }
}
"><code>contract Storage {
    bytes13 a<span class="hljs-comment">;</span>
    address b<span class="hljs-comment">;</span>
    bytes30 c<span class="hljs-comment">;</span>
    constructor() {
        <span class="hljs-attr">a</span> = <span class="hljs-string">"0xbach"</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">b</span> = address(<span class="hljs-number">0</span>xeBF8F38Fc49928A315d9B10889EccE53d58062A9)<span class="hljs-comment">;</span>
        <span class="hljs-attr">c</span> = <span class="hljs-string">"0xrachmaninoff"</span><span class="hljs-comment">;</span>
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/dc2118903310db6d7fb0951bb3ce4a49534205878d57b866b78d8e7fdec4620a.png" alt="定长字节数组类型的存储" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">定长字节数组类型的存储</figcaption></figure><p>地址与整形类似，每个变量占用一个槽位，从高位开始存储数据，低位不足的地方用0补齐。</p><h3 id="h-4" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4.变长字节数组类型的存储</h3><p>由于<code>bytes</code>与<code>string</code>的编码与存储方式相同，这里只用<code>string</code>举例说明。</p><p>存储字符串具有以下规则：</p><ul><li><p>如果字符串长度小于等于31个字节，则存储在一个槽位之中，高位存储数据，低位存储字符串长度。</p></li><li><p>如果字符串长度大于等于32个字符，单个槽位32字节无法满足（32字节字符串数据+1字节字符串长度），则将字符串长度<code>length * 2 + 1</code>存入槽位中，数据按顺序存入槽位<code>key = keccak256(abi.encode(slot))</code>与之后的槽位。</p></li></ul><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity &gt;=0.8.10;

contract Storage {
    string a;
    string b;
    string c;
    bytes32 e;

    constructor() {
        a = &apos;1&apos;;
        b = &apos;31byteslong00000000000000000000&apos;;
        c = &apos;32byteslong000000000000000000000&apos;;
        e = keccak256(abi.encode(2));
    }
}
"><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">Storage</span> </span>{
    <span class="hljs-keyword">string</span> a;
    <span class="hljs-keyword">string</span> b;
    <span class="hljs-keyword">string</span> c;
    <span class="hljs-keyword">bytes32</span> e;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        a <span class="hljs-operator">=</span> <span class="hljs-string">'1'</span>;
        b <span class="hljs-operator">=</span> <span class="hljs-string">'31byteslong00000000000000000000'</span>;
        c <span class="hljs-operator">=</span> <span class="hljs-string">'32byteslong000000000000000000000'</span>;
        e <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">2</span>));
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/27dfa4cf0b3d46670650c63623071348fdd2fc6045174dc6f0c25e79dd946c0a.png" alt="变长字节数组类型的存储" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">变长字节数组类型的存储</figcaption></figure><p>变量<code>a</code>占用槽位0x0，value的高位存储UFT-8的字节码，低位存储了字节长度<code>length * 2 = 2</code>，UTF-8中字符1的字节码为0x31，对应图中槽位0x0的value的值。</p><p>变量<code>b</code>占用槽位0x1，由于刚好等于31个字节，所以只占了一个槽位。&quot;333162797465736c6f6e673030303030303030303030303030303030303030 &quot;为数据&quot;31byteslong00000000000000000000&quot;的UTF-8编码。末尾&quot;0x3e&quot;为长度62。</p><p>变量<code>c</code>占用了两个槽位，分别是：</p><ul><li><p>0x2</p></li><li><p>0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace</p></li></ul><p>槽位0x2记录了字符串长度<code>length * 2 + 1 = 0x41（64）</code></p><p>槽位0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace记录了字符串数据UTF-8的字节码。</p><p>变量<code>e</code>使用槽位0x3，展示了编译器是如何计算槽位的。</p><p>再举一个例子：</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity &gt;=0.8.10;

contract Storage {
    string a;

    constructor() {
        a = &apos;65byteslong00000000000000000000065byteslong0000000000000000000000&apos;;
    }
}
"><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">Storage</span> </span>{
    <span class="hljs-keyword">string</span> a;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        a <span class="hljs-operator">=</span> <span class="hljs-string">'65byteslong00000000000000000000065byteslong0000000000000000000000'</span>;
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/858a1879e47d442ce9b96114df3dfefe9ca5b89d33aa1a327104a54e6aa98d92.png" alt="变长字节数组类型的存储" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">变长字节数组类型的存储</figcaption></figure><p>槽位0x0记录了字符串长度<code>length * 2 + 1 = 0x83（131）</code></p><p>数据存储在0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563与0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e565之间的3个槽位，共65个字节。</p><h3 id="h-5" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">5.动态数组类型的存储</h3><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity &gt;=0.8.10;

contract Storage {
    uint256[] a;

    constructor() {
        a.push(1);
        a.push(12);
        a.push(123);
    }
}
"><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">Storage</span> </span>{
    <span class="hljs-keyword">uint256</span>[] a;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        a.<span class="hljs-built_in">push</span>(<span class="hljs-number">1</span>);
        a.<span class="hljs-built_in">push</span>(<span class="hljs-number">12</span>);
        a.<span class="hljs-built_in">push</span>(<span class="hljs-number">123</span>);
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a36cddaa227478eb156a11ab7c19ad16c401aa4c1bfe55c0bba9ac725f1e6ce2.png" alt="数组类型" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">数组类型</figcaption></figure><p><code>array</code>与<code>string</code>的存储方式类似：</p><p>在槽位0x0上存储数组的length = 3；</p><p>在槽位<code>keccak256(abi.encode(a.slot))=0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563</code>处开始存储数据，每一个元素开辟一个新的连续槽位。</p><h3 id="h-6" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">6.映射类型的存储</h3><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity &gt;=0.8.10;

contract Storage {
    mapping (uint256 =&gt; uint256) a;
    bytes32 a1; 
    bytes32 a2; 

    constructor() {
        a[1] = 1;
        a[2] = 22;
        a1 = keccak256(abi.encode(1,0));
        a2 = keccak256(abi.encode(2,0));
    }
}
"><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">Storage</span> </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">uint256</span>) a;
    <span class="hljs-keyword">bytes32</span> a1; 
    <span class="hljs-keyword">bytes32</span> a2; 

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        a[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
        a[<span class="hljs-number">2</span>] <span class="hljs-operator">=</span> <span class="hljs-number">22</span>;
        a1 <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">1</span>,<span class="hljs-number">0</span>));
        a2 <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">2</span>,<span class="hljs-number">0</span>));
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0df4a7620ce87bfb5d6f2effce374cc077b4a2b0948e7bc7cfbfed79e5e7925e.png" alt="映射类型" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">映射类型</figcaption></figure><p>图中并未显示出槽位0点信息，我们使用solc来获取相关信息</p><pre data-type="codeBlock" text="&quot;storage&quot;:[
    {&quot;astId&quot;:5,&quot;contract&quot;:&quot;contracts/storage.sol:Storage&quot;,&quot;label&quot;:&quot;a&quot;,&quot;offset&quot;:0,&quot;slot&quot;:&quot;0&quot;,&quot;type&quot;:&quot;t_mapping(t_uint256,t_uint256)&quot;},
    {&quot;astId&quot;:7,&quot;contract&quot;:&quot;contracts/storage.sol:Storage&quot;,&quot;label&quot;:&quot;a1&quot;,&quot;offset&quot;:0,&quot;slot&quot;:&quot;1&quot;,&quot;type&quot;:&quot;t_bytes32&quot;},
    {&quot;astId&quot;:9,&quot;contract&quot;:&quot;contracts/storage.sol:Storage&quot;,&quot;label&quot;:&quot;a2&quot;,&quot;offset&quot;:0,&quot;slot&quot;:&quot;2&quot;,&quot;type&quot;:&quot;t_bytes32&quot;}
]
"><code><span class="hljs-attr">"storage"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">[</span>
    <span class="hljs-punctuation">{</span><span class="hljs-attr">"astId"</span><span class="hljs-punctuation">:</span><span class="hljs-number">5</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"contract"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"contracts/storage.sol:Storage"</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"label"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"a"</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"offset"</span><span class="hljs-punctuation">:</span><span class="hljs-number">0</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"slot"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"0"</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"t_mapping(t_uint256,t_uint256)"</span><span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-punctuation">{</span><span class="hljs-attr">"astId"</span><span class="hljs-punctuation">:</span><span class="hljs-number">7</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"contract"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"contracts/storage.sol:Storage"</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"label"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"a1"</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"offset"</span><span class="hljs-punctuation">:</span><span class="hljs-number">0</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"slot"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"1"</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"t_bytes32"</span><span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-punctuation">{</span><span class="hljs-attr">"astId"</span><span class="hljs-punctuation">:</span><span class="hljs-number">9</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"contract"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"contracts/storage.sol:Storage"</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"label"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"a2"</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"offset"</span><span class="hljs-punctuation">:</span><span class="hljs-number">0</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"slot"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"2"</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"t_bytes32"</span><span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">]</span>
</code></pre><p>通过solc编译后生成的layout json，我们可以发现<code>map a</code>是占用了槽位0x0的。</p><p>map类型的数据存储有自己独有的机制：</p><p>数据槽位<code>s = keccak256(v . p)</code>，其中v是存储的变量，p是map所在的槽位。</p><p>以<code>constructor</code>中的a[1] = 1;为例：</p><p>v是key 0x1，p是<code>map a</code>的槽位0x0</p><p>代码为：<code>keccak256(abi.encode(1,0))</code>，计算出的槽位是0xada5013122d395ba3c54772283fb069b10426056ef8ca54750cb9bb552a59e7d，槽位对应value为0x1。</p><h3 id="h-7" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">7.结构体类型的存储</h3><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity &gt;=0.8.10;

contract Storage {
    struct S {
        uint256 id;
        string name;
    }
    S s;

    constructor() {
        s = S({id : 1, name : &quot;name&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.10;</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Storage</span> </span>{
    <span class="hljs-keyword">struct</span> <span class="hljs-title">S</span> {
        <span class="hljs-keyword">uint256</span> id;
        <span class="hljs-keyword">string</span> name;
    }
    S s;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        s <span class="hljs-operator">=</span> S({id : <span class="hljs-number">1</span>, name : <span class="hljs-string">"name"</span>});
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1cfb738ecf21b7dfab97e8766b2e17b3b1f3a058021c54ed88196d0fe52c5525.png" alt="结构体类型" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">结构体类型</figcaption></figure><p>根据图中的信息不难推测出，结构体并没有特别的规则。</p><p>了解了以上的状态变量存储规则，似乎并没有可以调优Gas的地方，接下来将介绍编译器的一个变量打包机制。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">状态变量的紧密打包</h2><p>在浏览之前的代码中你也许会想到，uint8明明只需要一个字节就可以存储，但却占用了整个槽位的32个字节。</p><p>Solc确实提供了这个问题的解决方案，就是将变量紧密的打包在一起。</p><p>我们用这个例子介绍一下：</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity &gt;=0.8.10;

contract Storage {
    uint8 a ;
    uint256 b;
    uint8 c;
    string divider;
    uint8 d ;
    uint8 e;
    uint256 f;

    constructor() {
        a = 1;
        b = 2;
        c = 3;
        divider = &quot;divider&quot;;
        d = 4;
        e = 5;
        f = 6;
    }
}
"><code>// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10<span class="hljs-comment">;</span>

contract Storage {
    uint8 a <span class="hljs-comment">;</span>
    uint256 b<span class="hljs-comment">;</span>
    uint8 c<span class="hljs-comment">;</span>
    string divider<span class="hljs-comment">;</span>
    uint8 d <span class="hljs-comment">;</span>
    uint8 e<span class="hljs-comment">;</span>
    uint256 f<span class="hljs-comment">;</span>

    constructor() {
        <span class="hljs-attr">a</span> = <span class="hljs-number">1</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">b</span> = <span class="hljs-number">2</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">c</span> = <span class="hljs-number">3</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">divider</span> = <span class="hljs-string">"divider"</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">d</span> = <span class="hljs-number">4</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">e</span> = <span class="hljs-number">5</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">f</span> = <span class="hljs-number">6</span><span class="hljs-comment">;</span>
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a5e32c33e33c5a582cf32a6cc6e25eebae3e821dd558a021776d597dcb4e0bea.png" alt="状态变量的紧密打包" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">状态变量的紧密打包</figcaption></figure><p>在例子中，变量<code>d e</code>被打包进了同一个槽位0x4。</p><p>如果你按照变量<code>a b c</code> 的编写方式编写合约，那么每一个变量会占用一个槽位，你需要支出最少3个槽位的Gas fee。</p><p>但如果你按照变量d e f的排列方式编写，变量d e会被编译器打包在一个槽位中，这样你只需要支付2个槽位的Gas fee，尽管未来在修改这些被紧密打包的槽位时消耗的Gas会比普通槽位多，但如果修改频率较低时，你或许应该选用紧密打包的方式。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">能够触发紧密打包的状态变量</h3><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity &gt;=0.8.10;

contract Storage {
    struct S {
        uint8 a ;
        bool b;
        address c;
        bytes1 d;
    }   
    uint8 a ;
    bool b;
    address c;
    bytes1 d;
 
    S s;

    constructor() {
        a = 1;
        b = true;
        c = address(0x2681Ca153D2d1F4Df4b335339043B161B5B1f193);
        d = &apos;x&apos;;
        s = S({a: 1, b: true, c: address(0x2681Ca153D2d1F4Df4b335339043B161B5B1f193), d: &apos;x&apos;});
    }
}
"><code>// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10<span class="hljs-comment">;</span>

contract Storage {
    struct S {
        uint8 a <span class="hljs-comment">;</span>
        bool b<span class="hljs-comment">;</span>
        address c<span class="hljs-comment">;</span>
        bytes1 d<span class="hljs-comment">;</span>
    }   
    uint8 a <span class="hljs-comment">;</span>
    bool b<span class="hljs-comment">;</span>
    address c<span class="hljs-comment">;</span>
    bytes1 d<span class="hljs-comment">;</span>
 
    S s<span class="hljs-comment">;</span>

    constructor() {
        <span class="hljs-attr">a</span> = <span class="hljs-number">1</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">b</span> = <span class="hljs-literal">true</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">c</span> = address(<span class="hljs-number">0</span>x2681Ca153D2d1F4Df4b335339043B161B5B1f193)<span class="hljs-comment">;</span>
        <span class="hljs-attr">d</span> = <span class="hljs-string">'x'</span><span class="hljs-comment">;</span>
        <span class="hljs-attr">s</span> = S({a: <span class="hljs-number">1</span>, b: <span class="hljs-literal">true</span>, c: address(<span class="hljs-number">0</span>x2681Ca153D2d1F4Df4b335339043B161B5B1f193), d: <span class="hljs-string">'x'</span>})<span class="hljs-comment">;</span>
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/addd597260a2172072644950ae9374b27610c18f7fed940bdf5a5457d8dbefed.png" alt="紧密打包的状态变量" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">紧密打包的状态变量</figcaption></figure><p>从合约和remix的输出可以推测出，能够触发紧密打包的状态变量类型有整型（uint256除外）、布尔型、定长字节数组/地址类型、结构体型。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">结构体外的变量无法与结构体内的变量被动打包在一起</h3><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity &gt;=0.8.10;

contract Storage {
    struct S {
        uint8 a ;
    }
    uint8 a ;
 
    S s;
    
    constructor() {
        a = 1;
        s = S({a: 1});
    }
}
"><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">Storage</span> </span>{
    <span class="hljs-keyword">struct</span> <span class="hljs-title">S</span> {
        <span class="hljs-keyword">uint8</span> a ;
    }
    <span class="hljs-keyword">uint8</span> a ;
 
    S s;
    
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        a <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
        s <span class="hljs-operator">=</span> S({a: <span class="hljs-number">1</span>});
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2efe408fe14f2a3369c61932dee570ac29bc40d627fe54600db80eccc4179aa7.png" alt="结构体外的变量无法与结构体内的变量被动打包在一起" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">结构体外的变量无法与结构体内的变量被动打包在一起</figcaption></figure><p>如何本文有任何错误，或者你有什么疑问需要咨询，欢迎<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://linktr.ee/cqhg">联系我</a>。</p>]]></content:encoded>
            <author>0xcqhg@newsletter.paragraph.com (0xCQHG)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/246b22ad6acc4bec41e2091050b815e379dfe4596a466a0c1dc88d2cc635b1a9.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>