Smart embedded wallets are in Private Beta. You can email us or join our slack community to get access!

Dynamic integrates with multiple account abstraction providers so you can choose your optimal setup to convert wallets into smart contract wallets, leveraging EIP-4337 (the official account abstraction specification).

What can you do with a smart contract wallet?

Smart wallets offer a myriad of benefits. Four specific ones are:

  1. Gas fee sponsorship - you can choose to allow your users to conduct “gasless” transactions that will be sponsored by you.
  2. Transaction bundling - you can collect a single signature which is used for a bundle of transactions vs having to ask a user for signature on every independent action.
  3. Sophisticated recovery and transfer options - because the wallet is a smart contract, you can define sophisticated recovery methods for the wallet signer, as well as options to transfer ownership from one private key owner to another.
  4. Session keys - you can create session keys with permissions to send specific transactions for your users, so that you can execute transactions without asking your users to sign, and even execute transactions while they are offline.

How does smart wallet creation work?

Once a user logs in with email and an embedded wallet is created for them, a key pair is generated for that wallet. The key pair is then used to generate a smart contract wallet that uses the private key as the owner. In essence, you use the private key from the embedded wallet to control a more sophisticated smart wallet.

The resulting wallet can be interacted with using the same standard interface as native wallets, with the ability to add features such as gas sponsorship, session keys and batched transactions.

Getting started with Smart Wallets

ZeroDev + embedded wallets

Initial setup

The first iteration of account abstraction from Dynamic uses ZeroDev and embedded wallets. This guide will walk you through settting up ZeroDev and Dynamic so that you can sponsor transactions on Polygon Mumbai.

1

ZeroDev Account

Sign up for a free account at https://dashboard.zerodev.app/ and create a project

Configure your project name and network

Note that the network you select in your ZeroDev project will be the network that the smart contract wallet is deployed on and cannot be changed without creating a new project. For this guide, we will be using Polygon Mumbai.

Copy your ZeroDev project id

2

Enable in Dynamic

In your Dynamic Dashboard, go to Configurations > EVM

And toggle on Polygon Mumbai and click Save

Now, go to Configurations > Account Abstraction

Enable ZeroDev and paste in your ZeroDev project id

3

Enable Dynamic Embedded wallets + Email

  • In your Dynamic Dashboard, go to Configurations > Email/Social Auth & Embedded Wallets
  • Enable Dynamic Embedded wallets and Enable Email sign up (optionally, Enable social sign up and configure oauth)

Note that we currently only create smart wallets for embedded wallets. You will see native web3 wallets in your Dynamic widget, and can still use one to sign in, but only new embedded wallets will have a smart wallet.

4

Render Dynamic

In your local React project, install the following packages:

  • @dynamic-labs/sdk-react-core@alpha
  • @dynamic-labs/ethereum-aa@alpha
  • @dynamic-labs/ethereum@alpha

Then import and add the ZeroDevSmartWalletConnectors and EthereumWalletConnectors to the walletConnectors prop on DynamicContextProvider.

Minimum SDK version ^1.0.0-alpha.8

  import { DynamicContextProvider, DynamicWidget } from "@dynamic-labs/sdk-react-core";
  import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
  import { ZeroDevSmartWalletConnectors } from "@dynamic-labs/ethereum-aa";

  const App = () => (
    <DynamicContextProvider
      settings={{
        environmentId: "YOUR_ENVIRONMENT_ID",
        walletConnectors: [
          EthereumWalletConnectors,
          ZeroDevSmartWalletConnectors
        ],
      }}
    >
      <DynamicWidget />
    </DynamicContextProvider>
  )

  export default App;

Make sure to grab your Dynamic environment id from from the Dynamic Dashboard under Developer > SDK & API Keys, and replace it in the environmentID setting.

5

Set up a gas sponsorship rule

Now we will set up a basic gas sponsorship policy in the ZeroDev dashboard

In the Gas Policies tab, click on the button labeled “New” in the Project Policies section

Select “Amount” as the Type, “10” as the value and “Day” as the interval. This is saying that we will sponsor up to 10 MATIC total per day

Click Create Project Policy, and that’s it! Now start your app, log in and try sending some MATIC out. The Dynamic SDK will check if a transaction meets your gas policies and will automatically hide the gas in the transaction confirmation step if the gas is sponsored, if not the gas will be displayed.

Since you will be creating a brand new wallet when you log in, you will have to first send some MATIC to this address. You can access a free MATIC faucet here: https://mumbaifaucet.com/

6

Send a transaction

Run your app, and if you copied our snippet from earlier, you should see this basic page

Click Connect your wallet, enter your email and hit Continue. After pasting in your OTP, you are prompted to Set up a passkey.

Go ahead and set up your passkey, and now you’re fully logged in! Next, we’re going to send a transaction. To do that, we will need some MATIC. Grab your wallet address by clicking on the Dynamic Widget, then click on the three dots next to your address and hit “Copy wallet address”. You can paste your address into free Polygon Mumbai faucet which will deposit some free test MATIC into your account. After doing so, if you refresh your app, you should see your balance update

