Skip to Content
TutorialsDeploy a Smart Wallet

Deploy a Smart Wallet

Create and deploy your first EmbarkAI smart wallet.

In this tutorial you will create a new MPC wallet using the DKG protocol, understand how counterfactual addresses work, and trigger on-chain deployment by sending the first transaction.

Prerequisites

How Wallet Creation Works

When you call createWallet(), the SDK runs the DKLS23 Distributed Key Generation (DKG) protocol:

  1. The client and EmbarkAI TSS server perform a 3-round cryptographic protocol
  2. Each party generates its own keyshare — the full private key is never assembled
  3. A joint public key is computed and used to derive the owner EOA address
  4. The smart account address is computed deterministically from the owner address and the factory contract

No on-chain transaction occurs during wallet creation. The smart account address is a counterfactual address — it is known in advance but the contract is not yet deployed.

Step 1: Install the SDK

npm install @embarkai/core

Step 2: Create the Wallet Manager

import { createServerWalletManager, MemoryKeyshareStorage, } from '@embarkai/core' const storage = new MemoryKeyshareStorage() const manager = createServerWalletManager({ apiKey: process.env.EMBARK_API_KEY!, chainId: 2030232745, // Lumia Testnet storage, })

Step 3: Create a Wallet

Call createWallet with a unique identifier. This runs the DKG protocol and stores the client keyshare:

const wallet = await manager.createWallet('treasury-wallet') console.log('Owner (EOA):', wallet.ownerAddress) console.log('Smart Account:', wallet.smartAccountAddress)

The smartAccountAddress is the counterfactual address. You can receive funds at this address immediately, even before the smart contract is deployed.

Step 4: Fund the Wallet

Before sending the first transaction, the wallet needs native tokens for gas (unless you have a Paymaster configured).

Transfer tokens to the smartAccountAddress from a faucet or another wallet:

  • Lumia Testnet faucet: Check the Lumia documentation  for testnet faucets
  • Sepolia faucet: Use any public Sepolia faucet

Step 5: Send the First Transaction (Triggers Deployment)

The first UserOperation automatically includes the initCode that deploys the smart account contract on-chain:

const userOpHash = await wallet.sendUserOperation( '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', '10000000000000000', // 0.01 tokens ) const receipt = await wallet.waitForUserOperationReceipt(userOpHash) console.log('Deployed + sent in TX:', receipt.transactionHash)

This single transaction both deploys the smart account contract and executes the transfer. Subsequent transactions skip the deployment step.

Step 6: Verify on Block Explorer

Check the transaction on a block explorer to confirm the deployment:

  • Lumia Testnet: https://testnet.lumia.org/tx/{transactionHash}
  • Lumia Mainnet: https://explorer.lumia.org/tx/{transactionHash}

You should see the contract creation event alongside the token transfer in the same transaction.

Complete Code

import { createServerWalletManager, MemoryKeyshareStorage, } from '@embarkai/core' async function main() { const manager = createServerWalletManager({ apiKey: process.env.EMBARK_API_KEY!, chainId: 2030232745, // Lumia Testnet storage: new MemoryKeyshareStorage(), }) // Create a new wallet (runs DKG protocol) const wallet = await manager.createWallet('treasury-wallet') console.log('Owner EOA:', wallet.ownerAddress) console.log('Smart Account:', wallet.smartAccountAddress) // Fund the smart account address, then send the first transaction const userOpHash = await wallet.sendUserOperation( '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', '10000000000000000', // 0.01 tokens ) const receipt = await wallet.waitForUserOperationReceipt(userOpHash) if (receipt.success) { console.log('Wallet deployed and transaction confirmed!') console.log('TX:', receipt.transactionHash) } } main().catch(console.error)

Understanding Counterfactual Addresses

The smart account address is deterministic and computed from:

  • The owner address (derived from the MPC public key)
  • The factory contract address (chain-specific)
  • A salt value

This means the address is known before deployment and is the same across environments using the same parameters. Funds sent to this address are safe and accessible once the contract is deployed.

Next Steps

Last updated on