> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Adding EVM Extensions

> Add one or more EVM extensions to support embedded wallets and external wallets on EVM chains

> [!NOTE]
>
> **What you need to know:** Add the EVM extension from the [Chains](https://docs.dynamic.xyz/docs/javascript/reference/adding-extensions.md#chains) table on Adding extensions. You need at least one EVM extension to support Ethereum-compatible chains. There are only two kinds of wallet: **embedded** and **external**. Each extension below lets you support one or both for EVM.

To interact with EVM wallets, add one or more EVM extensions to your client. Each extension enables a different way to discover and connect EVM wallets (e.g. EIP-6963, `window.ethereum`, or Dynamic embedded wallets). Enable the EVM chain in the [Dynamic Console](https://console.dynamic.xyz/dashboard/chains-and-networks#evm) as well.

## Installation

```bash theme={"system"}
npm install @dynamic-labs-sdk/evm
```

## Default EVM extension

If you wish to support the standard EVM wallets and Dynamic embedded wallets,
you can add the default EVM extension to your client using the `addEvmExtension` method.

```javascript theme={"system"}
import { createDynamicClient } from '@dynamic-labs-sdk/client';
import { addEvmExtension } from '@dynamic-labs-sdk/evm';

const dynamicClient = createDynamicClient({
  ...
});

addEvmExtension();

```

## Standalone EVM extensions

If you want to be more granular, you can add the standalone EVM extensions individually to your client.

### EIP-6963 extension

```javascript theme={"system"}
import { createDynamicClient } from '@dynamic-labs-sdk/client';
import { addEIP6963Extension } from '@dynamic-labs-sdk/evm/eip6963';

const dynamicClient = createDynamicClient({
  ...
});

// this will add support for wallets that implement EIP-6963, which are most of the EVM external wallets
addEIP6963Extension();

```

### Window-injected extension

When users open your app inside a wallet’s built-in browser (e.g. MetaMask Mobile, Trust Wallet, Coinbase Wallet),
the wallet injects its provider into `window.ethereum` rather than announcing via EIP-6963. Use this
extension as a fallback to support those environments:

```javascript theme={"system"}
import { createDynamicClient } from '@dynamic-labs-sdk/client';
import { addEvmWindowInjectedExtension } from '@dynamic-labs-sdk/evm/window-injected';

const dynamicClient = createDynamicClient({
  ...
});

// this will add support for wallets injected into window.ethereum (built-in browser or legacy extensions)
addEvmWindowInjectedExtension();

```

When used alongside `addEIP6963Extension`, EIP-6963 providers take priority automatically — so if a
wallet supports both, the EIP-6963 version is used.

### MetaMask EVM extension

Add the MetaMask EVM extension to support MetaMask SDK pairing through a desktop QR code or SDK-managed mobile handoff. Keep `addEvmExtension` so installed MetaMask browser extensions are discovered through EIP-6963.

```javascript theme={"system"}
import { addEvmExtension } from '@dynamic-labs-sdk/evm';
import { addMetaMaskEvmExtension } from '@dynamic-labs-sdk/evm/metamask';

addEvmExtension();
addMetaMaskEvmExtension();
```

See the [MetaMask Connection Guide](https://docs.dynamic.xyz/docs/javascript/reference/wallets/metamask-connection-guide.md) for extension, QR code, mobile web, and React Native flows.

### WAAS EVM extension

```javascript theme={"system"}
import { createDynamicClient } from '@dynamic-labs-sdk/client';
import { addWaasEvmExtension } from '@dynamic-labs-sdk/evm/waas';

const dynamicClient = createDynamicClient({
  ...
});

// this will add support for Dynamic embedded EVM wallets
addWaasEvmExtension();

```

### Base Account extension

Adds support for [Base Account](https://docs.base.org/identity/smart-wallet/introduction/sw-quickstart) — Coinbase's popup / QR flow that surfaces both **Coinbase Smart Wallet** (passkey-backed ERC-4337 contract wallet at `keys.coinbase.com`) and **Coinbase Wallet (EOA)**. It complements, rather than replaces, the EIP-6963 and window-injected paths, which continue to handle the Coinbase Wallet browser extension.

```javascript theme={"system"}
import { createDynamicClient } from '@dynamic-labs-sdk/client';
import { addBaseAccountEvmExtension } from '@dynamic-labs-sdk/evm/base-account';

const dynamicClient = createDynamicClient({
  ...
});

// this will add Base Account (Coinbase Smart Wallet + EOA via popup/QR)
addBaseAccountEvmExtension();

```

Pass `preference.options` to control which Base Account flavor(s) appear in the popup:

| Value | Behavior |
| - | - |
| `'all'` (default) | Show both Smart Wallet and EOA options. Use this when in doubt. |
| `'smartWalletOnly'` | Only **Coinbase Smart Wallet** — a passkey-backed ERC-4337 contract wallet onboarded via Face ID / fingerprint / Yubikey, with no seed phrase. Best for first-time onchain users. |
| `'eoaOnly'` | Only **Coinbase Wallet (EOA)** — the standard private-key Coinbase Wallet. Best when the user already has Coinbase Wallet installed. |

```javascript theme={"system"}
import { addBaseAccountEvmExtension } from '@dynamic-labs-sdk/evm/base-account';
addBaseAccountEvmExtension({
  preference: { options: 'smartWalletOnly' },
});
```

See the [Base Account Connection Guide](https://docs.dynamic.xyz/docs/javascript/reference/wallets/base-account-connection-guide.md) for connect flows and React Native setup (Mobile Wallet Protocol).

## Tempo

Tempo is EVM compatible but resolves its networks from a separate registry and has no native token, so it ships in its own package rather than under `@dynamic-labs-sdk/evm`.

```bash theme={"system"}
npm install @dynamic-labs-sdk/tempo
```

See [Tempo](https://docs.dynamic.xyz/docs/javascript/reference/tempo/overview.md) for `addTempoExtension` and the Tempo wallet helpers.

## Combining extensions

You can combine as many extensions as you want to support all the wallets you want to support.

```javascript theme={"system"}
import { createDynamicClient } from '@dynamic-labs-sdk/client';
import { addEIP6963Extension } from '@dynamic-labs-sdk/evm/eip6963';
import { addWaasEvmExtension } from '@dynamic-labs-sdk/evm/waas';

const dynamicClient = createDynamicClient({
  ...
});

addEIP6963Extension();
addWaasEvmExtension();

```

## React

In React, register extensions at module level in the same file where you create the client — not inside components. This runs once when the module is first imported.

```tsx theme={"system"}
// dynamicClient.ts
import { createDynamicClient } from '@dynamic-labs-sdk/client';
import { addEIP6963Extension } from '@dynamic-labs-sdk/evm/eip6963';
import { addWaasEvmExtension } from '@dynamic-labs-sdk/evm/waas';

export const dynamicClient = createDynamicClient({
  environmentId: 'YOUR_ENVIRONMENT_ID',
});

addEIP6963Extension();
addWaasEvmExtension();
```

Then in your app entry point, import the client to trigger the module:

```tsx theme={"system"}
// main.tsx
import './dynamicClient';
import { initializeClient } from '@dynamic-labs-sdk/client';

void initializeClient();
```

## React Native

Complete [Setup on Bare React Native](https://docs.dynamic.xyz/docs/javascript/react-native/bare-react-native.md) or [Setup on Expo](https://docs.dynamic.xyz/docs/javascript/react-native/expo.md) before adding an EVM extension — this section only covers a Metro-specific issue those guides don't.

### Resolve Metro bundling errors

`@dynamic-labs-sdk/evm` uses [viem](https://viem.sh) internally, and viem depends on [`isows`](https://github.com/wevm/isows) for its WebSocket transport. `isows` imports the Node-only `ws` package unconditionally, even though it only uses `ws` on Node — on React Native it picks the platform's native `WebSocket` at runtime instead. Metro still has to resolve `ws` at bundle time, and `ws` isn't Node-compatible: `ws/lib/stream.js` requires Node's `stream` module (and transitively `net`/`tls`/`zlib`), none of which exist under Hermes, so the bundle fails with an error like `Unable to resolve module stream`.

> [!NOTE]
>
> This applies whenever `@dynamic-labs-sdk/evm` is in your dependency tree on React Native — regardless of which extension you use, and whether you use `viem` directly yourself. If you also use `@dynamic-labs-sdk/solana`, you may separately need [`rpc-websockets` version overrides](https://docs.dynamic.xyz/docs/get-started/troubleshooting/react/cannot-resolve-rpc-websockets) — that's an unrelated resolution issue in a different package.

Fix it by aliasing `ws` to a shim in `metro.config.js`. The only difference between Expo and bare React Native is which package provides `getDefaultConfig`:

> [tab] Bare React Native
>
> ```js metro.config.js theme={"system"}
> const { getDefaultConfig } = require('@react-native/metro-config');
> const path = require('path');
>
> const config = getDefaultConfig(__dirname);
>
> const wsShimPath = path.resolve(__dirname, 'shims/ws.js');
> const upstreamResolveRequest = config.resolver.resolveRequest;
>
> config.resolver.resolveRequest = (context, moduleName, platform) => {
>   if (moduleName === 'ws') {
>     return { type: 'sourceFile', filePath: wsShimPath };
>   }
>   if (upstreamResolveRequest) {
>     return upstreamResolveRequest(context, moduleName, platform);
>   }
>   return context.resolveRequest(context, moduleName, platform);
> };
>
> module.exports = config;
> ```

> [tab] Expo
>
> ```js metro.config.js theme={"system"}
> const { getDefaultConfig } = require('expo/metro-config');
> const path = require('path');
>
> const config = getDefaultConfig(__dirname);
>
> const wsShimPath = path.resolve(__dirname, 'shims/ws.js');
> const upstreamResolveRequest = config.resolver.resolveRequest;
>
> config.resolver.resolveRequest = (context, moduleName, platform) => {
>   if (moduleName === 'ws') {
>     return { type: 'sourceFile', filePath: wsShimPath };
>   }
>   if (upstreamResolveRequest) {
>     return upstreamResolveRequest(context, moduleName, platform);
>   }
>   return context.resolveRequest(context, moduleName, platform);
> };
>
> module.exports = config;
> ```

The shim only needs to answer to the shape `isows` calls — a `WebSocket` constructor — so subclass React Native's built-in global rather than reimplementing it:

```js shims/ws.js theme={"system"}
// Subclassing (rather than re-exporting global.WebSocket directly) avoids
// mutating React Native's built-in WebSocket constructor; `instanceof
// global.WebSocket` still holds for the shim.
class WsShim extends global.WebSocket {}

module.exports = WsShim;
module.exports.WebSocket = WsShim;
module.exports.default = WsShim;
```

> [!TIP]
>
> Using [WalletConnect](https://docs.dynamic.xyz/docs/javascript/reference/wallets/walletconnect-integration.md#react-native) on React Native? Set up this `ws` alias first, then check the WalletConnect guide for its other requirements. MetaMask does not use this WalletConnect flow. See the [MetaMask Connection Guide](https://docs.dynamic.xyz/docs/javascript/reference/wallets/metamask-connection-guide.md#react-native).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.