Skip to Content
TutorialsSend Your First Transaction

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

Step 1: Install the SDK

npm install @embarkai/core

Step 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: MemoryKeyshareStorage is 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

IssueSolution
Keyshare not foundEnsure the wallet was created with the same storage instance
Insufficient fundsFund the smart account address with native tokens or configure a Paymaster
UserOperation revertedCheck that the recipient address is valid and the value does not exceed the balance
Timeout waiting for receiptIncrease maxPollTimeMs or check network status

Next Steps

Last updated on