こんにちは。大和総研デジタルソリューション研究開発部の桑木です。
大和総研ではデジタル証券であるセキュリティ・トークン(ST:Security Token)を取り扱うためのウォレット(注1)と呼ばれるシステムの開発、NFTに関する書籍の執筆(注2)等、ブロックチェーン分野の取り組みを長年進めています。また、Ethereum等のパブリックブロックチェーンを活用したWeb3の広がりを見据え、部署横断でWeb3分野の研究開発を行う専門のプロジェクトを発足させ、ブロックチェーン・Web3に関する取り組みを強化しています。
今回はパブリックブロックチェーンのEthereumの改善提案であるEIP(Ethereum Improvement Proposals)の1つの「EIP-7702」で提案された仕組みを使ってトランザクション手数料のスポンサーシップ(手数料の支払いを他者に支払ってもらうこと)を試してみましたので、その方法について解説します。EIP-7702はトランザクション手数料を柔軟に扱えるようにし、Web3ウォレットのUX(ユーザ体験:User Experience)を向上させる仕組みとして注目を集めています。
1. EIP-7702とは
EIP-7702はEthereumの機能やプロセスの改善に関する提案が書かれたドキュメントであるEIP(Ethereum Improvement Proposal)の1つで、2024年5月にEthereumの考案者であるVitalik Buterin氏らによって提案されました(注3)。EIP-7702はコミュニティ内での議論を経て2025年5月7日にEthereumで行われたアップグレード(Pectraアップグレードと呼ばれる)で正式に実装されました(注4)。
EIP-7702は主にUXの向上を念頭に置いています。Ethereumに新しいトランザクションタイプを導入することで、EOA(Externally Owned Account:ブロックチェーンで「アカウント」や「ウォレット」と一般に呼ばれるもの)がスマートコントラクトのコードをロードできるようになり、一時的にスマートコントラクトのように振る舞うことが可能になります。これにより、以下の3つが実現可能になります(表1)。
実現できること |
概要 |
---|---|
トランザクション手数料のスポンサーシップ | 例えば、Aさんが保有するNFTをBさんに移転したい時はAさんがトランザクションを送信する必要があります。その際、Aさんは暗号資産でトランザクション手数料を支払う必要があり、NFTのみを取引したい場合でもトランザクション手数料の支払いのために暗号資産を保有しておく必要があります。 EIP-7702の仕組みを活用することで、Cさんの承認があれば、AさんはCさんにトランザクション手数料を代わりに支払ってもらうことが可能になります。これにより、Aさん自身は暗号資産を保有していなくてもNFTの移転ができるようになります。また、Cさんはトランザクション手数料相当額に支払代行手数料を上乗せしてAさんに法定通貨建てで請求するなどして収益を得ることが可能になります。 |
バッチ処理 | 同じユーザからの複数の操作を1つにまとめて実行できるようになります。例えば従来は別々のトランザクションで実行していたERC-20トークンのapproveとtransferFromを1つにまとめて実行することで「すべて成功」か「すべて失敗」というシンプルな状態を実現でき、状態管理が容易になります。 |
権限の段階的縮小 | 例えば、 ・特定の暗号資産は扱えるが別の暗号資産は扱えない ・1日あたり合計残高の最大10%まで暗号資産を使用できる ・特定のアカウントとは暗号資産をやり取りできない など、アカウントが持つ機能を細かく制御できるようになります。 |
出所:大和総研作成
EthereumのコミュニティではAccount Abstraction(アカウント抽象化)と呼ばれる、アカウントの機能を柔軟にしてUXを改善するための仕組みの実現方法に関する議論が続けられています。EIP-7702以外にも同様のUX改善のためのEIPはありましたが、今後本格的にAccount Abstractionを実現していくための重要なステップとしてEIP-7702が採用されました。
2. EIP-7702を使ったトランザクション手数料のスポンサーシップ
ここからはEIP-7702を使ったトランザクション手数料(ガス代とも呼ばれる)のスポンサーシップ(肩代わり)を試してみます。今回は以下のような構成で、Ethereumのテストネット(テスト用のネットワーク)であるSepoliaを使って、SenderのEOAからReceiverのEOAにERC-20トークンを移転するためのトランザクションを送信します(図1)。
Ethereumではトランザクションを実行するにはトランザクション手数料をEther(略称:ETH。以降、ETHと表記します。)で支払う必要があります。テストネットでも同様であり、テストネットで発行されているETHで支払う必要がありますが、今回はEIP-7702の仕組みを使い本来Senderが支払うべきトランザクション手数料をSponsorに代わりに支払ってもらいます。

