Giter Site home page Giter Site logo

gmtx's Introduction

GMTX - A protocol for generalized meta-transaction

Live demo

Lazy reading this document? Just want a demo? Here is the link (switch you metamask to goerli).

Introduction

What is a meta-transaction?

From Metamask's post:

Today, our users have to hold ether in every account to pay transaction fees for even the smallest interaction, to prevent them from spamming the network. This is a lot of overhead, especially when in many cases, a third party may be willing to pay a user’s transaction fees. This pattern of third-party transaction fee payment is known as Meta-Transactions, as popularized by Austin Thomas Griffith.

There are two major ways that MetaTransactions can become available to users, and we are working towards both, but this bounty will focus on the latter.

The Contract Account Approach

We look forward to allowing every user to have a contract-based account, and these accounts could support MetaTransactions natively. We are currently facilitating this goal with our Snaps plugin system, which will eventually support any number of different account types, such as the Gnosis Safe wallet, which already supports this type of MetaTransaction today.

The Contract Based Approach

The second major approach to allowing MetaTransactions is for contracts to expose a MetaTransaction processing method on themselves. We have seen some projects recently begin driving this forward, with Bounties Network (code) and Dai Stablecoin‘s permit method each exposing methods to facilitate third-party gas payment.

These approaches stand out because they do not require widespread adoption of a single type of account: Your dapp doesn’t need to handle each kind of contract account a user has, and it works out of the box today, and will continue to work for any key-authenticated method in the future.

Even once we support contract accounts, we expect there will still be some normal, external-key-based accounts, either to reduce the cost of publishing a contract for the account, or because a key may be used to manage a very small quantity of funds or permissions.

How is that different from the current state of the art?

Account based meta-transaction are pretty well understood, and some actual products are currently making use of this design. Argent has been using meta-transaction for a long time, and it works great without users even knowing about it. UniversalLogin is proposing an amazing toolkit to deploy meta-transaction powered multisig and have web3 application interact with it. Many other projects (uport, gnosis, authereum, kitsune-wallet, ..., I'm sure I'm forgetting some) are also working in this space, and they all have one thing in common: each user has its own smart contract that relays meta-transaction, and the address of this proxy is the "identity" of the user. This is the address apps see, its often associated with an ENS name, and its the address actually owning the user's assets. These user owned smart contracts can implement many security / recovery features but they are expensive to deploy, and are only worth it once the user as some assets that are really worth securing (here we could talk about counterfactual deployment and predictable addresses, but its not really today's topic).

Contract based meta-transaction, like the one used by Dai in the permit method are very different. As they are not relayed by a proxy, the msg.sender seen by the app is not the identity of the user. This is one of the main drawback of the GSN which needs proxy on top of it to accurately identify the users. If you don't want to deploy one proxy per user, but you still want an app to understand which user is interacting with you, regardless of who relays the transaction, then the app needs an additional mechanism to understand the true origin of a meta-transaction. Dai did it in the restricted context of their own smart contract, GMTX tries to provide a framework for any app to do so as easily as possible.

Why should I consider it for my dapp

Using the GMTX toolkit will simply had generalized meta-transaction to your smart contract, without the need to duplicate code between the "classic" methods and the "meta-transaction enabled" ones. All public and external methods are automatically accessible through ERC712 signed messages, opening gas-free options for your users.

Using GMTX in my dapp

How to?

To enable GMTX on a smart contract follow these steps:

  • Inherit from the GMTXRecipient contract.
  • Replace all references to msg.sender with call to the internal function _msgSender().

That's it! You can see an example use-case here. Live front-end is here.

Security concerns

GMTX has been designed with security in mind. Still, this is early work and we do not recommend to use it in production just yet. Review by the community is needed before to build trust on this design.

