EIP 2771

Secure Protocol For Native Meta Transactions

With adherence to EIP-2771 to enable native meta transactions in your contract, you can just inherit a simple contract BaseRelayRecipient.sol and set the trusted forwarder address.

import "@opengsn/gsn/contracts/BaseRelayRecipient.sol";
contract MyContract is BaseRelayRecipient {
* Set the trustedForwarder address either in constructor or
* in other init function in your contract
constructor(address _trustedForwarder) public {
trustedForwarder = _trustedForwarder;
* You should add one setTrustedForwarder(address _trustedForwarder)
* method with onlyOwner modifier so you can change the trusted
* forwarder address to switch to some other meta transaction protocol
* if any better protocol comes tomorrow or current one is upgraded.
* Override this function.
* This version is to keep track of BaseRelayRecipient you are using
* in your contract.
function versionRecipient() external view override returns (string memory) {
return "1";

List of trusted forwarder addresses per network can be found here https://docs.biconomy.io/smart-contracts/addresses

Use _msgSender(), not msg.sender

The Trusted Forwarder is responsible for signature verification and replay protection and forwards the transaction to your smart contract by appending the user address at the end of calldata. The _msgSender() method in your smart contract (inherited by BaseRelayRecipient) does the rest by returning the correct address for any context. Use _msgSender() wherever you use msg.sender.

Do add setTrustedForwarder(address _forwarder) method in your smart contract to change the address in future in case new implementations are introduced in the future, as its not a part of EIP-2771. You can add onlyOwner modifier to this function.

TrustedForwarder is not expected to change anytime in the future until new signature scheme or a better replay protection mechanism is introduced. But in that case new TrustedForwarder will be deployed and current implementation will continue to be supported.

So TrustedForwarder address can be hardcoded too in your contract, if you have security concerns about adding setTrustedForwarder method with onlyOwner modifier.

Why and when should you use this approach?

Your contract can be easily modified to securely accept meta-transactions whilst giving you the flexibility to change the meta transaction approach your Dapp uses if your needs change. E.g. it may make sense in your Dapp to use our product, Forward, which allows end users to pay gas in ERC20 tokens.

​Enable paying gas in ERC20 uses Biconomy’s Trusted Forwarder as a Lego. We intend to build all future meta transaction related products on top of EIP-2771 so building on this approach is probably your best bet - even if you don’t need to Enable paying gas in ERC20​

In next sections we'll see the steps to start using EIP-2771 enabled gasless transactions.