ERC-20トークンは、テスト用にSepoliaで発行されているステーブルコインのUSDCを使用します。下記のURLから無料で入手可能です。Sponsorには0.1 ETH、Senderには10 USDCを初期の残高として持たせておきます。SenderにはETHを保有させません。
テスト用USDCの配布サイト
https://faucet.circle.com/
各登場人物の資産(ETH、ERC-20)の初期保有量とトランザクションの実行による残高の増減は以下のようになります(表2)。
登場人物 |
資産の種類 |
初期保有量 |
残高の増減 |
残高の増減の理由 |
---|---|---|---|---|
Sender | ETH | 0 ETH | 変化なし | - |
ERC-20 | 10 USDC | 減少(-1) | ERC-20の移転のため | |
Receiver | ETH | 0 ETH | 変化なし | - |
ERC-20 | 0 USDC | 増加(+1) | ERC-20の移転のため | |
Sponsor | ETH | 0.1 ETH | 減少(減少量は実行時のネットワークの状況に応じて変動) | トランザクション手数料の支払いのため |
出所:大和総研作成
2.1 トランザクションの概要
トランザクション手数料のスポンサーシップはdelegation という仕組みを使って実現できます。まず、SponsorはSenderのEOAにトランザクションを送信します。するとSenderのEOAは一時的にスマートコントラクト(ここではDelegate Contractと呼ぶことにします)をロードします。そして、Delegate ContractをロードしたSenderのEOAはERC-20トークンのスマートコントラクトを実行し、SenderからReceiverにトークンを移転します(図2)。

EIP-7702に対応したトランザクションの構成は以下のようになります。EIP-7702では従来のトランザクションに加えてauthorization_listというデータを含める必要があるのがポイントです。authorization_listを使うことで、指定したEOAが指定したDelegate Contractをロードできるようになります。

したがって、Sponsorが送信するトランザクションの構成を図示すると大まかに以下のようになります(図4)。