Dangerous cases:

  • Your smart contracts allows user to perform arbitrary calls to any address (address(...).call(...)):
    • DO NOT USE GMTX ! (users can impersonate someone else)
    • If you can restrict the target to be different from gmtx_mirror then it is or to use GMTX. Be carefull, and don't hesitate to contact us.
  • Your smart contracts performs calls to itself (this.functionName(...)):
    • ENABLE THE MIRROR MODE (_msgSender would not be reliable unless you enable the mirror mode: pass true to the GMTXReceiver's constructor)

How does it work?

GMTXRecipient smart contract

To enable meta-transaction support, the contract must inherit from the GMTXReceiver. This will expose a function receiveMetaTx(GMTX, bytes) public payable. This is the endpoint that processes meta-transaction.

GMTX meta-transaction are structures signed using the ERC712 pattern.

struct GMTX
{
	address from;
	bytes   data;
	uint256 gas;
	uint256 value;
	uint256 nonce;
	uint256 expiry;
	bytes32 salt;
}

Meta-transactions can we relayed by anyone, with no restriction. Once the validity of the meta-transaction has been verified, the receiveMetaTx function will relay the call described by the data field has if it was send by from. To do that, the receiveMetaTx appends the from to the end of the calldata. This additional information does not affect the forwarded call but it processed by the _msgSender() internal method to accurately return the from of the meta-transaction when it identifies a relayed call.

Additional public functions include:

  • gmtx_domain() returns (Domain): Domain used for ERC712 signature. Includes name and version of the signature protocol, chainId, and address of the verifying contract.
  • gmtx_mirror() returns (address): Target used for relayed transaction. Can be the GMTXReceiver itself or a mirror (see Direct vs Mirror Mode).
  • gmtx_replay(bytes32) returns (bool): Status of the meta-transaction. Given a GMTX digest, tells whether this meta-transaction has already been relayed or not. Used for replay protection.
  • gmtx_nonce(address) returns (uint256): Nonce of the account. Nonce can optionally be used to force meta-transaction ordering.

Meta-transaction verification include the following checks:

  • The signature must comply with ERC712 standard. If the sender is an EOA then ecrecover is used, otherwise, the signature verification falls back to sender using the ERC1271 interface.
  • The GMTX nonce field must either be 0 (implicitly disabling the check) or equal to the current nonce of the sender + 1.
  • The GMTX expiry field must eitger be 0 (implicitly disabling the check) or greater then the current timestamp. This gives an expiry date to meta-transaction.
  • The GMTX value field must be equal to the value sent by the relayer. This allows the sender to ask for some either to be send with the relayed meta-transaction. Economics of the relayer are beyond the scope of the standard and must be established between the user and the relayer.

Direct vs Mirror Mode

If the contract uses calls to itself as part of it's normal operation, the _msgSender() might not behave correctly as it might mistake those self calls for relayed calls, and extract a sender value from the end of non-prepared data. To avoid that we must establish a distinction between calls to self and relayer calls by using a mirror.

Rather then calling itself with the relayed call data, the GMTXReceiver will send the call to a mirror contract that will send the message, untouched, back to the GMTXReceiver. This will cause the relayed call to have a msg.sender equal to the mirror's address, which is different from the GMTXReceiver, thus providing a way to distinguish between calls to self and relayed calls.

Enabling the mirror mode is achieved by passing true to the GMTXReceiver's constructor.

Writing and signing a gmtx meta-transaction

As described earlier, GMTX meta-transaction is a structure containing 6 fields:

  • from: sender of the meta-transaction, similarly to the from field of a regular transaction. It can either be an EOA or and ERC1271 compliant smart-contract.
  • data: data of the transaction, similarly to the data field of a regular transaction. This is what would have otherwise been sent. In our case it's just encapsulated into the GMTX meta-transaction.
  • gas: gas necessary to run the transaction, similarly to the gas field of a regular transaction.
  • value: value (in wei) to be sent with the meta-transaction, similarly to the value field of a regular transaction. While this protocol mandates that the relayer includes this value when relaying the meta-transaction, the economics of the relaying are beyond the scope of this document.
  • nonce: optional field, can be left to 0 to skip this test. Otherwise, meta-transaction are valid if an only if the nonce is one over the current value (accessible through the gmtx_nonce function). This can be used to force the ordering of multiple meta-transaction signed in batch.
  • expiry: optional field, can be left to 0 to skip this test. Otherwise, meta-transaction are valid if an only if the expiry is greater then the current timestamp. This can be used to implement mechanism such as "run this meta-transaction before Tuesday 8pm, or disregard it"
  • salt: value used to ensure uniqueness of meta-transaction and protect against replay protection when no nonce is used. This should be set to a random value.

Once completed, the structure is to be signed using ERC712. The ERC712 domain used for the signature can be accessed through the gmtx_domain() function. Details of the domain (name and version) are subject to change.

{
	name:              "GeneralizedMetaTX",
	version:           "0.0.1-beta.1",
	chainId:           <network id>,
	verifyingContract: <address of target contract>
}

For EOAs, this can be done using eth_signTypedData_v3 or eth_signTypedData_v4 (available on Metamask) or directly with a private key using the eth-sig-util npm package. This will provide a 65 bytes long signature. In case of ERC1271 compliant sender, the signature specification, potentially similar, depends on the actual implementation of the sender.

The address of the targeted smart contract, GMTX structure, signature should when be bundled in an ExtendedGMTX structure and broadcasted to potential relayers.

struct ExtendedGMTX
{
	address target;
	GMTX    gmtx;
	bytes   signature;
}

Relaying a gmtx meta-transaction

In order to relay a meta-transaction, one should call the receiveMetaTx(GMTX,bytes) function on the targeted smart contract. If the meta-transaction's value is not null, the relaying call should include the corresponding value. This can be done by anyone, EOA or smart contract. This makes the GMTX protocol compatible with the GSN as well as any other reasonable relaying protocol.

See the discussion here.

While GMTX doesn't include a repayment mechanism out of the box, it is possible to build a relaying/repayment protocole based GMTX. This approach allows for a clear separation between end-user, relayer and repayer.

Comming next: Batched Transactions

In the contracts you will find a GTMXRecipient_V2.sol and the corresponding ERC712GMTX_V2.sol. These are non-tested protoypes for a refactor of the ERC712 structures to better support batched meta-transaction. The overall logic is very similar.

Authors & Contributors

  • Hadrien Croubois [email protected]
    • Protocol design
    • Solidity toolkit
    • Demo app
    • Documentation (this Readme)

gmtx's People

Contributors

amxx avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.