> ## 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

> Sponsor EVM transaction fees for your users with Dynamic's gas sponsorship feature for embedded wallets.

<Card title="Recommended: JavaScript SDK for React Native" icon="react" color="#4779FE">
  While this SDK is still supported, we recommend using newer [JavaScript SDK](/docs/javascript/reference/react-native-quickstart), which is optimized for React Native, but also comes with a host of other benefits.
</Card>

EVM Gas Sponsorship lets your users send EVM transactions without paying gas. Dynamic relays the transaction on their behalf and uses EIP-7702 delegation to execute batched calls from the user's embedded wallet.

<Note>
  EVM Gas Sponsorship is available exclusively for **V3 MPC embedded wallets**. [Contact us](https://www.dynamic.xyz/talk-to-us) to enable it for your project.
</Note>

## How it works

A sponsored transaction is a batch of `{ target, data, value }` calls that Dynamic submits on-chain on the user's behalf:

1. The user's embedded wallet signs an EIP-712 intent that authorizes the calls and binds them to a relayer address and a deadline.
2. The first time a wallet is sponsored, the SDK also signs an EIP-7702 authorization to delegate the EOA to Dynamic's gasless delegation contract. After activation, the delegation persists on-chain and is reused on subsequent transactions.
3. Dynamic's relayer submits the transaction and reports a `pending` → `submitted` → `success` (or `failure`) lifecycle. The on-chain transaction hash is available once the relay reports `submitted`.

## Prerequisites

Before you start, make sure you have:

* A React Native app set up with the [React Native quickstart](/docs/react-native/reference/quickstart).
* V3 MPC embedded wallets enabled in **Settings > Embedded Wallets** in the [Dynamic dashboard](https://app.dynamic.xyz).
* EVM Gas Sponsorship enabled for your environment. [Contact us](https://www.dynamic.xyz/talk-to-us) to turn it on.
* The wallet's active network is one of the [supported chains](#supported-chains) below.

## Supported chains

Dynamic operates relayers on the following EVM chains.

**Mainnet**

| Chain            | Chain ID |
| ---------------- | -------- |
| Ethereum Mainnet | `1`      |
| Base             | `8453`   |
| Optimism         | `10`     |
| Arbitrum One     | `42161`  |
| BNB Smart Chain  | `56`     |
| Robinhood Chain  | `4663`   |

**Testnet**

| Chain            | Chain ID   |
| ---------------- | ---------- |
| Ethereum Sepolia | `11155111` |
| Base Sepolia     | `84532`    |

## Setup

### Install the extension

<Tabs>
  <Tab title="expo">
    ```bash theme={"system"}
    npx expo install @dynamic-labs/ethereum-gasless-extension
    ```
  </Tab>

  <Tab title="npm">
    ```bash theme={"system"}
    npm install @dynamic-labs/ethereum-gasless-extension
    ```
  </Tab>

  <Tab title="yarn">
    ```bash theme={"system"}
    yarn add @dynamic-labs/ethereum-gasless-extension
    ```
  </Tab>

  <Tab title="pnpm">
    ```bash theme={"system"}
    pnpm add @dynamic-labs/ethereum-gasless-extension
    ```
  </Tab>

  <Tab title="bun">
    ```bash theme={"system"}
    bun add @dynamic-labs/ethereum-gasless-extension
    ```
  </Tab>
</Tabs>

### Extend the Dynamic client

Add `EthereumGaslessExtension()` to the extension chain on your existing `createClient()` call. This adds an `ethereumGasless` namespace to the client.

```typescript client.ts theme={"system"}
import { createClient } from '@dynamic-labs/legacy-client';
import { ReactNativeExtension } from '@dynamic-labs/legacy-react-native-extension';
import { ViemExtension } from '@dynamic-labs/legacy-viem-extension';
import { EthereumGaslessExtension } from '@dynamic-labs/ethereum-gasless-extension';

export const dynamicClient = createClient({
  environmentId: 'YOUR_ENVIRONMENT_ID',
  appName: 'My App',
  appOrigin: 'https://YOUR-DOMAIN.com',
})
  .extend(ReactNativeExtension())
  .extend(ViemExtension())
  .extend(EthereumGaslessExtension());
```

## Check if sponsorship is enabled

Call `isEnabled` to confirm that sponsorship is turned on for your environment. Use this to gate UI before showing a "Send gasless" button.

```typescript theme={"system"}
import { dynamicClient } from './client';

const enabled = await dynamicClient.ethereumGasless.isEnabled();
```

## Send a sponsored transaction

`send` signs the intent, hands it to the relayer, and resolves once the transaction is included on-chain. The `calls` array supports batches — every entry runs atomically.

```tsx theme={"system"}
import { useReactiveClient } from '@dynamic-labs/legacy-react-hooks';
import { parseEther } from 'viem';

import { dynamicClient } from './client';

function SendGasless() {
  const { wallets } = useReactiveClient(dynamicClient);
  const wallet = wallets.primary;

  const send = async () => {
    if (!wallet || wallet.chain !== 'EVM') return;

    const { transactionHash } = await dynamicClient.ethereumGasless.send({
      wallet,
      calls: [
        {
          target: '0xRecipient...',
          data: '0x',
          value: parseEther('0.01'),
        },
      ],
    });

    console.log('Transaction hash:', transactionHash);
  };

  return <Button title="Send (gasless)" onPress={send} disabled={!wallet} />;
}
```

### Call shape

Each entry in `calls` describes a single call inside the sponsored batch:

| Field    | Type                | Description                                                 |
| -------- | ------------------- | ----------------------------------------------------------- |
| `target` | `` `0x${string}` `` | Contract or recipient address to execute the call against.  |
| `data`   | `` `0x${string}` `` | Hex-encoded calldata. Use `0x` for a plain native transfer. |
| `value`  | `bigint`            | Native token amount (in wei) to send with the call.         |

For contract calls, encode `data` with `encodeFunctionData` from viem:

```typescript theme={"system"}
import { encodeFunctionData, parseUnits } from 'viem';

const ERC20_TRANSFER_ABI = [
  {
    inputs: [
      { name: 'to', type: 'address' },
      { name: 'amount', type: 'uint256' },
    ],
    name: 'transfer',
    outputs: [{ name: '', type: 'bool' }],
    stateMutability: 'nonpayable',
    type: 'function',
  },
] as const;

const transferCall = {
  target: '0xTokenAddress...',
  data: encodeFunctionData({
    abi: ERC20_TRANSFER_ABI,
    functionName: 'transfer',
    args: ['0xRecipient...', parseUnits('1', 6)],
  }),
  value: 0n,
};
```

### Options

Pass these alongside `wallet` and `calls`:

| Option            | Default | Description                                                                                                                                                                                                                                         |
| ----------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `autoDelegate`    | `true`  | When `true`, the SDK signs an EIP-7702 authorization if the wallet is not already delegated. Set to `false` if you manage delegation yourself.                                                                                                      |
| `authorization`   | —       | Pre-signed EIP-7702 authorization. Takes priority over `autoDelegate`.                                                                                                                                                                              |
| `validForSeconds` | `600`   | How long the signed intent stays valid before the relayer rejects it.                                                                                                                                                                               |
| `nonce`           | —       | Reuse a specific bitmap nonce (`bigint`, as returned on the signed intent) instead of letting the SDK generate one. Sign several intents with the same nonce so at most one can land on-chain (cancel-replace). Used as-is, with no on-chain check. |

## Split signing and relaying

For retry, batching, or custom UI flows, you can split the steps:

* `sign` returns the signed intent without contacting the relayer.
* `relay` sends a signed intent (or signs and sends in one step) and returns a `requestId`.
* `waitFor` polls a `requestId` until it resolves to an on-chain transaction.
* `getStatus` does a one-shot status read for custom polling.

```typescript theme={"system"}
const wallet = dynamicClient.wallets.primary;

// Sign now, relay later
const signed = await dynamicClient.ethereumGasless.sign({ wallet, calls });

const { requestId } = await dynamicClient.ethereumGasless.relay({
  wallet,
  calls,
});

const { transactionHash } = await dynamicClient.ethereumGasless.waitFor({
  requestId,
});

// Or read the current status without polling
const status = await dynamicClient.ethereumGasless.getStatus({ requestId });
```

`status.status` is one of `pending`, `submitted`, `success`, or `failure`. `status.transactionHash` is populated once the relay reports `submitted`; `status.errorMessage` is populated on `failure`.

## EIP-7702 delegation

The first sponsored transaction from a given wallet activates EIP-7702 delegation to Dynamic's gasless contract. By default `send` and `relay` handle this for you via `autoDelegate: true`. If you want to manage delegation explicitly — for example, to surface an "Enable gasless" button before the first transaction — use these methods directly.

### Check delegation status

```typescript theme={"system"}
const wallet = dynamicClient.wallets.primary;

const isDelegated =
  await dynamicClient.ethereumGasless.is7702DelegationActive({ wallet });
```

Results are cached per wallet and chain.

### Sign a 7702 authorization

`sign7702Authorization` returns a signed authorization without broadcasting anything. Pass it to a later `send`, `relay`, or `activate7702Delegation` call.

```typescript theme={"system"}
const wallet = dynamicClient.wallets.primary;

const authorization =
  await dynamicClient.ethereumGasless.sign7702Authorization({
    wallet,
    // Defaults to the wallet's active network chain ID.
    // chainId: 1,
  });
```

### Activate delegation explicitly

`activate7702Delegation` sends a sponsored transaction whose only purpose is to activate the delegation on-chain. After it resolves, subsequent sponsored transactions skip the authorization step.

```typescript theme={"system"}
const wallet = dynamicClient.wallets.primary;

// Sign now, activate later
const authorization =
  await dynamicClient.ethereumGasless.sign7702Authorization({ wallet });

const { transactionHash } =
  await dynamicClient.ethereumGasless.activate7702Delegation({
    wallet,
    authorization,
  });

// Or have the SDK sign and activate in one call
await dynamicClient.ethereumGasless.activate7702Delegation({ wallet });
```

## Error handling

Sponsorship failures throw — there is no silent fallback. Wrap calls in `try`/`catch` and surface a useful message to the user:

```typescript theme={"system"}
try {
  const { transactionHash } = await dynamicClient.ethereumGasless.send({
    wallet,
    calls,
  });
} catch (error) {
  // The relay returned a terminal failure, polling timed out, or the API errored.
  console.error('Sponsored transaction failed:', error);
}
```

Common failure causes:

* EVM Gas Sponsorship is not enabled for the environment.
* The wallet is not a V3 MPC embedded wallet.
* The wallet's active network is not supported by the relayer.
* The signed intent expired (`validForSeconds` elapsed before the relayer ran).
* A terminal on-chain failure on the relayed transaction.

## Limitations

| Limitation      | Details                                                                                                              |
| --------------- | -------------------------------------------------------------------------------------------------------------------- |
| Wallet type     | V3 MPC embedded wallets only. External wallets and legacy embedded wallets are not supported.                        |
| Chain           | EVM networks where Dynamic has enabled a relayer. Use `isEnabled` and `is7702DelegationActive` to gate UI per chain. |
| Batch size      | Calls execute atomically inside one sponsored transaction — the whole batch reverts if any call fails.               |
| Intent lifetime | The signed intent expires after `validForSeconds` (default 600). Sign close to the time you relay.                   |
