Quick Start: Backend Integration
Create and manage server-side smart wallets from your Node.js backend using the @embarkai/core package.
Prerequisites
- Node.js 18+
- An EmbarkAI API key from dashboard.lumiapassport.com
Installation
Minimal Working Example
import { createServerWalletManager, MemoryKeyshareStorage } from '@embarkai/core'
const manager = createServerWalletManager({
apiKey: process.env.EMBARK_API_KEY!,
storage: new MemoryKeyshareStorage(),
chainId: 2030232745, // Lumia Beam testnet
})
// Create a wallet (runs TSS protocol automatically)
const wallet = await manager.createWallet('treasury-main')
console.log('Smart wallet address:', wallet.smartAccountAddress)
// Send a transaction
const userOpHash = await wallet.sendUserOperation(
'0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', // recipient
'1000000000000000000', // 1 LUMIA in wei
'0x', // calldata (empty)
'standard', // fee type
)
console.log('UserOperation hash:', userOpHash)
// Wait for on-chain confirmation
const receipt = await wallet.waitForUserOperationReceipt(userOpHash)
console.log('Transaction hash:', receipt.transactionHash)
console.log('Success:', receipt.success)What Just Happened?
- API key authenticated — The
createServerWalletManageruses your project API key to authenticate with EmbarkAI’s TSS service. - MPC wallet created —
createWallet()runs the DKLS23 distributed key generation protocol. Your backend holds one keyshare; EmbarkAI holds the other. No single party ever has the full private key. - UserOperation built and signed —
sendUserOperation()constructs an ERC-4337 UserOperation, signs it using a two-party TSS signing round, and submits it to the bundler. - Bundler executed on-chain — The bundler packages the UserOperation and submits it to the EntryPoint contract. The paymaster sponsors the gas so your wallet does not need native tokens for fees.
Storage Options
MemoryKeyshareStorage is fine for testing but loses keyshares on restart. For production, use encrypted file storage or a custom adapter:
File Storage (Node.js)
import { createServerWalletManager } from '@embarkai/core'
import { FileKeyshareStorage } from '@embarkai/core/clients/node'
const storage = new FileKeyshareStorage({
storageDir: './data/keyshares',
encryptionPassword: process.env.EMBARK_KEYSHARE_PASSWORD!,
})
await storage.init()
const manager = createServerWalletManager({
apiKey: process.env.EMBARK_API_KEY!,
storage,
chainId: 2030232745,
})Custom Storage
Implement the KeyshareStorage interface for AWS KMS, HashiCorp Vault, or any other backend:
interface KeyshareStorage {
set(userId: string, keyshare: string): Promise<void> | void
get(userId: string): Promise<string | null> | string | null
has(userId: string): Promise<boolean> | boolean
list(): Promise<string[]> | string[]
}Warning: Keyshares are irreplaceable. If you lose a keyshare, the wallet and its funds are permanently inaccessible. Always use encrypted storage with redundant backups in production.
Smart Contract Interactions
The SDK provides encoding helpers for common contract calls:
import {
createServerWalletManager,
MemoryKeyshareStorage,
encodeERC20Transfer,
} from '@embarkai/core'
const manager = createServerWalletManager({
apiKey: process.env.EMBARK_API_KEY!,
storage: new MemoryKeyshareStorage(),
chainId: 2030232745,
})
const wallet = await manager.getWallet('treasury-main')
// Encode an ERC20 transfer
const callData = await encodeERC20Transfer(
'0xRecipientAddress',
1000n * 10n ** 18n, // 1000 tokens
)
// Send it as a UserOperation
const userOpHash = await wallet.sendUserOperation(
'0xTokenContractAddress', // to = token contract
'0', // value = 0 (no native token)
callData, // encoded transfer call
'standard',
)Available encoding helpers: encodeERC20Transfer, encodeERC20Approve, encodeERC721TransferFrom, encodeERC721SafeTransferFrom, encodeERC1155SafeTransferFrom, encodeContractCall (generic).
Next Steps
- Server Wallets — multi-chain setup, batch operations, keyshare backup/restore
- Bundler Operations — low-level UserOperation submission and receipt polling