paint-brush
EIP-712 を使用したイーサリアムのガスレス メタトランザクション@thebojda
1,820 測定値
1,820 測定値

EIP-712 を使用したイーサリアムのガスレス メタトランザクション

Laszlo Fazekas12m2023/10/17
Read on Terminal Reader

長すぎる; 読むには

メタトランザクションの場合、ユーザーはトランザクションを記述する構造を作成し、秘密キーを使用してそれにデジタル署名します。それは誰かに小切手を書くのと似ています。デジタル署名されたトランザクションは中継ノードに送信され、中継ノードがそれをスマート コントラクトに送信します。コントラクトは署名を検証し、署名が有効であればトランザクションを実行します。中継ノードは契約の実行に対して支払いを行います。
featured image - EIP-712 を使用したイーサリアムのガスレス メタトランザクション
Laszlo Fazekas HackerNoon profile picture
0-item


私は最近、独自の ERC-20 トークンに基づく代替通貨システムであるKarma Moneyについての記事を書きました。私はカルマ マネーを、ユーザーが取引手数料もカルマで支払うことができるクローズド システムとして構想しました。独自のブロックチェーンを構築することはこれを実現する方法の 1 つですが、それは困難な作業です。ブロックチェーンのセキュリティと信頼性を確保するには、必要なインフラストラクチャと十分な規模のコミュニティを確立する必要があります。既存のブロックチェーンを使用する方がはるかに簡単です。 GnosisPolygonなどのチェーンがあり、イーサリアムと完全に互換性があり、取引手数料が非常に低くなります。これらのチェーンでの ERC20 取引の手数料は通常 1 セント未満です。問題は、この料金をチェーン独自の暗号通貨で支払わなければならないため、ユーザーにとってチェーンの使用が複雑になる可能性があることです。幸いなことに、 EIP-712メタル トランザクションというブリッジ ソリューションがあります。


メタトランザクションの場合、ユーザーはトランザクションを記述する構造を作成し、秘密キーを使用してそれにデジタル署名します。それは誰かに小切手を書くのと似ています。デジタル署名されたトランザクションは中継ノードに送信され、中継ノードがそれをスマート コントラクトに送信します。コントラクトは署名を検証し、署名が有効であればトランザクションを実行します。中継ノードは契約の実行に対して支払いを行います。


たとえば、カルマ トランザクションでは、ユーザーはトランザクションの金額 (たとえば、10 カルマ ドル)、その金額を送信するイーサリアム アドレス、および提示する意思のある取引手数料 (カルマ ドル) を提供します。取引のために。この構造はデジタル署名され、中継ノードに送信されます。ノードが取引手数料を許容できると判断した場合、デジタル署名された構造をカルマ コントラクトに送信し、カルマ コントラクトが署名を検証してトランザクションを実行します。取引手数料は中継ノードによってブロックチェーンのネイティブ通貨で支払われるため、ユーザーには、独自のブロックチェーンを必要とせずに取引に対してカルマドルを使って支払っているように見えます。


理論の次は実践を見てみましょう。


EIP-712 標準は、標準化された方法で構造化データ パッケージに署名する方法を定義します。 MetaMask は、これらの構造化データをユーザーが読み取り可能な形式で表示します。 MetaMask に示されている EIP-712 準拠の構造 (この URL でテストできます) は次のようになります。


EIP-712準拠の構造


上記のトランザクションは、次の単純なコードを使用して生成されました。


 async function main() { if (!window.ethereum || !window.ethereum.isMetaMask) { console.log("Please install MetaMask") return } const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); const chainId = await window.ethereum.request({ method: 'eth_chainId' }); const eip712domain_type_definition = { "EIP712Domain": [ { "name": "name", "type": "string" }, { "name": "version", "type": "string" }, { "name": "chainId", "type": "uint256" }, { "name": "verifyingContract", "type": "address" } ] } const karma_request_domain = { "name": "Karma Request", "version": "1", "chainId": chainId, "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" } document.getElementById('transfer_request')?.addEventListener("click", async function () { const transfer_request = { "types": { ...eip712domain_type_definition, "TransferRequest": [ { "name": "to", "type": "address" }, { "name": "amount", "type": "uint256" } ] }, "primaryType": "TransferRequest", "domain": karma_request_domain, "message": { "to": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "amount": 1234 } } let signature = await window.ethereum.request({ "method": "eth_signTypedData_v4", "params": [ accounts[0], transfer_request ] }) alert("Signature: " + signature) }) } main()


eip712domain_type_definition は、メタデータを含む一般的な構造の説明です。 name フィールドは構造の名前、version フィールドは構造の定義バージョン、chainId フィールドと verifyingContract フィールドはメッセージの対象となるコントラクトを決定します。実行コントラクトは、署名されたトランザクションがターゲット コントラクトでのみ実行されることを保証するために、このメタデータを検証します。