2.2 Delegation Contractの作成
Delegate Contract用に以下のようなスマートコントラクトをデプロイしました(アドレス:0xebe707c39CFdA896376D618369bd80973318Fe8b)。解説用にロジックをシンプルにしており、toで指定したスマートコントラクトをdataの内容で実行するだけのスマートコントラクトです。処理の内容を確認しやすくするために2種類のeventを用意していますがなくても問題ありません。
※検証用なのでセキュリティ面での検討を行っていません。本番環境ではそのまま使用しないようにしてください。
pragma solidity ^0.8.0; contract EIP7702Delegation { struct Call { address to; uint256 value; bytes data; } event CallExecuted( address indexed sender, address indexed to, uint256 value, bytes data ); event Executed(address value); function execute(Call calldata call) external payable { (bool success, ) = call.to.call{value: call.value}(call.data); require(success, "reverted"); emit Executed(address(this)); emit CallExecuted(msg.sender, call.to, call.value, call.data); } }
2.3 トランザクションの作成のためのソースコード
ソースコード(Typescript)の全文を以下に掲載します。トランザクションの作成や送信のためにethers.jsというライブラリ(バージョン6.13.7)を使用しています(注5)。次節以降でポイントを解説します。
import { ethers } from "ethers"; //======================================================= // パラメータの準備 //======================================================= // SponserのEOAの秘密鍵とアドレス const SPONSOR_ADDRESS: string = "SponcerのEOAのアドレス"; const SPONSOR_KEY: string = "Sponcerの秘密鍵"; const sponsorKey = new ethers.SigningKey(SPONSOR_KEY); // SenderのEOAの秘密鍵とアドレス const SENDER_ADDRESS: string = "SenderのEOAのアドレス"; const SENDER_KEY: string = "Senderの秘密鍵"; const senderKey = new ethers.SigningKey(SENDER_KEY); // ReceiverのEOAのアドレス const RECEIVER_ADDRESS: string = "ReceiverのEOAのアドレス"; // SepoliaのChain ID const chainId = 11155111; // Delegate Contractのアドレス const DELEGATE_CONTRACT_ADDRESS = "0xebe707c39CFdA896376D618369bd80973318Fe8b"; // ERC-20トークンのスマートコントラクトのアドレス(SepoliaのUSDCを使用) const ERC20_CONTRACT_ADDRESS = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"; // ノードへの接続情報 const provider = new ethers.JsonRpcProvider("Ethereum SepoliaノードのURL", { name: "sepolia", chainId: chainId, }); const main = async () => { //======================================================= // Authorization Listの作成 //======================================================= const senderNonce = await provider.getTransactionCount(SENDER_ADDRESS); // MAGIC(固定値)とethers.encodeRlp([chainId, Delegate Contractのアドレス, Sender のNonce])を連結 const MAGIC = "0x05"; const authContent = ethers.encodeRlp([ ethers.stripZerosLeft(ethers.toBeHex(chainId)), DELEGATE_CONTRACT_ADDRESS, ethers.stripZerosLeft(ethers.toBeHex(senderNonce)), ]); // Hashを計算しSenderの秘密鍵で署名 const authHash = ethers.keccak256(ethers.concat([MAGIC, authContent])); const senderSignature = senderKey.sign(authHash); // [chain_id, address, nonce, y_parity, r, s]の順に整列 const senderAuthorizationList = [ ethers.stripZerosLeft(ethers.toBeHex(chainId)), DELEGATE_CONTRACT_ADDRESS, ethers.stripZerosLeft(ethers.toBeHex(senderNonce)), ethers.stripZerosLeft(ethers.toBeHex(senderSignature.yParity)), senderSignature.r, senderSignature.s, ]; //======================================================= // ERC-20トークンのtransfer用のデータの作成 //======================================================= // ERC-20トークンのtransferを呼び出すためのインターフェース const erc20Interface = new ethers.Interface([ "function transfer(address to, uint256 amount) external returns (bool)", ]); // ERC-20トークンのtransfer用のデータの作成 const transferData = erc20Interface.encodeFunctionData("transfer", [ RECEIVER_ADDRESS, // ERC20トークンの送信先EOAのアドレス ethers.parseUnits("1", 6), // ERC20トークンの送信量 ]); //======================================================= // Delegate Contractのexecuteを呼び出すためのデータの作成 //======================================================= // Delegate Contractのexecuteを呼び出すためのインターフェース const delegationInterface = new ethers.Interface([ "function execute((address to, uint256 value, bytes data) calldata calls) external payable", ]); const internalTransferCall = { to: ERC20_CONTRACT_ADDRESS, // ERC20トークンのスマートコントラクトのアドレス value: ethers.parseEther("0"), // ETHは送らないので0を指定 data: transferData, // ERC-20トークンのtransferを呼び出すためのデータ }; // Delegate Contractのexecuteを呼び出す際に渡すデータ const executeCallData = delegationInterface.encodeFunctionData("execute", [ internalTransferCall, ]); //======================================================= // EIP-7702のトランザクションを作成 //======================================================= const sponsorNonce = await provider.getTransactionCount(SPONSOR_ADDRESS); const feeData = await provider.getFeeData(); const unsignedTx: ethers.RlpStructuredDataish = [ ethers.toBeHex(chainId), ethers.stripZerosLeft(ethers.toBeHex(sponsorNonce)), // SponsorのEOAのnonce ethers.stripZerosLeft(ethers.toBeHex(feeData.maxPriorityFeePerGas!)), // maxPriorityFeePerGas ethers.stripZerosLeft(ethers.toBeHex(feeData.maxFeePerGas!)), // maxFeePerGas ethers.stripZerosLeft(ethers.toBeHex(1000000)), // gasLimit SENDER_ADDRESS, // SenderのEOAのアドレス ethers.stripZerosLeft(ethers.toBeHex(0)), // value: ETHは送らないので0を指定 executeCallData, // Delegate Contractのexecuteに渡すcalldata [], // Access List [senderAuthorizationList], // Authorization List ]; // トランザクションタイプ(EIP-7702は4) const txType = "0x04"; // トランザクションタイプとRLPエンコードした未署名トランザクションを連結 const unsignedRLP = ethers.encodeRlp(unsignedTx); const unsignedSerializedTx = ethers.concat([txType, unsignedRLP]); // Hashを計算しSponsorの秘密鍵で署名 const txHash = ethers.keccak256(unsignedSerializedTx); const sponsorSignature = sponsorKey.sign(txHash); // Sponsorの署名を未署名トランザクションの末尾に追加 const signedTx = unsignedTx.concat([ ethers.stripZerosLeft(ethers.toBeHex(sponsorSignature.yParity)), sponsorSignature.r, sponsorSignature.s, ]); // RLPエンコードしてトランザクションタイプを付与したトランザクションを生成 const signedRLP = ethers.encodeRlp(signedTx); const rawTx = ethers.concat([txType, signedRLP]); console.log("Raw EIP 7702 Transaction:", rawTx); // ethersの6.13.7はまだEIP-7702に対応していないので // ethersのsendメソッドを使用してEIP-7702のトランザクションを送信 const tx = await provider.send("eth_sendRawTransaction", [rawTx]); console.log("Transaction Hash:", tx); }; main();
2.4 authorization_listの作成
まず、authorization_listを作成します。下図の赤枠の部分に該当し、authorization_listで指定したEOAが、指定したスマートコントラクトをロードできるようになります(図5)。ここでは、SenderのEOAがDelegate Contractをロードできるようにしており、MAGIC という固定値を連結したうえでSenderの秘密鍵で署名する必要があります。MAGICの値については、下記サイトの「Parameters」の場所に記載があります。
https://eips.ethereum.org/EIPS/eip-7702#parameters

