コンテンツへスキップ
0%

メタトランザクション(ERC-2771)とは?

2023年8月9日 公開読了時間 1 分

すべてのEthereumトランザクションはガスを使用し、各トランザクションの送信者はガス代を支払うのに十分なEtherを保有している必要があります。これにより、新規ユーザーはdappを使い始める前にEtherを購入する必要が生じ(これは面倒な作業になり得ます)、ユーザーオンボーディングにおける大きな障壁となっています。

いわゆる「メタトランザクション」は、ユーザーに代わってガス代を支払うことを可能にしました。

しかし、こうしたソリューションの多くはAccount Abstraction(ERC-4337)インフラへ移行しつつあります。これはガスの抽象化に関してより有望で洗練された方法であり、多くの機能を備えているためです。

メタトランザクションとは何か

メタトランザクションのアイデアはシンプルです。Relayerと呼ばれる第三者が、ユーザーに代わってトランザクションを送信し、ガス代を支払います。

ユーザーは、実行されるトランザクションの情報を含むメッセージ(できればEIP-712準拠のもの)に署名します。

署名されたメッセージはRelayerに渡され、Relayerは自身が対価を得られるかどうかを検証し(これはオプションの場合もあり、詳細は後述します)、ガス代を支払うのに十分な資金があるかを確認し、ネイティブトランザクションに署名して実行のために送信する責任を負います。

OpenZepplinのRelayerを例に、メタトランザクションを理解していきましょう。

メタトランザクションのアニメーション図:ユーザーがメッセージに署名し、Relayerがそれを送信してガス代を支払う
メタトランザクションのステップバイステップガイド

メタトランザクションリクエストはどのようなものか

サンプルのメタトランザクションメッセージは以下のようになります。

NFTをミントするサンプルのメタトランザクションメッセージ。fromアドレスとcallデータを表示
サンプルのメタトランザクション

上記のメッセージはNFTをミントするものであり、fromアドレス0x7099797...によって署名されます。

メッセージが署名されると、クライアント(またはユーザー)はメタトランザクションリクエスト(下記参照)を作成できます。これはPOSTリクエストとしてAutotaskに送信されます。AutotaskはWebhookであり、呼び出されると内部的にRelayerの秘密鍵を使ってネイティブトランザクションに署名します。

Autotask relayer webhookに送信されるメタトランザクションのPOSTリクエストペイロード
メタトランザクションのPOSTリクエスト

メタトランザクションのRelayerとは何か

Relayerとは、ユーザーのガス代をスポンサーするための資金を保有するEthereumアカウントです。このアカウントの秘密鍵は、プロバイダーのサーバー上の安全なvaultに保管されます。

開発者はRelayerに資金を送金することで、ユーザーのトランザクション手数料をカバーできます。

開発者は好きなだけRelayerを立ち上げることができますが、それぞれ個別にETHで資金を供給する必要があります。

OZ Relayでは、Relayerの一時停止、Whitelistされたアドレスからのリクエストの受け入れ、Gas Price Cappingが可能です。必要であれば、さらに条件付きロジックをAutotaskにプログラムすることもできます。

なお、ユーザーが定義したトランザクションは、現在Relayerのネイティブトランザクションのdataフィールド内に格納されています。メタトランザクションとは、あるトランザクションの中に入った別のトランザクションなのです。

メタトランザクションにおけるMinimalForwarderとは何か

ここまで、オンチェーンでの検証は一切行われていません。ここで登場するのがMinimalForwarderです。

MinimalForwarderは、ユーザーが署名したメッセージの妥当性とリプレイ保護を検証するオンチェーンのスマートコントラクトです。

text
Copied
struct ForwardRequest { address from; address to; uint256 value; uint256 gas; uint256 nonce; bytes data; } function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) { address signer = _hashTypedDataV4( keccak256( abi.encode( _TYPEHASH, req.from, req.to, req.value, req.gas, req.nonce, keccak256(req.data) ) ) ).recover(signature); return _nonces[req.from] == req.nonce && signer == req.from; }

検証に成功すると、MinimalForwarderは適切なcalldataを使ってリクエスト先のコントラクトを呼び出すことで、トランザクションを実行します。

