Wallet Management
Secure wallet handling, private key management, and multi-wallet support
Wallet Management
The SEI provider offers comprehensive wallet management capabilities, from secure private key handling to multi-wallet support. This guide covers everything you need to know about managing wallets in your SEI AI agents.
AxiomSeiWallet Class
The AxiomSeiWallet
class is the core component for SEI wallet operations. It provides a secure, type-safe interface for blockchain interactions.
Basic Initialization
import { AxiomSeiWallet } from "@axiomkit/sei";
import { sei } from "viem/chains";
const wallet = new AxiomSeiWallet({
rpcUrl: "https://evm-rpc.sei-apis.com/",
privateKey: "0x...", // Your private key
chain: sei, // Optional: defaults to SEI mainnet
});
Configuration Options
interface AxiomSeiWalletConfig {
rpcUrl: string; // SEI RPC endpoint
privateKey: `0x${string}`; // Wallet private key
chain?: viemChains.Chain; // Network (optional)
}
Private Key Management
🔐 Security Best Practices
Never hardcode private keys in your source code!
// ❌ BAD - Never do this
const wallet = new AxiomSeiWallet({
rpcUrl: "https://evm-rpc.sei-apis.com/",
privateKey: "0x1234567890abcdef...", // Exposed in code!
});
// ✅ GOOD - Use environment variables
const wallet = new AxiomSeiWallet({
rpcUrl: process.env.SEI_RPC_URL!,
privateKey: process.env.SEI_PRIVATE_KEY as `0x${string}`,
});
Environment Variable Setup
Create a .env
file:
# .env
SEI_RPC_URL=https://evm-rpc.sei-apis.com/
SEI_PRIVATE_KEY=0x1234567890abcdef...
SEI_PRIVATE_KEY_2=0xabcdef1234567890... # For multi-wallet
Load with validation:
import { validateEnv } from "@axiomkit/core";
import { z } from "zod";
const env = validateEnv(
z.object({
SEI_RPC_URL: z.string().min(1),
SEI_PRIVATE_KEY: z.string().min(1),
SEI_PRIVATE_KEY_2: z.string().min(1).optional(),
})
);
const wallet = new AxiomSeiWallet({
rpcUrl: env.SEI_RPC_URL,
privateKey: env.SEI_PRIVATE_KEY as `0x${string}`,
});
Key Generation
For development, you can generate new private keys:
import { generatePrivateKey } from "viem/accounts";
// Generate a new private key
const privateKey = generatePrivateKey();
console.log("New private key:", privateKey);
// Create wallet with generated key
const wallet = new AxiomSeiWallet({
rpcUrl: "https://evm-rpc.sei-apis.com/",
privateKey,
});
Multi-Wallet Support
Managing Multiple Wallets
import { AxiomSeiWallet } from "@axiomkit/sei";
class MultiWalletManager {
private wallets: Map<string, AxiomSeiWallet> = new Map();
addWallet(name: string, config: AxiomSeiWalletConfig) {
const wallet = new AxiomSeiWallet(config);
this.wallets.set(name, wallet);
return wallet;
}
getWallet(name: string): AxiomSeiWallet | undefined {
return this.wallets.get(name);
}
getAllWallets(): AxiomSeiWallet[] {
return Array.from(this.wallets.values());
}
getWalletAddresses(): Record<string, string> {
const addresses: Record<string, string> = {};
for (const [name, wallet] of this.wallets) {
addresses[name] = wallet.walletAdress;
}
return addresses;
}
}
// Usage
const manager = new MultiWalletManager();
// Add wallets
manager.addWallet("main", {
rpcUrl: process.env.SEI_RPC_URL!,
privateKey: process.env.SEI_PRIVATE_KEY as `0x${string}`,
});
manager.addWallet("trading", {
rpcUrl: process.env.SEI_RPC_URL!,
privateKey: process.env.SEI_PRIVATE_KEY_2 as `0x${string}`,
});
// Use specific wallet
const mainWallet = manager.getWallet("main");
const tradingWallet = manager.getWallet("trading");
Multi-Wallet Agent Context
import { context, action } from "@axiomkit/core";
import { z } from "zod";
const multiWalletContext = context({
type: "multi-wallet",
schema: z.object({
walletName: z.string(),
}),
actions: [
action({
name: "switch-wallet",
description: "Switch to a different wallet",
schema: z.object({
walletName: z.string().describe("Name of the wallet to switch to"),
}),
handler: async (args, { memory }) => {
const wallet = manager.getWallet(args.walletName);
if (!wallet) {
return `Wallet '${args.walletName}' not found. Available wallets: ${Object.keys(manager.getWalletAddresses()).join(", ")}`;
}
memory.currentWallet = args.walletName;
memory.walletAddress = wallet.walletAdress;
return `Switched to wallet: ${args.walletName} (${wallet.walletAdress})`;
},
}),
action({
name: "list-wallets",
description: "List all available wallets",
schema: z.object({}),
handler: async () => {
const addresses = manager.getWalletAddresses();
const walletList = Object.entries(addresses)
.map(([name, address]) => `- ${name}: ${address}`)
.join("\n");
return `Available wallets:\n${walletList}`;
},
}),
],
});
Wallet Information
Getting Wallet Address
const wallet = new AxiomSeiWallet({
rpcUrl: "https://evm-rpc.sei-apis.com/",
privateKey: "0x...",
});
// Get the wallet address
const address = wallet.walletAdress;
console.log("Wallet address:", address);
Network Information
// Get current network
const chain = wallet.walletClient.chain;
console.log("Network:", chain.name);
console.log("Chain ID:", chain.id);
// Check if connected to correct network
if (chain.id !== 1329) { // SEI mainnet
console.warn("Not connected to SEI mainnet!");
}
Balance Management
Native SEI Balance
// Get native SEI balance
const balance = await wallet.getERC20Balance();
console.log(`SEI Balance: ${balance} SEI`);
ERC-20 Token Balance
// Get specific token balance
const usdcAddress = "0x3894085ef7ff0f0aedf52e2a2704928d1ec074f1"; // USDC on SEI
const usdcBalance = await wallet.getERC20Balance(usdcAddress);
console.log(`USDC Balance: ${usdcBalance} USDC`);
Balance Monitoring Action
action({
name: "monitor-balances",
description: "Monitor balances across multiple tokens",
schema: z.object({
tokens: z.array(z.string()).optional().describe("Token tickers to check (default: SEI)"),
}),
handler: async (args) => {
const tokens = args.tokens || ["SEI"];
const balances: Record<string, string> = {};
for (const token of tokens) {
if (token === "SEI") {
balances[token] = await wallet.getERC20Balance();
} else {
const tokenAddress = await wallet.getTokenAddressFromTicker(token);
if (tokenAddress) {
balances[token] = await wallet.getERC20Balance(tokenAddress);
} else {
balances[token] = "Token not found";
}
}
}
const balanceReport = Object.entries(balances)
.map(([token, balance]) => `${token}: ${balance}`)
.join("\n");
return `Current balances:\n${balanceReport}`;
},
}),
Transaction Management
Transaction History
import { createPublicClient, http } from "viem";
const publicClient = createPublicClient({
chain: sei,
transport: http(process.env.SEI_RPC_URL!),
});
// Get recent transactions
async function getRecentTransactions(address: string, limit = 10) {
// Note: This is a simplified example
// In practice, you'd use a block explorer API or indexer
const blockNumber = await publicClient.getBlockNumber();
// Get transactions from recent blocks
const transactions = [];
for (let i = 0; i < limit; i++) {
const block = await publicClient.getBlock({
blockNumber: blockNumber - BigInt(i),
});
const relevantTxs = block.transactions.filter(tx =>
tx.from === address || tx.to === address
);
transactions.push(...relevantTxs);
}
return transactions.slice(0, limit);
}
Transaction Status Monitoring
action({
name: "check-transaction",
description: "Check the status of a transaction",
schema: z.object({
txHash: z.string().describe("Transaction hash to check"),
}),
handler: async (args) => {
try {
const receipt = await wallet.publicClient.getTransactionReceipt({
hash: args.txHash as `0x${string}`,
});
if (receipt.status === "success") {
return `Transaction successful! Gas used: ${receipt.gasUsed}`;
} else {
return "Transaction failed or reverted";
}
} catch (error) {
return `Transaction not found or pending: ${error.message}`;
}
},
}),
Error Handling
Common Wallet Errors
class WalletError extends Error {
constructor(message: string, public code: string) {
super(message);
this.name = "WalletError";
}
}
// Enhanced error handling
async function safeWalletOperation<T>(
operation: () => Promise<T>,
errorContext: string
): Promise<T> {
try {
return await operation();
} catch (error) {
if (error instanceof Error) {
// Handle specific error types
if (error.message.includes("insufficient funds")) {
throw new WalletError("Insufficient balance for transaction", "INSUFFICIENT_FUNDS");
}
if (error.message.includes("gas")) {
throw new WalletError("Gas estimation failed", "GAS_ERROR");
}
if (error.message.includes("network")) {
throw new WalletError("Network connection failed", "NETWORK_ERROR");
}
}
throw new WalletError(`${errorContext}: ${error.message}`, "UNKNOWN_ERROR");
}
}
// Usage
const balance = await safeWalletOperation(
() => wallet.getERC20Balance(),
"Failed to get balance"
);
Advanced Features
Wallet Recovery
// Recover wallet from mnemonic (if you have the mnemonic)
import { mnemonicToAccount } from "viem/accounts";
function recoverWalletFromMnemonic(mnemonic: string, index = 0) {
const account = mnemonicToAccount(mnemonic, { addressIndex: index });
return new AxiomSeiWallet({
rpcUrl: process.env.SEI_RPC_URL!,
privateKey: account.key,
});
}
Wallet Backup
// Create wallet backup (encrypted)
import crypto from "crypto";
function createWalletBackup(wallet: AxiomSeiWallet, password: string) {
const privateKey = wallet.walletClient.account?.key;
if (!privateKey) throw new Error("No private key found");
const cipher = crypto.createCipher("aes-256-cbc", password);
let encrypted = cipher.update(privateKey, "utf8", "hex");
encrypted += cipher.final("hex");
return {
address: wallet.walletAdress,
encryptedKey: encrypted,
timestamp: new Date().toISOString(),
};
}
// Restore wallet from backup
function restoreWalletFromBackup(backup: any, password: string) {
const decipher = crypto.createDecipher("aes-256-cbc", password);
let decrypted = decipher.update(backup.encryptedKey, "hex", "utf8");
decrypted += decipher.final("utf8");
return new AxiomSeiWallet({
rpcUrl: process.env.SEI_RPC_URL!,
privateKey: decrypted as `0x${string}`,
});
}
Best Practices
1. Security
- Never commit private keys to version control
- Use environment variables or secure key management
- Consider using hardware wallets for production
- Implement proper access controls
2. Error Handling
- Always wrap wallet operations in try-catch blocks
- Provide meaningful error messages
- Implement retry logic for network operations
- Log errors for debugging
3. Performance
- Cache wallet instances when possible
- Use connection pooling for multiple wallets
- Implement rate limiting for API calls
- Monitor gas prices and network congestion
4. Testing
- Use testnet for development
- Test with small amounts first
- Implement comprehensive error scenarios
- Use mock wallets for unit tests
Example: Complete Wallet Manager
import { AxiomSeiWallet } from "@axiomkit/sei";
import { context, action } from "@axiomkit/core";
import { z } from "zod";
class SeiWalletManager {
private wallets = new Map<string, AxiomSeiWallet>();
constructor(private rpcUrl: string) {}
addWallet(name: string, privateKey: string) {
const wallet = new AxiomSeiWallet({
rpcUrl: this.rpcUrl,
privateKey: privateKey as `0x${string}`,
});
this.wallets.set(name, wallet);
return wallet;
}
getWallet(name: string) {
return this.wallets.get(name);
}
async getAllBalances() {
const balances: Record<string, Record<string, string>> = {};
for (const [name, wallet] of this.wallets) {
balances[name] = {
SEI: await wallet.getERC20Balance(),
};
}
return balances;
}
}
// Create context with wallet management
const walletManager = new SeiWalletManager(process.env.SEI_RPC_URL!);
const walletContext = context({
type: "wallet-manager",
schema: z.object({
currentWallet: z.string().optional(),
}),
actions: [
action({
name: "add-wallet",
description: "Add a new wallet to the manager",
schema: z.object({
name: z.string().describe("Name for the wallet"),
privateKey: z.string().describe("Private key for the wallet"),
}),
handler: async (args) => {
try {
const wallet = walletManager.addWallet(args.name, args.privateKey);
return `Wallet '${args.name}' added successfully. Address: ${wallet.walletAdress}`;
} catch (error) {
return `Failed to add wallet: ${error.message}`;
}
},
}),
action({
name: "get-all-balances",
description: "Get balances for all managed wallets",
schema: z.object({}),
handler: async () => {
const balances = await walletManager.getAllBalances();
const report = Object.entries(balances)
.map(([walletName, walletBalances]) => {
const balanceStr = Object.entries(walletBalances)
.map(([token, balance]) => `${token}: ${balance}`)
.join(", ");
return `${walletName}: ${balanceStr}`;
})
.join("\n");
return `Wallet Balances:\n${report}`;
},
}),
],
});
This comprehensive wallet management system provides everything you need to securely manage SEI wallets in your AI agents. Remember to always prioritize security and test thoroughly before deploying to production.
Next Steps
- Token Operations - Learn about token transfers and interactions
- Smart Contracts - Interact with smart contracts
- Examples - See real-world implementations