//======================================================= // Authorization Listの作成 //======================================================= const senderNonce = await provider.getTransactionCount(SENDER_ADDRESS); // MAGIC(固定値)とethers.encodeRlp([chainId, Delegate Contractのアドレス, SenderのNonce])を連結 const MAGIC = "0x05"; const authContent = ethers.encodeRlp([ ethers.stripZerosLeft(ethers.toBeHex(chainId)), DELEGATE_CONTRACT_ADDRESS, ethers.stripZerosLeft(ethers.toBeHex(senderNonce)), ]); // Hashを計算しSenderの秘密鍵で署名 const authHash = ethers.keccak256(ethers.concat([MAGIC, authContent])); const senderSignature = senderKey.sign(authHash); // [chain_id, address, nonce, y_parity, r, s]の順に整列 const senderAuthorizationList = [ ethers.stripZerosLeft(ethers.toBeHex(chainId)), DELEGATE_CONTRACT_ADDRESS, ethers.stripZerosLeft(ethers.toBeHex(senderNonce)), ethers.stripZerosLeft(ethers.toBeHex(senderSignature.yParity)), senderSignature.r, senderSignature.s, ];
2.5 ERC-20のtransfer用のデータの作成
次にDelegate Contractに実行させるERC-20トークンのtransferのためのデータを作成します。下図の赤枠の部分に該当します(図6)。

//======================================================= // ERC-20トークンのtransfer用のデータの作成 //======================================================= // ERC-20トークンのtransferを呼び出すためのインターフェース const erc20Interface = new ethers.Interface([ "function transfer(address to, uint256 amount) external returns (bool)", ]); // ERC-20トークンのtransfer用のデータの作成 const transferData = erc20Interface.encodeFunctionData("transfer", [ RECEIVER_ADDRESS, // ERC20トークンの送信先EOAのアドレス ethers.parseUnits("1", 6), // ERC20トークンの送信量 ]);
2.6 Delegate Contractのexecuteを呼び出すためのデータの作成
次に、Delegate Contractのexecuteを呼び出すためのデータを作成します。先ほど作成したERC-20トークンのtransferを呼び出すためのデータを内包させます。下図の赤枠の部分に該当します(図7)。

//======================================================= // Delegate Contractのexecuteを呼び出すためのデータの作成 //======================================================= // Delegate Contractのexecuteを呼び出すためのインターフェース const delegationInterface = new ethers.Interface([ "function execute((address to, uint256 value, bytes data) calldata calls) external payable", ]); const internalTransferCall = { to: ERC20_CONTRACT_ADDRESS, // ERC20トークンのスマートコントラクトのアドレス value: ethers.parseEther("0"), // ETHは送らないので0を指定 data: transferData, // ERC-20トークンのtransferを呼び出すためのデータ }; // Delegate Contractのexecuteを呼び出す際に渡すデータ const executeCallData = delegationInterface.encodeFunctionData("execute", [ internalTransferCall, ]);
2.7 EIP-7702のトランザクションの作成
最後に、これまで作成したデータを使ってEIP-7702のトランザクションを組み立てます(図8)。トランザクションにはSponsorの署名が必要です。本記事の執筆時点でethersがEIP-7702の署名に対応していないため、トランザクションタイプ(EIP-7702は4)を付与したうえで署名し、手動でEIP-7702に対応したトランザクションを組み立てています。

