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.
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. |
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:
- Builds an EIP-712 intent describing the batch of calls and a deadline
- Signs the intent with the user's embedded wallet (and, if delegation is needed, an EIP-7702 authorization for the wallet's EOA)
- Relays the signed intent to Dynamic's sponsorship backend
- 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.