Skip to main content

Documentation Index

Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt

Use this file to discover all available pages before exploring further.

EVM Gas Sponsorship lets you sponsor EVM transaction fees for embedded wallet users. Use sendSponsoredTransaction to send a sponsored transaction and wait for the on-chain hash, or compose the lower-level functions for custom flows.
EVM Gas Sponsorship is available exclusively for V3 MPC embedded wallets. It uses EIP-7702 delegation to relay a batch of calls through a Dynamic-operated relayer contract.

Overview

When you call sendSponsoredTransaction, the SDK:
  1. Builds an EIP-712 intent describing the batch of calls and a deadline
  2. Signs the intent with the user’s embedded wallet (and, if delegation is needed, an EIP-7702 authorization for the wallet’s EOA)
  3. Relays the signed intent to Dynamic’s sponsorship backend
  4. Polls the relayer until the request reaches a terminal state, then returns the on-chain transaction hash
If sponsorship fails for any reason — terminal relay status, timeout, or unsupported wallet — a SponsorTransactionError is thrown. There is no silent fallback.

Enabling EVM Gas Sponsorship

  1. Go to the Dynamic Dashboard
  2. Navigate to Settings > Embedded Wallets
  3. Ensure the EVM chains you want to sponsor are enabled
  4. Toggle on EVM Gas Sponsorship

Usage

Use sendSponsoredTransaction with a batch of calls to sign, relay, and wait for the transaction in a single call:
import { sendSponsoredTransaction } from '@dynamic-labs-sdk/evm';
import { parseEther } from 'viem';

const sendSponsoredTx = async (walletAccount, recipientAddress) => {
  const { transactionHash } = await sendSponsoredTransaction({
    walletAccount,
    calls: [
      {
        target: recipientAddress,
        data: '0x',
        value: parseEther('0.01'),
      },
    ],
  });
  console.log('Sponsored transaction confirmed:', transactionHash);
};
Each call in the batch has the same shape:
FieldTypeDescription
targetHexTarget contract address.
dataHexHex-encoded calldata to execute on the target. Use 0x for a plain native-token transfer.
valuebigintAmount of native token (in wei) to send with the call.

Splitting sign and send

Reuse a pre-signed intent (for example, to send it from a different process) by calling signSponsoredTransaction first and passing the result to sendSponsoredTransaction:
import {
  signSponsoredTransaction,
  sendSponsoredTransaction,
} from '@dynamic-labs-sdk/evm';

const signedTransaction = await signSponsoredTransaction({
  walletAccount,
  calls,
});

const { transactionHash } = await sendSponsoredTransaction({ signedTransaction });
By default the signed intent is valid for 10 minutes. Override with validForSeconds:
const signedTransaction = await signSponsoredTransaction({
  walletAccount,
  calls,
  validForSeconds: 60,
});

EIP-7702 delegation

Before a wallet can submit a sponsored transaction, its EOA must be delegated to the Dynamic relayer contract via an EIP-7702 authorization. The SDK ships three helpers that cover the full lifecycle without dropping into viem.

Checking delegation status

Use is7702DelegationActive to check whether delegation is already active on the wallet’s current network. Results are cached per wallet + chain in an in-memory registry, so repeated calls are cheap:
import { is7702DelegationActive } from '@dynamic-labs-sdk/evm';

const isActive = await is7702DelegationActive({ walletAccount });

Signing an authorization

sign7702Authorization signs an EIP-7702 authorization for the Dynamic delegation contract using the wallet’s active network (or an explicit chainId override). Pass the result to signSponsoredTransaction or sendSponsoredTransaction to attach it to the next sponsored call:
import {
  sign7702Authorization,
  signSponsoredTransaction,
} from '@dynamic-labs-sdk/evm';

const authorization = await sign7702Authorization({ walletAccount });

const signedTransaction = await signSponsoredTransaction({
  walletAccount,
  calls,
  authorization,
});

Activating delegation up-front

For flows that want delegation persisted on-chain before the first user-facing sponsored transaction (e.g. during onboarding), call activate7702Delegation. It sends an empty sponsored transaction that only carries the EIP-7702 authorization, then returns the on-chain transaction hash. Subsequent sponsored transactions no longer need to include an authorization:
import {
  activate7702Delegation,
  is7702DelegationActive,
} from '@dynamic-labs-sdk/evm';

