Skip to main content
Testnet native EVM gas sponsorship is available on every plan. Mainnet sponsorship requires the Enterprise plan or Dynamic enabling it for your environment. Contact Dynamic to enable mainnet sponsorship.
With the default EVM gas sponsorship flow, the client signs a sponsored transaction and submits it in one step. That’s the fastest path, but the decision to sponsor lives entirely in the SDK. This recipe moves that decision to your backend. The user signs the transaction on the client, but instead of submitting it, the client hands the signed transaction to your server. Your server applies whatever policy you want, per user or per transaction, and only then relays it. You decide exactly what gets sponsored and for whom. Common reasons to do this:
  • Sponsor gas only for specific actions (e.g. a card top-up, a first purchase) and reject everything else.
  • Sponsor gas only for specific users (e.g. a plan tier, a verified account, a region).
  • Apply per-user budgets or rate limits before spending sponsored gas.
The example below uses the JavaScript client SDK to sign and the Node server SDK to relay. This is not limited to these SDKs. The same pattern works with any Dynamic client SDK for signing and any Dynamic server SDK for relaying.

How it works

  1. Client signs the transaction and sends the signed bundle to your API.
  2. Your backend validates the transaction and the user, then relays it through Dynamic.
  3. Dynamic submits the transaction on-chain and the user pays no gas.

1. Restrict sponsorship to your server

First, make sure testnet EVM gas sponsorship is enabled for your environment: in the Dynamic Console, go to Sponsor Gas → Dynamic and enable Sponsor Network Fees (EVM) and Sponsor on testnets (with the EVM chains you want to sponsor enabled). Then, in the same card, turn on Restrict sponsorship to server-side requests. The one toggle applies to both testnet and mainnet sponsorship. With it on, only your server can submit sponsored transactions. Requests sent directly by end users are rejected. This controls who can submit a sponsored transaction, not who signs it: users still sign on the client exactly as before.

2. Sign on the client

Call signSponsoredTransaction to produce a signed transaction bundle without submitting it, then send that bundle to your backend along with the user’s Dynamic user id. This example signs a single USDC transfer on Base, the same transaction your backend validates in the next step.
Idempotent retries: if the request to your backend fails or times out and the client retries, calling signSponsoredTransaction again with a fresh nonce signs an independent intent. If the original request actually went through, the retry can get sponsored too. Generate the nonce once, keep it for the lifetime of that logical request, and pass the same one on every retry. The 7702 contract marks a nonce used the moment its intent executes, so a retried intent with the same nonce is rejected on-chain instead of sponsoring the action twice.

3. Validate on your backend

Your API receives the signed bundle. This is the shape to expect:
When you relay, Dynamic already verifies that the wallet belongs to this user in your environment, caps the batch at 5 calls, and checks that the EIP-7702 authorization (when present) targets the correct delegate contract. The checks below are your business policy on top of that.
Before relaying, you can apply your own rules, for example checking what the transaction does and who the user is. The example below validates a single USDC-on-Base transfer to an allowed recipient.

4. Relay from your backend

Authenticate the Node SDK with your environment API token, then relay the signed bundle. Pass the userId so the relay is attributed to that end user.
You then have two ways to return the result to the client.

Option A: wait on the server, return the transaction hash

sendSponsoredTransaction relays and waits for the transaction to land on-chain, then returns the hash. Simplest when your client just wants the final hash.

Option B: return a request id, wait on the client

relaySponsoredTransaction relays without waiting and returns a requestId. Return it to the client and let the client poll for the hash. This is useful for keeping your request short or driving a progress UI.
On the client, wait for the hash with waitForSponsoredTransaction:
Last modified on October 29, 2026