{"id":3103,"date":"2026-09-08T11:09:56","date_gmt":"2026-09-08T11:09:56","guid":{"rendered":"https:\/\/nownodes.io\/blog\/?p=3103"},"modified":"2026-09-08T11:09:57","modified_gmt":"2026-09-08T11:09:57","slug":"what-are-meta-transactions-erc-2771","status":"publish","type":"post","link":"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/","title":{"rendered":"How to Implement Meta Transactions With ERC-2771"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">To implement meta transactions with ERC-2771, four separate pieces have to come together: a forwarder-aware contract, a trusted forwarder, an off-chain signing step, and something willing to submit the transaction and pay for it. None of these four pieces is complicated in isolation \u2014 the mistakes happen in how they connect.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This ERC-2771 tutorial walks through that implementation end to end: inheriting the right base contract, signing and relaying a request, choosing a relayer, and closing the security gaps that have actually cost real projects money. If you want the concept first, <a href=\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/\">what meta transactions are and how ERC-2771 works<\/a> covers that ground \u2014 this is the hands-on follow-up for anyone ready to implement meta transactions in a live contract.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"what-do-you-need-before-you-start\">What Do You Need Before You Start?<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Four things need to be in place before writing any code for this ERC-2771 implementation. Skipping one tends to surface as a confusing bug three steps later rather than an obvious error up front.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>A contract you can still modify.<\/strong> Forwarder support has to be built in from the start \u2014 there&#8217;s no way to add ERC-2771 to a contract that&#8217;s already deployed and immutable (more on upgradeable contracts in the FAQ below).<\/li>\n\n\n\n<li><strong>A trusted forwarder<\/strong> \u2014 either your own deployment of OpenZeppelin&#8217;s ERC2771Forwarder, or one already running on your target network.<\/li>\n\n\n\n<li><strong>A signing library<\/strong> on the client side. ethers.js and viem both handle EIP-712 typed-data signing natively, which is the format a forwarder expects.<\/li>\n\n\n\n<li><strong>Something to relay the transaction<\/strong> \u2014 your own backend wallet, or a relayer network, covered in the comparison further down.<\/li>\n<\/ul>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-1-how-do-you-make-a-contract-forwarderaware\">Step 1: How Do You Make a Contract Forwarder-Aware?<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Inheriting <a href=\"https:\/\/docs.openzeppelin.com\/contracts\/5.x\/api\/metatx\" rel=\"nofollow noopener noreferrer\">ERC2771Context<\/a> and passing a forwarder address to the constructor is the entire contract-side change. It overrides <code>_msgSender()<\/code> so the contract reads the real signer instead of the forwarder&#8217;s own address, without touching any of the contract&#8217;s actual logic:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import {ERC2771Context} from \"@openzeppelin\/contracts\/metatx\/ERC2771Context.sol\";\n\ncontract MyDapp is ERC2771Context {\n    constructor(address trustedForwarder) ERC2771Context(trustedForwarder) {}\n\n    function doSomething() external {\n        address user = _msgSender(); \/\/ the real signer, not the forwarder\n        \/\/ your logic here, unchanged\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the whole change for a simple contract, and it&#8217;s the foundation every other part of an ERC-2771 implementation builds on. Two situations complicate it: multiple inheritance, where every parent contract reading <code>msg.sender<\/code> directly needs to call <code>_msgSender()<\/code> instead, and any use of <code>Multicall<\/code>, which needs a specific fix covered in the security section rather than glossed over here.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-2-where-does-the-forwarder-contract-come-from\">Step 2: Where Does the Forwarder Contract Come From?<\/h2>\n\n\n<p class=\"wp-block-paragraph\">The choice is control versus setup time. Deploying your own forwarder gives you one dedicated, verified contract with no dependency on anyone else&#8217;s infrastructure. Pointing your contract at a forwarder a relayer network already runs means less deployment work, at the cost of trusting infrastructure you don&#8217;t control.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/image-38-1024x683.png\" alt=\"\" class=\"wp-image-3104\" srcset=\"https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/image-38-1024x683.png 1024w, https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/image-38-300x200.png 300w, https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/image-38-768x512.png 768w, https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/image-38.png 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Most teams shipping a single dApp deploy their own, the simplest path to an ERC-2771 implementation with no external dependencies. It takes minutes: deploy <code>ERC2771Forwarder<\/code> with a <code>name<\/code> string \u2014 used in its EIP-712 domain \u2014 then point your contract&#8217;s constructor at its address. The constructor signature is just <code>constructor(string memory name) EIP712(name, \"1\")<\/code>, and that name has to match exactly what your signing code uses in the next step.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Either way, the trusted forwarder&#8217;s address isn&#8217;t something to hardcode and forget. If it&#8217;s ever found to be compromised or buggy, a project needs a way to stop trusting it \u2014 which is why some teams deliberately keep <code>_trustedForwarder<\/code> upgradeable instead of immutable, a trade-off covered below.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-3-how-do-you-build-and-sign-the-offchain-request\">Step 3: How Do You Build and Sign the Off-Chain Request?<\/h2>\n\n\n<p class=\"wp-block-paragraph\">The signer never touches the blockchain directly \u2014 that separation is the whole point of how you implement meta transactions this way. They sign a structured EIP-712 message describing the call they want made, and hand the signature to whatever relays it:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">javascript<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const domain = {\n  name: \"MyForwarder\", \/\/ must match the name passed to the forwarder's constructor\n  version: \"1\",\n  chainId: 1,\n  verifyingContract: forwarderAddress,\n};\n\nconst types = {\n  ForwardRequest: &#91;\n    { name: \"from\", type: \"address\" },\n    { name: \"to\", type: \"address\" },\n    { name: \"value\", type: \"uint256\" },\n    { name: \"gas\", type: \"uint256\" },\n    { name: \"nonce\", type: \"uint256\" },\n    { name: \"deadline\", type: \"uint48\" },\n    { name: \"data\", type: \"bytes\" },\n  ],\n};\n\nconst message = {\n  from: signerAddress,\n  to: targetContractAddress,\n  value: 0,\n  gas: 200000,\n  nonce: await forwarder.nonces(signerAddress),\n  deadline: Math.floor(Date.now() \/ 1000) + 3600,\n  data: targetContract.interface.encodeFunctionData(\"doSomething\", &#91;]),\n};\n\nconst signature = await signer.signTypedData(domain, types, message);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>EIP-712 domain:<\/strong> a set of contract-specific values \u2014 name, version, chain ID, and verifying contract address \u2014 mixed into every signature so it can&#8217;t be replayed against a different contract or a different chain. See <a href=\"https:\/\/eips.ethereum.org\/EIPS\/eip-712\" rel=\"nofollow noopener noreferrer\">EIP-712<\/a> for the full specification.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>nonce<\/code> comes from the forwarder&#8217;s own <code>nonces()<\/code> mapping, and <code>deadline<\/code> is a plain Unix timestamp \u2014 an hour out in the example above. Both exist so a signed request can&#8217;t be replayed or left to sit indefinitely before someone finally submits it. <\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-4-how-does-the-relayer-actually-submit-it\">Step 4: How Does the Relayer Actually Submit It?<\/h2>\n\n\n<p class=\"wp-block-paragraph\">This is the one on-chain step, and the only place gas gets spent. The relayer takes the signed message and calls the forwarder&#8217;s <code>execute()<\/code> function \u2014 but the struct it submits isn&#8217;t identical to what got signed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const request = {\n  from: signerAddress,\n  to: targetContractAddress,\n  value: 0,\n  gas: 200000,\n  deadline,\n  data: callData,\n  signature,\n};\n\nawait forwarder.execute(request); \/\/ relayer's wallet pays gas, not the signer's<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Notice <code>nonce<\/code> is missing here. The on-chain <code>ForwardRequestData<\/code> struct doesn&#8217;t store it \u2014 <a href=\"https:\/\/github.com\/OpenZeppelin\/openzeppelin-contracts\/blob\/master\/contracts\/metatx\/ERC2771Forwarder.sol\" rel=\"nofollow noopener noreferrer\">the forwarder looks up the current nonce itself<\/a> and checks it against what was signed, rather than trusting a value the caller provides. Assuming the signed struct and the submitted struct share the same shape is a common first-integration mistake.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Calling <code>verify()<\/code> before <code>execute()<\/code> costs nothing and confirms a request is still good \u2014 signature intact, nonce current, deadline not passed \u2014 which is worth doing before spending gas on a call that might revert anyway.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"should-you-run-your-own-relayer-or-use-a-network\">Should You Run Your Own Relayer or Use a Network?<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Someone has to operate the piece that watches for signed requests, decides which ones to sponsor, and actually submits them. Whichever way you implement meta transactions in production, a few established options handle this piece differently enough that the choice matters:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Relayer<\/th><th>Model<\/th><th>ERC-2771 support<\/th><th>Network reach<\/th><th>Notable number<\/th><\/tr><\/thead><tbody><tr><td><a href=\"https:\/\/docs.opengsn.org\/\" rel=\"nofollow noopener noreferrer\">OpenGSN<\/a><\/td><td>Open-source; self-host or integrate with its existing relay infrastructure<\/td><td>Native \u2014 the original reference implementation<\/td><td><a href=\"https:\/\/docs.opengsn.org\/networks.html\" rel=\"nofollow noopener noreferrer\">7 networks<\/a>: Ethereum, Polygon, Optimism, Arbitrum, Avalanche, BNB Smart Chain, Gnosis Chain<\/td><td>Longest production track record of the four<\/td><\/tr><tr><td><a href=\"https:\/\/docs.gelato.network\/web3-services\/relay\" rel=\"nofollow noopener noreferrer\">Gelato Relay<\/a><\/td><td>Hosted, decentralized executor network<\/td><td>Native \u2014 dedicated <code>sponsoredCallERC2771<\/code> and <code>callWithSyncFeeERC2771<\/code> methods<\/td><td>17 EVM networks<\/td><td>1Balance lets one deposit cover gas across every supported chain<\/td><\/tr><tr><td><a href=\"https:\/\/docs.biconomy.io\/\" rel=\"nofollow noopener noreferrer\">Biconomy<\/a><\/td><td>Hosted, built around its own smart-account stack<\/td><td>Indirect \u2014 current tooling centers on ERC-4337 rather than ERC-2771 forwarding<\/td><td>Multiple EVM networks<\/td><td>70M+ transactions processed, 4.5M+ smart accounts deployed<\/td><\/tr><tr><td><a href=\"https:\/\/docs.openzeppelin.com\/relayer\" rel=\"nofollow noopener noreferrer\">OpenZeppelin Relayer<\/a><\/td><td>Open-source, self-hosted<\/td><td>General-purpose \u2014 pairs with any forwarder, not ERC-2771-specific<\/td><td>EVM, Solana, and Stellar<\/td><td>Licensed AGPL v3.0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">OpenGSN and Gelato Relay are the two built specifically around the trusted-forwarder pattern, which makes either a faster starting point for an ERC-2771 implementation than wiring up general-purpose relay infrastructure yourself. Biconomy and OpenZeppelin Relayer are worth knowing about, but neither is an ERC-2771-first tool the way the other two are.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"how-do-you-test-before-going-to-mainnet\">How Do You Test Before Going to Mainnet?<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Deploy the same ERC-2771 implementation \u2014 forwarder, forwarder-aware contract, signing code \u2014 to a testnet first, and run through the full signed-request-to-execute() flow with worthless test ETH before anything touches real funds. A forwarder bug is exactly the kind of thing that&#8217;s cheap to catch on a testnet and expensive to discover in production.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before that, use <code>verify()<\/code> and a plain <code>eth_call<\/code> against the forwarder to <a href=\"https:\/\/nownodes.io\/blog\/how-to-simulate-a-transaction-on-ethereum\/\">simulate the request<\/a> without spending any gas at all \u2014 the same pre-flight check a wallet runs before you sign anything, just triggered from your own test script instead. Testnet access is the one piece of infrastructure every step in this ERC-2771 tutorial depends on; providers like <a href=\"https:\/\/nownodes.io\/nodes\/ethereum-eth\">NOWNodes<\/a> give RPC access to Ethereum and 120-plus other networks so a team can test a forwarder deployment without running and syncing a node just for that.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"what-security-mistakes-have-actually-cost-real-projects\">What Security Mistakes Have Actually Cost Real Projects?<\/h2>\n\n<h3 class=\"wp-block-heading\" id=\"the-erc2771context-multicall-addressspoofing-bug\">The ERC2771Context + Multicall Address-Spoofing Bug<\/h3>\n\n\n<p class=\"wp-block-paragraph\">In December 2023, OpenZeppelin publicly disclosed a vulnerability affecting any contract that combined <code>ERC2771Context<\/code> with a <code>Multicall<\/code>-style batching function. The issue isn&#8217;t specific to OpenZeppelin&#8217;s own contracts \u2014 thirdweb&#8217;s prebuilt contracts were among those broadly affected, since the same combination shows up across many independent codebases.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the mechanism: <code>Multicall<\/code> batches several sub-calls into one transaction using <code>delegatecall<\/code>, which preserves <code>msg.sender<\/code> across every sub-call. If a trusted forwarder calls <code>multicall()<\/code>, an attacker can craft one of the batched sub-calls so its own data ends in a fake 20-byte address. Because <code>msg.sender<\/code> still reads as the trusted forwarder inside that sub-call, <code>_msgSender()<\/code> trusts the attacker&#8217;s fake suffix instead of the real signer \u2014 letting the attacker impersonate any address for that nested call.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/image-39-1024x683.png\" alt=\"\" class=\"wp-image-3106\" srcset=\"https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/image-39-1024x683.png 1024w, https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/image-39-300x200.png 300w, https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/image-39-768x512.png 768w, https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/image-39.png 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">OpenZeppelin fixed this by giving <code>Multicall<\/code> a context-suffix length specifically for ERC-2771 data, so a forwarded call is correctly identified and handled the same way across every sub-call rather than trusted blindly. As OpenZeppelin&#8217;s Vlad Estoup and Pedro Aisenson put it in the company&#8217;s own write-up of the incident: &#8220;Even well-audited, secure code can become a liability when combined in unforeseen ways. When importing dependencies, be sure to know how they work on the inside and think about how you&#8217;re combining them with other functionality.&#8221;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the practical takeaway for anyone working on an ERC-2771 implementation today: an <a href=\"https:\/\/nownodes.io\/blog\/top-smart-contract-auditing-firms\/\">audited base contract<\/a> isn&#8217;t a guarantee once you start combining it with other patterns. The interaction between two individually safe pieces is exactly where new bugs come from \u2014 checking that interaction is part of the audit scope, not something either component&#8217;s own review already covered.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"smaller-mistakes-that-still-break-production\">Smaller Mistakes That Still Break Production<\/h3>\n\n\n<p class=\"wp-block-paragraph\">A handful of narrower mistakes show up often enough in a real ERC-2771 implementation to name directly:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Treating the trusted forwarder as permanent.<\/strong> An immutable forwarder address can&#8217;t be changed if that forwarder turns out to be compromised. Making it upgradeable costs little and gives a project an emergency off switch.<\/li>\n\n\n\n<li><strong>Skipping the deadline check on the client side.<\/strong> The contract enforces it, but a relayer that queues requests for too long wastes a user&#8217;s signature on something that will simply revert.<\/li>\n\n\n\n<li><strong>Forgetting <code>isTrustedForwarder()<\/code> in a multi-contract system.<\/strong> Each contract decides independently whether to trust a given forwarder \u2014 there&#8217;s no global setting, so a new contract added to an existing system needs its own explicit opt-in.<\/li>\n<\/ul>\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n<p class=\"wp-block-paragraph\">The ERC-2771 implementation itself is small: one base contract to inherit, one forwarder to deploy or point at, a few dozen lines of signing code, and a relayer to call <code>execute()<\/code>. What takes real care is everything around that core \u2014 picking a relayer that fits the project, testing the full flow before mainnet, and knowing that a securely audited piece can still fail once it&#8217;s combined with something else.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">None of that is a reason to avoid the pattern. It&#8217;s a reason to treat the trusted forwarder and its interactions as the part of the system that deserves the closest look, since that&#8217;s precisely where the one disclosed real-world failure actually happened.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"faq\">FAQ<\/h3>\n\n<h3 class=\"wp-block-heading\" id=\"can-you-add-erc2771-support-to-an-alreadydeployed-contract\">Can You Add ERC-2771 Support to an Already-Deployed Contract?<\/h3>\n\n\n<p class=\"wp-block-paragraph\">Not to the contract itself \u2014 inheriting <code>ERC2771Context<\/code> changes how the contract is compiled and deployed, so you can&#8217;t add an ERC-2771 implementation to an existing immutable contract after the fact. A team in that position typically deploys a new, forwarder-aware version and migrates users over, or wraps the old contract behind a proxy pattern that supports upgrades.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"do-you-need-a-separate-forwarder-for-every-contract\">Do You Need a Separate Forwarder for Every Contract?<\/h3>\n\n\n<p class=\"wp-block-paragraph\">No. A single trusted forwarder can serve any number of contracts, as long as each one explicitly calls it trusted through <code>isTrustedForwarder()<\/code>. Most teams run one forwarder across their whole system rather than deploying a new one per contract.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"how-much-does-it-cost-to-run-your-own-relayer\">How Much Does It Cost to Run Your Own Relayer?<\/h3>\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s no gas cost to operate the relayer role itself \u2014 the cost is whatever gas the relayer pays on behalf of users, plus normal server hosting for the backend that watches for and submits signed requests. That&#8217;s why most relayers apply sponsorship rules rather than covering every request unconditionally.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"does-openzeppelins-forwarder-work-with-upgradeable-contracts\">Does OpenZeppelin&#8217;s Forwarder Work With Upgradeable Contracts?<\/h3>\n\n\n<p class=\"wp-block-paragraph\">Yes, with the standard caveat that applies to any upgradeable setup: the trusted forwarder&#8217;s address and the forwarder-aware logic both have to live in the implementation contract, and <code>_msgSender()<\/code> needs to resolve correctly through whatever proxy pattern is in use. This is exactly the kind of interaction worth including in a security review rather than assuming by default.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>To implement meta transactions with ERC-2771, four separate pieces have to come together: a forwarder-aware contract, a trusted forwarder, an off-chain signing step, and something willing to submit the transaction and pay for it. None of these four pieces is complicated in isolation \u2014 the mistakes happen in how they connect. This ERC-2771 tutorial walks [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":3105,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_eb_attr":"","_lmt_disableupdate":"","_lmt_disable":"","_monsterinsights_skip_tracking":false,"footnotes":""},"categories":[102],"tags":[],"class_list":["post-3103","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dev-report"],"blocksy_meta":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.0 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>ERC-2771 Implementation Guide: Meta Transactions Step by Step<\/title>\n<meta name=\"description\" content=\"A hands-on ERC-2771 tutorial: make a contract forwarder-aware, sign and relay a meta transaction, choose a relayer, and avoid the mistakes that have broken real deployments.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"ERC-2771 Implementation Guide: Meta Transactions Step by Step\" \/>\n<meta property=\"og:description\" content=\"A hands-on ERC-2771 tutorial: make a contract forwarder-aware, sign and relay a meta transaction, choose a relayer, and avoid the mistakes that have broken real deployments.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/\" \/>\n<meta property=\"og:site_name\" content=\"NOWNodes Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-08T11:09:56+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-08T11:09:57+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/nodes_1-42.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2400\" \/>\n\t<meta property=\"og:image:height\" content=\"1200\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"\u0410nastasia\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@nownodes\" \/>\n<meta name=\"twitter:site\" content=\"@nownodes\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"\u0410nastasia\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/\"},\"author\":{\"name\":\"\u0410nastasia\",\"@id\":\"https:\/\/nownodes.io\/blog\/#\/schema\/person\/0890ec68e813adecb93c18ee00e1e7a8\"},\"headline\":\"How to Implement Meta Transactions With ERC-2771\",\"datePublished\":\"2026-09-08T11:09:56+00:00\",\"dateModified\":\"2026-09-08T11:09:57+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/\"},\"wordCount\":1920,\"commentCount\":2,\"publisher\":{\"@id\":\"https:\/\/nownodes.io\/blog\/#organization\"},\"articleSection\":[\"Dev Report\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/\",\"url\":\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/\",\"name\":\"ERC-2771 Implementation Guide: Meta Transactions Step by Step\",\"isPartOf\":{\"@id\":\"https:\/\/nownodes.io\/blog\/#website\"},\"datePublished\":\"2026-09-08T11:09:56+00:00\",\"dateModified\":\"2026-09-08T11:09:57+00:00\",\"description\":\"A hands-on ERC-2771 tutorial: make a contract forwarder-aware, sign and relay a meta transaction, choose a relayer, and avoid the mistakes that have broken real deployments.\",\"breadcrumb\":{\"@id\":\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Blog\",\"item\":\"https:\/\/nownodes.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Dev Report\",\"item\":\"https:\/\/nownodes.io\/blog\/category\/dev-report\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"How to Implement Meta Transactions With ERC-2771\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/nownodes.io\/blog\/#website\",\"url\":\"https:\/\/nownodes.io\/blog\/\",\"name\":\"NOWNodes Blog\",\"description\":\"Your first-to-go source of development guides, web3 analytics and most recent news about NOWNodes\",\"publisher\":{\"@id\":\"https:\/\/nownodes.io\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/nownodes.io\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/nownodes.io\/blog\/#organization\",\"name\":\"NOWNodes Blog\",\"url\":\"https:\/\/nownodes.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/nownodes.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2024\/02\/cropped-New-Logo-NN.png\",\"contentUrl\":\"https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2024\/02\/cropped-New-Logo-NN.png\",\"width\":1164,\"height\":1164,\"caption\":\"NOWNodes Blog\"},\"image\":{\"@id\":\"https:\/\/nownodes.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/twitter.com\/nownodes\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/nownodes.io\/blog\/#\/schema\/person\/0890ec68e813adecb93c18ee00e1e7a8\",\"name\":\"\u0410nastasia\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/nownodes.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/1de24ab8dcdd7ec30f6adaf78b56bc1eda421f87575b7e103c8fc3fc4420e833?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/1de24ab8dcdd7ec30f6adaf78b56bc1eda421f87575b7e103c8fc3fc4420e833?s=96&d=mm&r=g\",\"caption\":\"\u0410nastasia\"},\"url\":\"https:\/\/nownodes.io\/blog\/author\/nasty-nownodes\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"ERC-2771 Implementation Guide: Meta Transactions Step by Step","description":"A hands-on ERC-2771 tutorial: make a contract forwarder-aware, sign and relay a meta transaction, choose a relayer, and avoid the mistakes that have broken real deployments.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/","og_locale":"en_US","og_type":"article","og_title":"ERC-2771 Implementation Guide: Meta Transactions Step by Step","og_description":"A hands-on ERC-2771 tutorial: make a contract forwarder-aware, sign and relay a meta transaction, choose a relayer, and avoid the mistakes that have broken real deployments.","og_url":"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/","og_site_name":"NOWNodes Blog","article_published_time":"2026-09-08T11:09:56+00:00","article_modified_time":"2026-09-08T11:09:57+00:00","og_image":[{"width":2400,"height":1200,"url":"https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2026\/08\/nodes_1-42.jpg","type":"image\/jpeg"}],"author":"\u0410nastasia","twitter_card":"summary_large_image","twitter_creator":"@nownodes","twitter_site":"@nownodes","twitter_misc":{"Written by":"\u0410nastasia","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/#article","isPartOf":{"@id":"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/"},"author":{"name":"\u0410nastasia","@id":"https:\/\/nownodes.io\/blog\/#\/schema\/person\/0890ec68e813adecb93c18ee00e1e7a8"},"headline":"How to Implement Meta Transactions With ERC-2771","datePublished":"2026-09-08T11:09:56+00:00","dateModified":"2026-09-08T11:09:57+00:00","mainEntityOfPage":{"@id":"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/"},"wordCount":1920,"commentCount":2,"publisher":{"@id":"https:\/\/nownodes.io\/blog\/#organization"},"articleSection":["Dev Report"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/","url":"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/","name":"ERC-2771 Implementation Guide: Meta Transactions Step by Step","isPartOf":{"@id":"https:\/\/nownodes.io\/blog\/#website"},"datePublished":"2026-09-08T11:09:56+00:00","dateModified":"2026-09-08T11:09:57+00:00","description":"A hands-on ERC-2771 tutorial: make a contract forwarder-aware, sign and relay a meta transaction, choose a relayer, and avoid the mistakes that have broken real deployments.","breadcrumb":{"@id":"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/nownodes.io\/blog\/what-are-meta-transactions-erc-2771\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Blog","item":"https:\/\/nownodes.io\/blog"},{"@type":"ListItem","position":2,"name":"Dev Report","item":"https:\/\/nownodes.io\/blog\/category\/dev-report"},{"@type":"ListItem","position":3,"name":"How to Implement Meta Transactions With ERC-2771"}]},{"@type":"WebSite","@id":"https:\/\/nownodes.io\/blog\/#website","url":"https:\/\/nownodes.io\/blog\/","name":"NOWNodes Blog","description":"Your first-to-go source of development guides, web3 analytics and most recent news about NOWNodes","publisher":{"@id":"https:\/\/nownodes.io\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/nownodes.io\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/nownodes.io\/blog\/#organization","name":"NOWNodes Blog","url":"https:\/\/nownodes.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/nownodes.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2024\/02\/cropped-New-Logo-NN.png","contentUrl":"https:\/\/nownodes.io\/blog\/wp-content\/uploads\/2024\/02\/cropped-New-Logo-NN.png","width":1164,"height":1164,"caption":"NOWNodes Blog"},"image":{"@id":"https:\/\/nownodes.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/twitter.com\/nownodes"]},{"@type":"Person","@id":"https:\/\/nownodes.io\/blog\/#\/schema\/person\/0890ec68e813adecb93c18ee00e1e7a8","name":"\u0410nastasia","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/nownodes.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/1de24ab8dcdd7ec30f6adaf78b56bc1eda421f87575b7e103c8fc3fc4420e833?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1de24ab8dcdd7ec30f6adaf78b56bc1eda421f87575b7e103c8fc3fc4420e833?s=96&d=mm&r=g","caption":"\u0410nastasia"},"url":"https:\/\/nownodes.io\/blog\/author\/nasty-nownodes"}]}},"modified_by":"\u0410nastasia","_links":{"self":[{"href":"https:\/\/nownodes.io\/blog\/wp-json\/wp\/v2\/posts\/3103","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nownodes.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nownodes.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nownodes.io\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/nownodes.io\/blog\/wp-json\/wp\/v2\/comments?post=3103"}],"version-history":[{"count":3,"href":"https:\/\/nownodes.io\/blog\/wp-json\/wp\/v2\/posts\/3103\/revisions"}],"predecessor-version":[{"id":3268,"href":"https:\/\/nownodes.io\/blog\/wp-json\/wp\/v2\/posts\/3103\/revisions\/3268"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nownodes.io\/blog\/wp-json\/wp\/v2\/media\/3105"}],"wp:attachment":[{"href":"https:\/\/nownodes.io\/blog\/wp-json\/wp\/v2\/media?parent=3103"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nownodes.io\/blog\/wp-json\/wp\/v2\/categories?post=3103"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nownodes.io\/blog\/wp-json\/wp\/v2\/tags?post=3103"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}