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
- Node.js 18+ installed
- An EmbarkAI API key from the Dashboard
- Basic understanding of Account Abstraction
How Wallet Creation Works
When you call createWallet(), the SDK runs the DKLS23 Distributed Key Generation (DKG) protocol:
- The client and EmbarkAI TSS server perform a 3-round cryptographic protocol
- Each party generates its own keyshare — the full private key is never assembled
- A joint public key is computed and used to derive the owner EOA address
- 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/coreStep 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
- Send Your First Transaction — Send a transfer from your deployed wallet
- Gasless Transactions — Deploy and transact without gas fees
- MPC Security — Understand the DKG protocol in depth
- Server Wallets — Full API reference for wallet management