send sponsored transaction.md

sendSponsoredTransaction

Send a gas-sponsored EVM transaction and wait for it to land on-chain.

sendSponsoredTransaction is the primary function for EVM gas sponsorship. It signs an intent for a batch of calls, relays it to Dynamic's sponsorship backend, and resolves once the relay reports the transaction has been included on-chain. The user pays no gas.

EVM Gas Sponsorship is an enterprise-only feature and works with **V3 MPC embedded wallets** only. See the [Overview](/docs/javascript/reference/evm/evm-gas-sponsorship) for setup.

Usage

import { sendSponsoredTransaction } from '@dynamic-labs-sdk/evm';
import { parseEther } from 'viem';

const { transactionHash } = await sendSponsoredTransaction({
  walletAccount,
  calls: [
    {
      target: recipientAddress, // who/what you're sending to
      data: '0x',               // '0x' = a plain native-token transfer
      value: parseEther('0.01'),
    },
  ],
});

console.log('Sponsored transaction confirmed:', transactionHash);

Parameters

sendSponsoredTransaction accepts one of two shapes: a set of unsigned calls to sign and send, or a signedTransaction you already produced with signSponsoredTransaction.

Signing and sending calls

Parameter Type Description
walletAccount EvmWalletAccount The V3 MPC embedded wallet to sign and send for.
calls SponsoredTransactionCall[] The batch of calls to execute (see below).
authorization SignAuthorizationReturnType (optional) A pre-signed EIP-7702 authorization. Takes priority over autoDelegate.
autoDelegate boolean (optional) Whether to auto-sign an EIP-7702 authorization when the wallet isn't delegated yet. Defaults to true.
nonce bigint (optional) A bitmap nonce to sign the intent with. When provided, nonce generation and its on-chain check are skipped. Useful for cancel-replace. See Reusing a nonce.
validForSeconds number (optional) How long the signed intent stays valid. Defaults to 600 (10 minutes).

Each entry in calls is a SponsoredTransactionCall:

Field Type Description
target Hex The address you're sending to (a recipient or a contract).
data Hex The calldata to run on the target. Use 0x for a plain native-token transfer.
value bigint Amount of native token (in wei) to send with the call. Use 0n for contract calls that don't move native token.

Sending a pre-signed transaction

Parameter Type Description
signedTransaction SignedSponsoredTransaction A payload produced by signSponsoredTransaction.
Both shapes take an optional final `client` argument — only needed if your app runs [multiple Dynamic clients](/docs/javascript/reference/client/create-dynamic-client).

Returns

Promise<{ transactionHash: Hex }> — the on-chain transaction hash, once the relay reports the transaction has been included. Under the hood the function relays the intent, then polls for up to 60 seconds (see waitForSponsoredTransaction).

How it works

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

EIP-7702 delegation is what lets a relayer submit a batch of calls on the user's behalf. The user's EOA is delegated, once, to a Dynamic-operated relayer contract.

Examples

Batch calls (multiple actions, atomically)

A single call can carry multiple entries — they execute together, all-or-nothing. This sends two USDC transfers to two addresses in one sponsored transaction:

import { sendSponsoredTransaction } from '@dynamic-labs-sdk/evm';
import { encodeFunctionData, erc20Abi, parseUnits } from 'viem';

// USDC on Base — an ERC-20 contract with 6 decimals
const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';

const { transactionHash } = await sendSponsoredTransaction({
  walletAccount,
  calls: [
    {
      target: USDC_ADDRESS,
      value: 0n,
      data: encodeFunctionData({
        abi: erc20Abi,
        functionName: 'transfer',
        args: [recipientA, parseUnits('5', 6)], // 5 USDC
      }),
    },
    {
      target: USDC_ADDRESS,
      value: 0n,
      data: encodeFunctionData({
        abi: erc20Abi,
        functionName: 'transfer',
        args: [recipientB, parseUnits('10', 6)], // 10 USDC
      }),
    },
  ],
});

Reusing an authorization

Pass a pre-signed authorization (from sign7702Authorization) to attach an explicit EIP-7702 authorization to the transaction instead of letting the SDK sign one automatically:

import {
  sign7702Authorization,
  sendSponsoredTransaction,
} from '@dynamic-labs-sdk/evm';

const authorization = await sign7702Authorization({ walletAccount });

const { transactionHash } = await sendSponsoredTransaction({
  walletAccount,
  calls,
  authorization,
});

Errors

If sponsorship can't go through, sendSponsoredTransaction throws a SponsorTransactionError. There is no silent fallback — if it throws, the transaction did not happen.

import {
  sendSponsoredTransaction,
  SponsorTransactionError,
} from '@dynamic-labs-sdk/evm';

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

A SponsorTransactionError is thrown when the sponsorship API rejects the request, the relay reports a terminal failure status, the request times out after 60 seconds, or the wallet doesn't support sponsored transactions. See Error handling for the full list.