<?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>rmanzoku</title>
        <link>https://paragraph.com/@rmanzoku</link>
        <description>CTO at [double jump.tokyo](https://www.doublejump.tokyo)</description>
        <lastBuildDate>Wed, 28 Oct 2026 02:19:07 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>rmanzoku</title>
            <url>https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/16bb1fac86c548d7ddc096277bdddb2dc4cf9cf2d2211ec6f67af597b8642e05.png</url>
            <link>https://paragraph.com/@rmanzoku</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Azukiが開発したNFT規格ERC712Aは何を行なっているのか？]]></title>
            <link>https://paragraph.com/@rmanzoku/azuki-nft-erc712a</link>
            <guid>JEdZToZd1jjImETHkhtD</guid>
            <pubDate>Thu, 13 Jan 2022 17:49:16 GMT</pubDate>
            <description><![CDATA[はじめにMetaverseのブランドを標榜し、1月13日に行われたPhase1セールで8700個のNFTが即完売したAzukiプロジェクト。このAzuki NFTは独自のERC721規格であるERC721Aを利用することでセール時のトランザクション手数料を大幅に削減することができました。 https://twitter.com/AzukiZen/status/1479235139167207432 イーサリアムをはじめとしたEVMエコシステムにおけるNFTの代表的な規格であるERC721は、暗号資産のようなFungibleなトークンと比べて、多くのトランザクション手数料（いわゆるGas代）が発生します。 AzukiはERC721の内部構造をプロジェクトに最適化することで、発行時の手数料の少ないERC721互換のNFTを実現しています。本記事ではこのERC721Aについて解説していきます。なお、ソースコードはEtherscan上で公開されているもの、およびGitHubで公開されているものをベースにします。ERC721Aの3つの特徴ERC721Enumerableのインターフェースは...]]></description>
            <content:encoded><![CDATA[<h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">はじめに</h2><p>Metaverseのブランドを標榜し、<strong>1月13日に行われたPhase1セールで8700個のNFTが即完売</strong>したAzukiプロジェクト。このAzuki NFTは独自のERC721規格である<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.azuki.com/erc721a">ERC721A</a>を利用することでセール時のトランザクション手数料を大幅に削減することができました。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/AzukiZen/status/1479235139167207432">https://twitter.com/AzukiZen/status/1479235139167207432</a></p><p>イーサリアムをはじめとしたEVMエコシステムにおけるNFTの代表的な規格であるERC721は、暗号資産のようなFungibleなトークンと比べて、多くのトランザクション手数料（いわゆるGas代）が発生します。</p><p>AzukiはERC721の内部構造をプロジェクトに最適化することで、発行時の手数料の少ないERC721互換のNFTを実現しています。本記事ではこのERC721Aについて解説していきます。なお、ソースコードは<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0xed5af388653567af2f388e6224dc7c4b3241c544#code">Etherscan上で公開されているもの</a>、および<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/chiru-labs/ERC721A/tree/00f50587262a5d415303c36756fb0ef5e217c5ec">GitHubで公開されているもの</a>をベースにします。</p><h2 id="h-erc721a3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">ERC721Aの3つの特徴</h2><ul><li><p>ERC721Enumerableのインターフェースは保持</p></li><li><p>複数のNFTをmintする際の手数料を削減</p></li><li><p>tokenOwnerByIndexのgas削減と注意</p></li></ul><p>ERC721Aの特徴を3つのパートに分けて説明していきます。</p><h2 id="h-erc721enumerable" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">ERC721Enumerableのインターフェースは保持</h2><p>ERC721は、あるトークンから所有するアカウントの情報を持つ規格ではありますが、あるアカウントが所有するトークンの一覧をストレージのデータとして保持しているわけではありません。つまり、自身を含むスマートコントラクト上から、あるアカウントのトークン一覧を参照することができません。</p><p>そこで、<strong>ERC721EnumerableというERC721の拡張規格</strong>が提案されており、OpenZeppelin等にも広く利用されています。ERC721Enumerableでは、トークン一覧参照のためのデータをストレージに保存することで一覧情報を参照できるようにします。したがってシンプルなERC721に比べて、余分にストレージを利用することになるため、ERC721Enumerableを実装する場合は、transferやmintに必要となるgasが増加することになります。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://speakerdeck.com/rmanzoku/enumerate-erc-721">https://speakerdeck.com/rmanzoku/enumerate-erc-721</a></p><p>Enumerableつまり、スマートコントラクトでの一覧表示の必要性の低い一点物のアートNFTや、一部ゲームのようにオフチェーンでの参照を前提とするエコシステムを構築する場合には、Enumebrableをあえて選択しないことで、mintや将来的なtransferのコストを下げる選択が考えられます。</p><p>Azukiは、将来的なオンチェーンエコシステムを鑑み、ERC721Enumerableを外すという選択はしませんでした。後述するように、単純な所有情報すらも発行時のストレージ利用に手を加えているため、<strong>全てのトークンの中から保有しているトークンを走査するというアプローチでERC721Enumerableを実現</strong>しています。</p><h2 id="h-nftmint" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">複数のNFTをmintする際の手数料を削減</h2><p>ERC721Aの目玉機能として、mintつまり新規発行時に複数同時に発行する場合のgasを大幅に削減できることを謳っています。</p><p>まず、スマートコントラクトの記憶領域であるストレージに情報を保存する際にgasを使用します。このgasに対してトランザクション作成時に指定するgas priceを掛け算することでトランザクションの手数料が発生します。</p><p>ERC721の場合、トークンIDに対してオーナー情報をスマートコントラクトのストレージ上に記録するため、トークンIDの数だけストレージを使用しmintやtransferの際にgasを使用します。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/2c7d8319903e8feea1eb6ecb5d1f8e833238b175a04ba19fe5beddce1e2d8dc4.png" alt="ERC721はトークンの数だけストレージを使用する" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">ERC721はトークンの数だけストレージを使用する</figcaption></figure><p>一方、ERC721Aでは複数のNFTのmintかつ連番での発行を前提としているため、その複数個の処理（バッチサイズ/BatchSize）のまとめ、<strong>一番若いトークンIDのみストレージを使用し、バッチサイズ-1のストレージの書き込みを先送りします</strong>。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/07e2535836c482b8555f4a447174310dffd17cf75f09b8955eab6d1478298bf3.png" alt="ERC721Aのmint" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">ERC721Aのmint</figcaption></figure><p>上図の場合、0xAが5つのNFTをmintする際にトークンID 1にのみオーナー情報を記録し、2〜4のIDにオーナー情報は記録しません。<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/chiru-labs/ERC721A/blob/00f50587262a5d415303c36756fb0ef5e217c5ec/ERC721A.sol#L159-L179">オーナー情報を参照する際</a>は、IDが若い順に走査することで、2〜4のトークンのオーナーは1のトークンオーナーと同じ、つまり0xAであることがわかるようになっています。</p><p>ただし、この実装は<strong>所有情報を書き込みの先送り</strong>です。例えばtransferによる譲渡が発生する場合は改めてオーナー情報をストレージに書き込むことになります。また、Azukiが言及しているように<strong>複数のトークンを同時にmintする際にのみ効力を発揮する</strong>ことがわかります。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/7735cd1bb408c02569d6ba5f30da0394dd0e78e01a4f34b465c0496ce52f2869.png" alt="ERC721Aのtransfer" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">ERC721Aのtransfer</figcaption></figure><p>上図では、トークンID 3のトークンを0xCへtransferします。このとき、トークンID 4も同時にストレージに書き込むことで、前述した参照時の実装に整合性を持たせるようになっています。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/1fbc684b3c66e12674bc70bac58ea0b2749caa662c7dfe43b6269f4a724fb9ef.png" alt="ERC721A transfer後のストレージ" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">ERC721A transfer後のストレージ</figcaption></figure><p>トークンID 4を0xAと書き込むことで、トークンID 5の所有情報に整合性がとれるようになっています。これを繰り返して全てのトークンが一度以上transferされたとき、ストレージの状況は標準のERC721と同じ状態になります。</p><p>このようにトークンごと個別にストレージに書き込むことでmintに負担があった従来のERC721に比べて、mint時の負担を極力減らすことを実現しており、ERC721Aの最大の特徴といえるでしょう。</p><h2 id="h-tokenofownerbyindexgas" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">tokenOfOwnerByIndexのgas削減と注意</h2><p>前述したとおり、ERC721AではERC721Enumerableを実装しているため、スマートコントラクトから所有するトークン一覧を取得することができます。通常であればアカウントごとのトークン所有情報をストレージに追加保存し、tokenOfOwnerByIndex関数を実行することで一覧を取得します。</p><p>ERC721Aでは、ERC721Enumerableで発生する追加情報を保存せずにtokenOfOwnerByIndex実行時に<strong>全ての発行済みトークンを走査する</strong>ことで実現しています。これにより、Enemerableを実装しながらもmintおよびtransfer時のgasを大幅に削減することができます。</p><p>一方で、tokenOfOwnerByIndexが全ての発行済みトークンを走査するため、この関数を<strong>トランザクションの一部で実行した場合に、発行済みトークンの数だけ処理が実行される</strong>ことになります。この処理は、ストレージの更新を伴わないため、1つ1つは大きなgasとなりませんが、膨大な数のトークンがある場合には注意が必要です。</p><p>Azukiのコントラクトには、10000以上のトークンがある場合は注意をする旨がコメントされています。</p><pre data-type="codeBlock" text="    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     * This read function is O(collectionSize). If calling from a separate contract, be sure to test gas first.
     * It may also degrade with extremely large collection sizes (e.g &gt;&gt; 10000), test for your use case.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index)
        public
        view
        override
        returns (uint256)
    {
        require(index &lt; balanceOf(owner), &quot;ERC721A: owner index out of bounds&quot;);
        uint256 numMintedSoFar = totalSupply();
        uint256 tokenIdsIdx = 0;
        address currOwnershipAddr = address(0);
        for (uint256 i = 0; i &lt; numMintedSoFar; i++) {
            TokenOwnership memory ownership = _ownerships[i];
            if (ownership.addr != address(0)) {
                currOwnershipAddr = ownership.addr;
            }
            if (currOwnershipAddr == owner) {
                if (tokenIdsIdx == index) {
                    return i;
                }
                tokenIdsIdx++;
            }
        }
        revert(&quot;ERC721A: unable to get token of owner by index&quot;);
    }
"><code>    <span class="hljs-comment">/**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     * This read function is O(collectionSize). If calling from a separate contract, be sure to test gas first.
     * It may also degrade with extremely large collection sizes (e.g >> 10000), test for your use case.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tokenOfOwnerByIndex</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> owner, <span class="hljs-keyword">uint256</span> index</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">override</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-built_in">require</span>(index <span class="hljs-operator">&#x3C;</span> balanceOf(owner), <span class="hljs-string">"ERC721A: owner index out of bounds"</span>);
        <span class="hljs-keyword">uint256</span> numMintedSoFar <span class="hljs-operator">=</span> totalSupply();
        <span class="hljs-keyword">uint256</span> tokenIdsIdx <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
        <span class="hljs-keyword">address</span> currOwnershipAddr <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</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> numMintedSoFar; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            TokenOwnership <span class="hljs-keyword">memory</span> ownership <span class="hljs-operator">=</span> _ownerships[i];
            <span class="hljs-keyword">if</span> (ownership.addr <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>)) {
                currOwnershipAddr <span class="hljs-operator">=</span> ownership.addr;
            }
            <span class="hljs-keyword">if</span> (currOwnershipAddr <span class="hljs-operator">=</span><span class="hljs-operator">=</span> owner) {
                <span class="hljs-keyword">if</span> (tokenIdsIdx <span class="hljs-operator">=</span><span class="hljs-operator">=</span> index) {
                    <span class="hljs-keyword">return</span> i;
                }
                tokenIdsIdx<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;
            }
        }
        <span class="hljs-keyword">revert</span>(<span class="hljs-string">"ERC721A: unable to get token of owner by index"</span>);
    }
</code></pre><h2 id="h-erc721a" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">ERC721Aはどういったケースに使えるのか？</h2><p>Azuki NFTをベースにERC721Aの解説を行いました。この規格はどのようなケースに使えるのでしょうか？</p><p>AzukiのTwitterの投稿やウェブサイトを見ると、このプロジェクトのモチベーションの一つに、<strong>NBA TopShotやポケモンカードパック開封の楽しみ</strong>があることがわかります。</p><p><strong>トークンを購入する、後日パックを開封する、唯一の存在であるNFTに出会う</strong>、そのような体験を前提とした場合、複数個を購入することはある種当然の思想になります。</p><p>本記事で記述したとおり、<strong>ERC721Aはトランザクション手数料を下げる銀の弾丸のような実装であるわけではなく、プロジェクトの特性に合わせてERC721という規格のgas利用タイミングを最適化した</strong>ものです。</p><p>イーサリアムの手数料の高騰が叫ばれて1年以上立ちますが、NFTを含めイーサリアム上のプロジェクトはまだまだ勢いを持っています。今後もERC規格を満たす特殊な実装が増えてイノベーションが起こっていくのでしょう！</p>]]></content:encoded>
            <author>rmanzoku@newsletter.paragraph.com (rmanzoku)</author>
            <enclosure url="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/d0916900e31dc0d2e9e01beba998acf7d554a7813f91974dda0dcb465651bdf1.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>