//======================================================= // EIP-7702のトランザクションを作成 //======================================================= const sponsorNonce = await provider.getTransactionCount(SPONSOR_ADDRESS); const feeData = await provider.getFeeData(); const unsignedTx: ethers.RlpStructuredDataish = [ ethers.toBeHex(chainId), ethers.stripZerosLeft(ethers.toBeHex(sponsorNonce)), // SponsorのEOAのnonce ethers.stripZerosLeft(ethers.toBeHex(feeData.maxPriorityFeePerGas!)), // maxPriorityFeePerGas ethers.stripZerosLeft(ethers.toBeHex(feeData.maxFeePerGas!)), // maxFeePerGas ethers.stripZerosLeft(ethers.toBeHex(1000000)), // gasLimit SENDER_ADDRESS, // SenderのEOAのアドレス ethers.stripZerosLeft(ethers.toBeHex(0)), // value: ETHは送らないので0を指定 executeCallData, // Delegate Contractのexecuteに渡すcalldata [], // Access List [senderAuthorizationList], // Authorization List ]; // トランザクションタイプ(EIP-7702は4) const txType = "0x04"; // トランザクションタイプとRLPエンコードした未署名トランザクションを連結 const unsignedRLP = ethers.encodeRlp(unsignedTx); const unsignedSerializedTx = ethers.concat([txType, unsignedRLP]); // Hashを計算しSponsorの秘密鍵で署名 const txHash = ethers.keccak256(unsignedSerializedTx); const sponsorSignature = sponsorKey.sign(txHash); // Sponsorの署名を未署名トランザクションの末尾に追加 const signedTx = unsignedTx.concat([ ethers.stripZerosLeft(ethers.toBeHex(sponsorSignature.yParity)), sponsorSignature.r, sponsorSignature.s, ]); // RLPエンコードしてトランザクションタイプを付与したトランザクションを生成 const signedRLP = ethers.encodeRlp(signedTx); const rawTx = ethers.concat([txType, signedRLP]); console.log("Raw EIP 7702 Transaction:", rawTx); // ethersの6.13.7はまだEIP-7702に対応していないので // ethersのsendメソッドを使用してEIP-7702のトランザクションを送信 const tx = await provider.send("eth_sendRawTransaction", [rawTx]); console.log("Transaction Hash:", tx);
2.8 トランザクションの確認
今回送信したトランザクションのハッシュは、0x6f8a54706c8d7b073e05bca95b77614aef620595f72b732d0669feab9f4b9181となりました。
Ethereum(Sepoliaを含む)のトランザクションや暗号資産、NFTなどの残高を確認することができるEtherscanというサイトを使って、実際に送信したEIP-7702のトランザクションの内容やトランザクション送信前後でのERC-20トークンの残高の変動を確認してみます。
Sepolia向けEtherscan
https://sepolia.etherscan.io/
関連するURLは下記の通りです。
EIP-7702のトランザクション:
https://sepolia.etherscan.io/tx/0x6f8a54706c8d7b073e05bca95b77614aef620595f72b732d0669feab9f4b9181SenderのEOA:
https://sepolia.etherscan.io/address/0x0508eA9488A4FeC33d16310Bb16552d4be9AA73fSponsorのEOA:
https://sepolia.etherscan.io/address/0xcF3F0CbBc251345d1Cbbde4ba587c46fa92399C3
2.8.1 トランザクション送信前の残高の確認
まず、SenderのEOAの残高を確認します。左上のOverviewからETHを0、USDCを10保有していることが分かります(図9)。

次にSponsorのEOAの残高を確認します。左上のOverviewからETHを0.1保有していることが分かります。Transactionsには他のEOAから0.1 ETHを受け取ったトランザクションが表示されています(図10)。

2.8.2 トランザクション送信後の残高の確認
Senderのトランザクション送信後の残高です。左上のOverviewからUSDCが9になっていることが分かります。また、TransactionsからERC-20トークンのTransferが実行されたことが分かります(図11)。

Sponsorのトランザクション送信後の残高です。左上のOverviewからETHが減少していることが分かります。また、TransactionsからTransferが実行され手数料が支払われたことが分かります(図12)。