karma_request_domain には、 EIP712Domain 構造によって定義されたメタデータの特定の値が含まれています。


署名のために MetaMask に送信する実際の構造は、 transfer_request変数に含まれています。 type ブロックには型定義が含まれます。ここで、最初の要素は必須の EIP712Domain 定義であり、メタデータを記述します。これに実際の構造定義が続きます。この場合は TransferRequest です。これは、ユーザーの MetaMask に表示される構造です。ドメイン ブロックにはメタデータの特定の値が含まれ、メッセージにはユーザーと署名する特定の構造が含まれます。


カルマ マネーに関して言えば、メタトランザクションがどのようにまとめられてスマート コントラクトに送信されるかの例は次のようになります。


 const types = { "TransferRequest": [ { "name": "from", "type": "address" }, { "name": "to", "type": "address" }, { "name": "amount", "type": "uint256" }, { "name": "fee", "type": "uint256" }, { "name": "nonce", "type": "uint256" } ] } let nonce = await contract.connect(MINER).getNonce(ALICE.address) const message = { "from": ALICE.address, "to": JOHN.address, "amount": 10, "fee": 1, "nonce": nonce } const signature = await ALICE.signTypedData(karma_request_domain, types, message) await contract.connect(MINER).metaTransfer(ALICE.address, JOHN.address, 10, 1, nonce, signature) assert.equal(await contract.balanceOf(ALICE.address), ethers.toBigInt(11))


types変数はトランザクションの構造を定義します。 「from」は送信者のアドレス、「to」は受信者のアドレスです。金額は、転送されるトークンの量を表します。料金は、トランザクションを実行し、チェーンのネイティブ通貨でコストをカバーする代わりに、中継ノードに提供するトークンの「量」です。 「ノンス」は、トランザクションの一意性を保証するためのカウンターとして機能します。このフィールドがないと、トランザクションが複数回実行される可能性があります。ただし、nonce のおかげで、署名されたトランザクションは 1 回しか実行できません。


ethers.jsが提供するsignTypedData関数を使用すると、EIP-712 構造に簡単に署名できます。これは前に示したコードと同じことを行いますが、使用方法はより簡単です。


メタトランスファーは、メタトランザクションを実行するためのカルマコントラクトのメソッドです。それがどのように機能するかを見てみましょう:


 function metaTransfer( address from, address to, uint256 amount, uint256 fee, uint256 nonce, bytes calldata signature ) public virtual returns (bool) { uint256 currentNonce = _useNonce(from, nonce); (address recoveredAddress, ECDSA.RecoverError err) = ECDSA.tryRecover( _hashTypedDataV4( keccak256( abi.encode( TRANSFER_REQUEST_TYPEHASH, from, to, amount, fee, currentNonce ) ) ), signature ); require( err == ECDSA.RecoverError.NoError && recoveredAddress == from, "Signature error" ); _transfer(recoveredAddress, to, amount); _transfer(recoveredAddress, msg.sender, fee); return true; }


署名を検証するには、まず構造体のハッシュを生成する必要があります。これを行うための正確な手順は、サンプルのスマート コントラクトサンプル JavaScript コードを含むEIP-712 標準で詳しく説明されています。


要約すると、本質は、abi.encode を使用して TYPEHASH (構造記述のハッシュ) と構造のフィールドを組み合わせるということです。次に、keccak256 ハッシュを生成します。ハッシュは、Karma コントラクト内の EIP712 OpenZeppelin コントラクトから継承された _hashTypedDataV4 メソッドに渡されます。この関数はメタデータを構造に追加し、最終的なハッシュを生成するため、構造の検証が非常にシンプルかつ透過的になります。最も外側の関数は ECDSA.tryRecover で、ハッシュと署名から署名者のアドレスを復元しようとします。 「from」パラメータのアドレスと一致する場合、署名は有効です。コードの最後で、実際のトランザクションが実行され、トランザクションを実行する中継ノードが料金を受け取ります。


EIP-712 は署名構造の一般標準であり、メタトランザクションを実装するための多くの用途の 1 つにすぎません。署名はスマート コントラクトだけでなく検証できるため、ブロックチェーン以外のアプリケーションでも非常に役立ちます。たとえば、ユーザーが秘密キーで自分自身を識別するサーバー側の認証に使用できます。このようなシステムは、通常は暗号通貨に関連する高レベルのセキュリティを提供することができ、ハードウェア キーのみを使用して Web アプリケーションを使用できるようになります。さらに、MetaMask を使用して個々の API 呼び出しに署名することもできます。


EIP-712 標準のこの概要が多くの人にとってインスピレーションとなり、ブロックチェーン ベースのプロジェクトと非ブロックチェーン プロジェクトの両方で EIP-712 標準を活用できることを願っています。


すべてのコードは、 Karma Money の GitHub リポジトリで入手できます。