Optionally, set up fiat onramp by following our guide here: https://docs.dynamic.xyz/fiat-onboarding/banxa

Now, send yourself some MATIC by clicking on the Send button in the Dynamic Widget. Enter 0.1 as the amount, and an addres of your choosing as the recipient, then hit Send now. You will see a screen like the following. Notice that there is no gas estimate field, because this transaction will be sponsored!

Hit confirm, sign for the transaction with your passkey. Congratulations, you just sent a gas-sponsored transaction! If you take your smart wallet address and paste it into the Polygon Mumbai scanner, you will see your smart wallet and the transaction you just sent.

Further Configuration

Now you are set up, you can utilize the full functionality of ZeroDev inside Dynamic - everything from session keys to gas policies. Learn more in the ZeroDev Docs.

Using with Viem & Ethers

You can use viem or ethers with account abstraction to sign messages or send sponsored transaction with no extra configuration, it also works with our wagmi integration.

What’s next

Once you’ve tested things out and want to deploy to a live network, you will need to do the following:

  1. Add your credit card to ZeroDev under Account Settings > Billing
  2. Create a new ZeroDev project and select a live network
  3. Copy your new ZeroDev project id and paste it into your Dynamic Dashboard a. We recommend using your Dynamic Sandbox environment for testing your testnet setup, and using your Dynamic Live environment for production.

Examples

Get smart wallet address vs signer address

The wallet connector will return your smart wallet address, that address will be used in the Dynamic UI and is the main address you will interact with. But you can fetch the signer address by using the wallet connector getEOAConnector and then fetching the address there.

import { useEffect, useState } from "useEffect";
import {
  useDynamicContext,
  DynamicContextProvider,
  DynamicWidget,
} from "@dynamic-labs/sdk-react-core";
import {
  isZeroDevConnector,
  ZeroDevSmartWalletConnectors,
} from "@dynamic-labs/ethereum-aa";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";

const SignerAddress = () => {
  const { primaryWallet } = useDynamicContext();
  const [signerAddress, setSignerAddress] = useState();

  useEffect(() => {
    if (!primaryWallet) {
      return;
    }

    const {
      connector,
      address, // This is your smart wallet address
    } = primaryWallet;

    if (!isZeroDevConnector(connector)) {
      return;
    }

    const signerConnector = connector.getEOAConnector();

    if (!signerConnector) {
      return;
    }

    // This is the signer address
    const [address] = signerConnector.fetchPublicAddress();

    setSignerAddress(address);
  }, [primaryWallet]);

  return <span>My Signer address: {signerAddress}</span>;
};

const App = () => (
  <DynamicContextProvider
    settings={{
      environmentId: "YOUR_ENVIRONMENT_ID",
      walletConnectors: [
        EthereumWalletConnectors,
        ZeroDevSmartWalletConnectors,
      ],
    }}
  >
    <DynamicWidget />

    <SignerAddress />
  </DynamicContextProvider>
);

export default App;

Using ZeroDev ECDSAProvider

To do a batched transaction or session key user operations, you can use the ZeroDev ECDSAProvider directly from the wallet connector.

import {
  useDynamicContext,
  DynamicContextProvider,
  DynamicWidget,
} from "@dynamic-labs/sdk-react-core";
import {
  isZeroDevConnector,
  ZeroDevSmartWalletConnectors,
} from "@dynamic-labs/ethereum-aa";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";

const contractABI = [
  {
    inputs: [{ internalType: "address", name: "_to", type: "address" }],
    name: "mint",
    outputs: [],
    stateMutability: "nonpayable",
    type: "function",
  },
];

const contractAddress = "0x34bE7f35132E97915633BC1fc020364EA5134863";

const BatchMint = () => {
  const { primaryWallet } = useDynamicContext();

  const mint = async () => {
    if (!primaryWallet) {
      return;
    }

    const { connector, address } = primaryWallet;

    // Use the isZeroDevConnector type guard to check for the ZeroDevConnector
    if (!isZeroDevConnector(connector)) {
      return;
    }

    // Export the ZeroDev ECDSAProvider from the connector
    const ecdsaProvider = connector.getAccountAbstractionProvider();

    if (!ecdsaProvider) {
      return;
    }

    // Now the ecdsaProvider can be used to perform user operations

    const { hash } = await ecdsaProvider.sendUserOperation([
      {
        data: encodeFunctionData({
          abi: contractABI,
          args: [address],
          functionName: "mint",
        }),
        target: contractAddress,
      },
      // ... add your additional transactions here
    ]);
  };

  if (!primaryWallet) return null;

  return <button onClick={mint}>mint</button>;
};

const App = () => (
  <DynamicContextProvider
    settings={{
      environmentId: "YOUR_ENVIRONMENT_ID",
      walletConnectors: [
        EthereumWalletConnectors,
        ZeroDevSmartWalletConnectors,
      ],
    }}
  >
    <DynamicWidget />

    <BatchMint />
  </DynamicContextProvider>
);

export default App;

For more information about the ECDSAProvider, go to ZeroDev documentation on choosing a validator

FAQ