# Creating a Smart Account (/onboarding/create-a-smart-account)

> For the complete documentation index, see [llms.txt](/llms.txt)



<Callout type="info">
  Impatient?  Check out [a complete example here](https://github.com/zerodevapp/zerodev-examples/blob/main/create-account/main.ts).
</Callout>

At the core of account abstraction is the *smart account* -- an account powered by a smart contract.  ZeroDev is built on [Kernel](https://github.com/zerodevapp/kernel), a *modular smart account* that can be customized with plugins.

When you create a Kernel account, you set it up with a *validator*, which is a type of plugin that handles how the account validates UserOps.  In this tutorial, we will be using the ECDSA validator, which works like a normal EOA by validating signatures from a ECDSA private key.  ZeroDev supports other validators such as [passkeys](/onboarding/passkeys/overview) and [multisig](/advanced/multisig).

We will be using a local private key, but the ECDSA validator also works with [third-party auth providers](/onboarding/auth-providers).

## Installation [#installation]

<Tabs items="[&#x22;npm&#x22;,&#x22;yarn&#x22;,&#x22;pnpm&#x22;,&#x22;bun&#x22;]">
  <Tab value="npm">
    ```bash
    npm i @zerodev/sdk @zerodev/ecdsa-validator
    ```
  </Tab>

  <Tab value="yarn">
    ```bash
    yarn add @zerodev/sdk @zerodev/ecdsa-validator
    ```
  </Tab>

  <Tab value="pnpm">
    ```bash
    pnpm i @zerodev/sdk @zerodev/ecdsa-validator
    ```
  </Tab>

  <Tab value="bun">
    ```bash
    bun add @zerodev/sdk @zerodev/ecdsa-validator
    ```
  </Tab>
</Tabs>

## API [#api]

<div className="fd-steps">
  <div className="fd-step">
    ### Picking an EntryPoint [#picking-an-entrypoint]

    Currently there are two versions of ERC-4337 that are used in production.  They are referred to as "EntryPoint 0.6" and "EntryPoint 0.7", where "EntryPoint" refers to the singleton ERC-4337 contract.

    If you are building a new application, we recommend using EntryPoint 0.7 (Kernel v3), which gives you the latest and greatest features and optimizations.  If you already have an application using EntryPoint 0.6 (Kernel v2), just stick with it -- it will be supported indefinitely.

    In this tutorial, we will use EntryPoint 0.7.  Start by selecting an EntryPoint:

    ```ts
    const entryPoint = getEntryPoint("0.7")
    ```
  </div>

  <div className="fd-step">
    ### Picking a Kernel version [#picking-a-kernel-version]

    [Kernel](https://github.com/zerodevapp/kernel) is the smart account that ZeroDev builds on.  ZeroDev SDK used to implicitly use the latest version of Kernel, which has caused some compatibility issues when people upgrade the SDK.  Therefore, starting from ZeroDev SDK v5.3, we require that you explicitly specify the Kernel version.  This is how you generally should choose:

    * If you had already been in production with ZeroDev SDK v4 or lower, use Kernel version 2.4 with EntryPoint 0.6.
    * If you had already been in production with ZeroDev SDK v5, use Kernel version 3.0 with EntryPoint 0.7.
    * If you are still in development or starting a new project, use Kernel version 3.1 with EntryPoint 0.7.

    ```ts
    import { KERNEL_V3_1 } from "@zerodev/sdk/constants"

    const kernelVersion = KERNEL_V3_1
    ```
  </div>

  <div className="fd-step">
    ### Creating a public client [#creating-a-public-client]

    In Viem, a [public client](https://viem.sh/docs/clients/public.html) is an interface to a JSON-RPC API such as Infura or Alchemy.

    ```typescript
    import { createPublicClient, http } from "viem"
    import { base } from 'viem/chains'

    const publicClient = createPublicClient({
      // In production, you will want to set your RPC provider here (e.g. Infura/Alchemy).
      transport: http(),
      chain: base,
    })
    ```
  </div>

  <div className="fd-step">
    ### Creating a signer [#creating-a-signer]

    As aforementioned, a Kernel account using a ECDSA validator is "owned" by a signer, which is anything that can sign messages with a private key.

    Since Kernel is built on top of Viem, we can use any [Viem account](https://viem.sh/docs/accounts/local/toAccount) as the signer.  In this example, we create a signer from a private key:

    ```typescript
    import { Hex } from "viem"
    import { privateKeyToAccount } from "viem/accounts"

    const signer = privateKeyToAccount("PRIVATE_KEY" as Hex)
    ```

    Replace `PRIVATE_KEY` with an actual private key.  You can [generate a random one here](https://privatekeys.pw/keys/ethereum/random).
  </div>

  <div className="fd-step">
    ### Creating a ECDSA validator [#creating-a-ecdsa-validator]

    Then create a ECDSA validator from the signer:

    ```typescript
    import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator"

    const ecdsaValidator = await signerToEcdsaValidator(publicClient, {
      signer,
      entryPoint,
      kernelVersion
    })
    ```
  </div>

  <div className="fd-step">
    ### Create a Kernel account [#create-a-kernel-account]

    Next, create a Kernel account with the ECDSA validator:

    ```typescript
    import { createKernelAccount } from "@zerodev/sdk"

    const account = await createKernelAccount(publicClient, {
      plugins: {
        sudo: ecdsaValidator,
      },
      entryPoint,
      kernelVersion
    })
    ```
  </div>

  <div className="fd-step">
    ### Create an account client [#create-an-account-client]

    Now that we have an account, we can finally construct an "account client," which is the equivalent of a [wallet client in Viem](https://viem.sh/docs/clients/wallet.html) that allows you to send UserOps to bundlers.

    ```typescript
    import { createKernelAccountClient } from "@zerodev/sdk"
    import { http } from "viem"
    import { base } from 'viem/chains'

    const kernelClient = createKernelAccountClient({
      account,

      // Replace with your chain
      chain: base,

      // Find the RPC in your ZeroDev dashboard
      bundlerTransport: http('ZERODEV_RPC'),

      // Required - the public client
      client: publicClient,

      // Optional -- only if you want to use a paymaster
      paymaster: {
        getPaymasterData(userOperation) {
          return paymasterClient.sponsorUserOperation({userOperation})
        }  
      },
    })
    ```

    Note that:

    * You need to replace the `ZERODEV_RPC` with the RPC found on your ZeroDev dashboard.
    * You need to make sure to set the right `chain`.
    * `paymaster` only needs to be specified if you want to [use a paymaster](/smart-accounts/sponsor-gas/evm).

    Now you are ready to do things with your smart account, like [sending UserOps](/smart-accounts/send-transactions)!
  </div>
</div>

## FAQs [#faqs]

<Accordions>
  <Accordion title="When I create an account, is it deployed on-chain?">
    No.  If your account hasn't been deployed yet, we simply use [`CREATE2`](https://eips.ethereum.org/EIPS/eip-1014) to compute the address that the account *would* be deployed to.  Your account is deployed automatically with the first UserOp it sends.

    In other words, "creating" accounts with the SDK is free -- you can create an infinite number of such account objects without paying any gas.  It's only when you send the first UserOp that the account is deployed automatically.
  </Accordion>

  <Accordion title="Can I create multiple accounts from the same EOA signer?">
    Yes, you can do so by providing an `index` when you create the account object.

    ```typescript
    import { createKernelAccount } from "@zerodev/sdk"

    const account = createKernelAccount(publicClient, {
      // other options...

      // optionally specify the index; different indexes will yield different accounts
      index: 1,
    })
    ```
  </Accordion>

  <Accordion title="How do I get the smart account address from the EOA signer address?">
    Sometimes you only know the address of the EOA signer but you don't have the signer itself.  In that case, you can still compute the address of the smart account with this helper function:

    ```ts
    import { getKernelAddressFromECDSA } from "@zerodev/ecdsa-validator"

    // index is 0 by default
    const smartAccountAddress = await getKernelAddressFromECDSA(publicClient, eoaAddress, index)
    ```
  </Accordion>

  <Accordion title="How do I get the EOA signer address from the smart account address?">
    You can query it on-chain like this:

    ```ts
    import { getValidatorAddress } from "@zerodev/ecdsa-validator"

    const ecdsaValidatorContract = getContract({
      abi: [
        {
          type: "function",
          name: "ecdsaValidatorStorage",
          inputs: [{ name: "", type: "address", internalType: "address" }],
          outputs: [{ name: "owner", type: "address", internalType: "address" }],
          stateMutability: "view",
        },
      ],
      address: getValidatorAddress(entryPoint, kernelVersion),
      client: publicClient,
    })

    const owner = await ecdsaValidatorContract.read.ecdsaValidatorStorage([
      account.address,
    ])
    ```
  </Accordion>

  <Accordion title="How do I create a Kernel account object with a specific address?">
    Normally, you don't need to manually specify an address because the smart account address is computed from your signer data.  However, if you have changed the signer, then you may need to manually specify the smart account address.

    You can do it like this:

    ```ts
    const account = await createKernelAccount(publicClient, {
      address: "address",
      // ...other args
    })
    ```
  </Accordion>
</Accordions>