if (!(await is7702DelegationActive({ walletAccount }))) {
  const { transactionHash } = await activate7702Delegation({ walletAccount });
  console.log('Delegation activated:', transactionHash);
}
You can also pass a pre-signed authorization to activate7702Delegation to reuse one obtained from sign7702Authorization.
When sendSponsoredTransaction is called on a wallet that has not been delegated yet, the SDK signs an EIP-7702 authorization automatically and attaches it to the first call — these helpers are only needed when you want explicit control over the delegation step.

Polling the relay status yourself

For custom UI (e.g. a progress bar across SUBMITTEDCONFIRMED), call getEVMSponsoredTransactionStatus directly on the requestId returned by relaySponsoredTransaction:
import {
  relaySponsoredTransaction,
  getEVMSponsoredTransactionStatus,
} from '@dynamic-labs-sdk/evm';

const { requestId } = await relaySponsoredTransaction({ walletAccount, calls });

const { status, transactionHash, errorMessage } =
  await getEVMSponsoredTransactionStatus({ requestId });
status is one of PENDING, SENT, SUBMITTED, CONFIRMED, FAILED, CANCELED, EXPIRED, or REJECTED. transactionHash is set once the relay broadcasts the transaction. For the common “wait until done” case, use waitForSponsoredTransaction — it polls every 2 seconds and resolves on CONFIRMED (timeout: 60s):
import { waitForSponsoredTransaction } from '@dynamic-labs-sdk/evm';

const { transactionHash } = await waitForSponsoredTransaction({ requestId });

Error Handling

Sponsorship failures throw a SponsorTransactionError. This error is thrown when:
  • The sponsorship API rejects the request (e.g. sponsorship not enabled, chain not supported, paymaster limit hit)
  • The relay reaches a terminal failure status (FAILED, CANCELED, EXPIRED, REJECTED)
  • waitForSponsoredTransaction times out after 60 seconds
  • The wallet provider does not support sponsored transactions (e.g. external wallets)
import {
  sendSponsoredTransaction,
  SponsorTransactionError,
} from '@dynamic-labs-sdk/evm';

const sendTransaction = async (walletAccount, calls) => {
  try {
    const { transactionHash } = await sendSponsoredTransaction({
      walletAccount,
      calls,
    });
    return { success: true, transactionHash };
  } catch (error) {
    if (error instanceof SponsorTransactionError) {
      return {
        success: false,
        error: 'Gas sponsorship failed',
      };
    }

    return { success: false, error: error.message };
  }
};

React

Use sendSponsoredTransaction inside a button handler, with useWalletAccounts from @dynamic-labs-sdk/react-hooks to reactively get the embedded wallet:
import {
  sendSponsoredTransaction,
  isEvmWalletAccount,
  SponsorTransactionError,
} from '@dynamic-labs-sdk/evm';
import { useWalletAccounts } from '@dynamic-labs-sdk/react-hooks';
import { useState } from 'react';
import { parseEther } from 'viem';

function SponsoredSendButton({ recipientAddress }) {
  const walletAccounts = useWalletAccounts();
  const walletAccount = walletAccounts.find(isEvmWalletAccount);
  const [transactionHash, setTransactionHash] = useState('');
  const [error, setError] = useState('');

  const handleSend = async () => {
    if (!walletAccount) return;
    setError('');
    try {
      const { transactionHash } = await sendSponsoredTransaction({
        walletAccount,
        calls: [
          { target: recipientAddress, data: '0x', value: parseEther('0.01') },
        ],
      });
      setTransactionHash(transactionHash);
    } catch (err) {
      if (err instanceof SponsorTransactionError) {
        setError('Gas sponsorship failed');
      }
    }
  };

  return (
    <div>
      <button onClick={handleSend} disabled={!walletAccount}>
        Send (Sponsored)
      </button>
      {transactionHash && <p>Hash: {transactionHash.slice(0, 20)}...</p>}
      {error && <p style={{ color: 'red' }}>{error}</p>}
    </div>
  );
}

Limitations

LimitationDetails
Wallet typeEmbedded wallets only (V3 MPC)
MechanismEIP-7702 delegation to the Dynamic relayer contract
Intent validity10 minutes by default; configurable via validForSeconds
Polling timeoutwaitForSponsoredTransaction resolves or throws within 60 seconds
BatchingOne signed intent per sendSponsoredTransaction call; each intent can contain multiple calls

Helpers

  • sign7702Authorization — sign an EIP-7702 authorization for the Dynamic delegation contract
  • is7702DelegationActive — check whether delegation is active for a wallet on its current network
  • activate7702Delegation — activate delegation on-chain via a single empty sponsored transaction