text
Copied
function execute(ForwardRequest calldata req, bytes calldata signature) public payable returns (bool, bytes memory) { require(verify(req, signature), "MinimalForwarder: signature does not match request"); _nonces[req.from] = req.nonce + 1; (bool success, bytes memory returndata) = req.to.call{gas: req.gas, value: req.value}(abi.encodePacked(req.data, req.from)); // Validate that the relayer has sent enough gas for the call. // See if (gasleft() <= req.gas / 63) { // We explicitly trigger invalid opcode to consume all gas and bubble-up the effects, since // neither revert or assert consume all gas since Solidity 0.8.0 // /// @solidity memory-safe-assembly assembly { invalid() } } return (success, returndata); }

ここにメタトランザクションの欠点も現れます。というのも、MinimalForwarderが呼び出すコントラクト側で、MinimalForwarderがユーザーに代わってそのコントラクトを呼び出せるようにする必要があるからです。

ERC-2771とは何か

最終的に、呼び出しが目的のターゲットコントラクトに到達したとき、そのコントラクトはRelayerのトランザクションの中にネストされた、元のユーザー(msg.sender)とcalldata(msg.data)を特定できる必要があります。このプロセスを標準化するためにERC2771が提案されました。

OpenZepplinは、ERC2771Contextというユーティリティコントラクトを提供しており、スマートコントラクト開発者がMinimalForwarderから送られてきたデータから、本来のmsg.senderとmsg.dataを容易に取り出せるようにしています。

text
Copied
contract YourContract is ERC2771Context { constructor(MinimalForwarder forwarder, string memory uri_) ERC2771Context(address(forwarder)) {} // Depending on whether the call is made by the `MinimalForwarder` or not the `msg.sender` and `msg.data` will be inferred accordingly }

各スマートコントラクトはメタトランザクションの読み取り方法を知っておく必要があるため、すでにデプロイ済みのコントラクトに対してこれを実装するのは難しく、バグの原因にもなりかねません。

他にも多くのメタトランザクションインフラサービスが存在しますが、そのいずれも_msg.sender_と_msg.data_を導出するために、対象コントラクトがEIP-712またはEIP-2771ベースのコントラクトを継承していることを要求します。

この標準は優れたものですが、遡及的には機能しません。これが、ERC-4337が作られた理由であり、既存の多くのERC-2771ソリューションを置き換えつつあります。

よくある質問

メタトランザクションとは何か

メタトランザクションは、Relayerと呼ばれる第三者がユーザーに代わってトランザクションを送信しガス代を支払うことを可能にする仕組みで、ユーザーがアプリを利用する前にEtherを保有していなければならないという障壁を取り除きます。

メタトランザクションはどのように機能するか

ユーザーはトランザクション情報を含むメッセージに署名し、それがRelayerに渡されます。Relayerはリクエストを検証し、ガス代を支払い、ネイティブトランザクションに署名して、ブロックチェーン上での実行のために送信します。

ERC-2771とは何か

ERC-2771は、信頼されたforwarderを通じてメタトランザクションを受け取った際に、スマートコントラクトが元のユーザーとcalldataを特定できるようにする標準であり、正しいmsg.senderとmsg.dataの抽出を保証します。

メタトランザクションにおけるMinimalForwarderとは何か

MinimalForwarderは、ユーザーが署名したメッセージの真正性とリプレイ保護を検証したうえで、ターゲットコントラクトを呼び出してトランザクションを実行するオンチェーンのスマートコントラクトです。

ERC2771Contextとは何か

ERC2771ContextはOpenZeppelinのユーティリティコントラクトで、スマートコントラクト開発者がMinimalForwarderから送られてきたメタトランザクションから、本来のmsg.senderとmsg.dataを容易に取り出せるようにします。

ERC-2771のメタトランザクションにはどのような制約があるか

ERC-2771は、対象コントラクトがEIP-712またはEIP-2771ベースのコントラクトを継承することでこの標準を明示的にサポートする必要があるため、すでにデプロイ済みのコントラクトへの実装が難しく、バグを引き起こす可能性があります。

ERC-2771とERC-4337はどのように比較されるか

ERC-2771はRelayerとforwarderを通じてガスレストランザクションを可能にしますが、ERC-4337(Account Abstraction)は遡及的に機能し、既存のコントラクトを変更する必要がないため、ほとんどのERC-2771ソリューションを置き換えつつあります。

メタトランザクションにおいてRelayerはどのような役割を果たすか

RelayerはユーザーのガスFeeをスポンサーするEthereumアカウントであり、その秘密鍵はプロバイダーのサーバー上に安全に保管されます。また、Whitelist登録、Gas Price Capping、条件付きロジックといった機能を設定することもできます。

Background gradient

ブロックチェーンで魔法を生み出す

Alchemyは、最も強力なweb3開発者向けプロダクトとツールを、豊富なリソース、コミュニティ、そして卓越したサポートと組み合わせて提供します。