Skip to main content
Gelato is deprecating the trusted forwarder contracts. The old forwarder will no longer be available, and Gelato will not provide a replacement. To continue using ERC-2771 meta-transactions, you must deploy your own trusted forwarder and update your integration.

Migration Examples Repository

Complete migration examples with contracts, deployment scripts, and step-by-step code.

Migration Steps

1

Deploy Your Own Trusted Forwarder

Choose between two forwarder types based on your needs:
DISCLAIMER: All Solidity contracts in the referenced repository are provided as examples for educational purposes only. They have NOT been audited and may contain bugs or security vulnerabilities. USE AT YOUR OWN RISK. For production use, please ensure proper security audits are conducted by qualified professionals.
Sequential Forwarder (Nonce-based)Contract: TrusteForwarderERC2771.solConcurrent Forwarder (Hash-based)Contract: TrustedForwarderConcurrentERC2771.sol
Save your deployed forwarder address - you’ll need it for the next steps.
2

Whitelist the Trusted Forwarder in Your Contract

Your contract must trust the new forwarder address. How you do this depends on your contract’s architecture:If your contract has an updateable forwarder:
If your contract has an immutable forwarder:You’ll need to redeploy your contract with the new forwarder address:
If your contract is not upgradeable and contains important state data, a migration strategy will be required to transfer the state to the new contract.
3

Update Frontend Encoding

Previously, Gelato handled the encoding to the trusted forwarder internally. Now you must encode the call to the forwarder yourself.

Key Changes Summary

Sequential vs Concurrent

Migration Checklist

  • Deploy trusted forwarder (sequential or concurrent)
  • Whitelist forwarder in your contract (update address or redeploy)
  • Update frontend to encode calls to your forwarder
  • Test on testnet
  • Deploy to production

Troubleshooting

  • Ensure domain verifyingContract is your forwarder address (not your contract)
  • Ensure domain name matches exactly: "TrustedForwarder" or "TrustedForwarderConcurrentERC2771"
  • Ensure chainId matches the network
  • Get fresh nonce from forwarder before each signature: forwarder.userNonce(user)
  • Don’t reuse old signatures
  • Generate a new random userSalt for each transaction
  • Don’t reuse salts
  • Ensure your contract uses _msgSender() (from ERC2771Context)
  • Ensure the forwarder is whitelisted in your contract

Example Implementations

Sequential Forwarder

Nonce-based replay protection

Concurrent Forwarder

Salt-based replay protection

Sequential Example Script

Complete sequential implementation

Concurrent Example Script

Complete concurrent implementation
Example Contracts: