Send Your First Transaction
Send a native token transfer using your smart wallet.
In this tutorial you will install the Core SDK, create a wallet manager, load a wallet, and send a native token transfer as a UserOperation. By the end, you will have a confirmed transaction on-chain.
Prerequisites
- Node.js 18+ installed
- An EmbarkAI API key from the Dashboard
- A wallet already created (see Deploy a Smart Wallet if you need one)
- Some native tokens in the wallet for gas (or a Paymaster configured)
Step 1: Install the SDK
npm install @embarkai/coreStep 2: Create the Wallet Manager
Set up a ServerWalletManager instance with your API key and target chain:
import {
createServerWalletManager,
MemoryKeyshareStorage,
} from '@embarkai/core'
const manager = createServerWalletManager({
apiKey: process.env.EMBARK_API_KEY!,
chainId: 2030232745, // Lumia Testnet
storage: new MemoryKeyshareStorage(),
})Note:
MemoryKeyshareStorageis suitable for development only. In production, use a persistent secure storage implementation. See Server Wallets for details.
Step 3: Load Your Wallet
Retrieve an existing wallet by its ID:
const wallet = await manager.getWallet('my-wallet')
console.log('Smart Account:', wallet.smartAccountAddress)If you have not created a wallet yet, use manager.createWallet('my-wallet') first. This runs the DKG protocol and stores the client keyshare.
Step 4: Send a Native Transfer
Use sendUserOperation to send native tokens. The value is specified in wei:
const recipientAddress = '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
const valueInWei = '100000000000000000' // 0.1 tokens
const userOpHash = await wallet.sendUserOperation(
recipientAddress,
valueInWei,
)
console.log('UserOperation hash:', userOpHash)The SDK constructs a UserOperation, signs it using the DKLS23 threshold protocol, and submits it to the Bundler.
Step 5: Wait for Confirmation
Poll for the transaction receipt:
const receipt = await wallet.waitForUserOperationReceipt(userOpHash, {
pollIntervalMs: 2000,
maxPollTimeMs: 60000,
})
if (receipt.success) {
console.log('Transaction confirmed!')
console.log('TX hash:', receipt.transactionHash)
console.log('Block:', receipt.blockNumber)
console.log('Gas used:', receipt.actualGasUsed)
} else {
console.error('Transaction failed')
}Step 6: Verify on Explorer
Open the transaction hash in a block explorer to confirm the transfer:
- Lumia Testnet:
https://testnet.lumia.org/tx/{transactionHash} - Lumia Mainnet:
https://explorer.lumia.org/tx/{transactionHash} - Sepolia:
https://sepolia.etherscan.io/tx/{transactionHash}
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(),
})
// Load an existing wallet
const wallet = await manager.getWallet('my-wallet')
console.log('Sending from:', wallet.smartAccountAddress)
// Send 0.1 native tokens
const userOpHash = await wallet.sendUserOperation(
'0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
'100000000000000000',
)
console.log('UserOp hash:', userOpHash)
// Wait for confirmation
const receipt = await wallet.waitForUserOperationReceipt(userOpHash)
if (receipt.success) {
console.log('Confirmed! TX:', receipt.transactionHash)
} else {
console.error('Failed')
}
}
main().catch(console.error)Troubleshooting
| Issue | Solution |
|---|---|
Keyshare not found | Ensure the wallet was created with the same storage instance |
Insufficient funds | Fund the smart account address with native tokens or configure a Paymaster |
UserOperation reverted | Check that the recipient address is valid and the value does not exceed the balance |
Timeout waiting for receipt | Increase maxPollTimeMs or check network status |
Next Steps
- Gasless Transactions — Send transactions without gas fees
- ERC-20 Transfers — Transfer ERC-20 tokens
- Server Wallets — Full API reference for wallet operations