EIP-7702のトランザクションの詳細です。「Txn Type: 4 (EIP-7702)」となっており、EIP-7702のトランザクションとして処理されていることが分かります。トランザクションそのものはSponsorからSenderへのトランザクションとして表示されています(図13)。

EIP-7702のトランザクション特有の項目として「Authorization List」が表示されています。SenderがDelegate Contractをロードしたということが表示されています(図14)。

最後に「Logs」を確認します。トランザクション実行時に発生したeventが確認できます。Eventは3つあり、下記のようなeventとなっています(表3、図15)。
event名 |
内容 |
---|---|
Transfer | ERC-20トークンのスマートコントラクトのeventが表示されており、SenderのEOAからReceiverのEOAに1000000(=1 USDC)移転されたことが分かります。 |
Executed | Delegate Contractのeventが表示されています。スマートコントラクト内ではaddress(this)としていました。Delegate Contract そのもののアドレスではなくSenderのEOAのアドレスが表示されているため、SenderのEOAがDelegate Contractをロードして実行していることが分かります。 |
CallExecuted | Delegate Contractのeventが表示されています。スマートコントラクト内ではCallExecuted(msg.sender, call.to, call.value, call.data) としていました。msg.senderがSponsorのEOAのアドレス、call.toがERC-20トークンのスマートコントラクトのアドレスとなっていることが分かります。 |
出所:大和総研作成

3. まとめ
本記事では、EIP-7702の仕組みを解説し、実際にトランザクションを送信しました。従来は、例えばパブリックブロックチェーン上でステーブルコインやセキュリティ・トークン、NFTなどの暗号資産以外の資産のみを扱いたい場合でも、それを他の人に移転する際などはトランザクション手数料を支払うために暗号資産(Ethereumの場合はETH)が必要でした。EIP-7702の仕組みによりSenderはトランザクション手数料を支払う必要がなくなるため暗号資産を保有する必要がなくなります。これにより、以下のようなことが可能になります。
トランザクション手数料の支払いを代行するサービスが登場し、それを利用することで暗号資産を保有することなくさまざまな資産を扱うことができるようになる。
トランザクション手数料をステーブルコインで支払うことができるようになる。
トランザクション手数料支払い用のアカウントを別に設けて暗号資産を他のアカウントと分けて保有することで暗号資産が複数のアカウントに分散することを防ぎ、残高管理や財務処理がしやすくなる。
トランザクション手数料というブロックチェーン特有の要素が抽象化されることでウォレットの操作性やUXが向上する。
このように、暗号資産やトランザクション手数料を柔軟に扱うことができるようになり、ブロックチェーン・Web3全般に対する参入障壁が下がることで新たなビジネスモデルやサービスの登場が期待されます。一方で、EOAが従来とは異なりプログラムを実行することができるようになるため、本来意図しないプログラムを実行させられたり、トランザクションを再利用され資産の移転を複数回実行させられたりしてしまう(リプレイ攻撃)などのリスクがあり、セキュリティ面には十分注意する必要があります。
(本ブログの内容は2025年5月時点のものです)
お問い合わせ先
大和総研では、ブロックチェーン・Web3分野の研究開発を行う専門のプロジェクトを発足し、ウォレットに関する特許を取得するなど、ブロックチェーン・Web3に関する取り組みを行っています。長年にわたるブロックチェーン・Web3関連の取り組みの実績を活かし、お客様のブロックチェーン・Web3ビジネスの検討やシステムの構築をサポートします。ご要望・ご不明点などがありましたら、ITソリューションサービスサイトよりお問い合わせください。
参考文献
(注1)本邦初の PTS(私設取引システム)取扱セキュリティ・トークンの引受及び、 セキュリティ・トークンウォレット「Crossllet」開発のお知らせ
https://ssl4.eir-parts.net/doc/8601/ir_material3/218042/00.pdf
(注2)大和総研出版書籍「図解まるわかり NFTのしくみ」
https://www.dir.co.jp/publicity/book/20221201.html
(注3)ethereum.org 「EIP-7702: Set Code for EOAs」
https://eips.ethereum.org/EIPS/eip-7702
(注4)ethereum.org 「Pectra Mainnet Announcement」
https://blog.ethereum.org/2025/04/23/pectra-mainnet
(注5)ethers.js
https://docs.ethers.org/v6/