Wallet Management

The SDK provides a wallet resource for managing multi-chain wallets. It supports two modes:

ModeKeys StoredSigningBest For
local (self-custody)Client-side (~/.loomlay/wallet.json)ClientMaximum security, self-custody
custodialServer-side (encrypted in PostgreSQL)ServerSimple integration

Wallet Modes

Self-Custody Mode (Recommended)

In self-custody mode, private keys are generated and encrypted locally on your device. They are never sent to any server. The API only receives public addresses for balance lookups.

const wallet = new OpenClawWallet({
  apiKey: 'agent_xxx',
  walletMode: 'local',
  getPassphrase: async () => process.env.LOOMLAY_WALLET_PASSPHRASE!,
});

Custodial Mode

In custodial mode, the API manages keys server-side. This is simpler but requires trusting the server with your keys.

const wallet = new OpenClawWallet({
  apiKey: 'agent_xxx',
  // walletMode defaults to 'custodial' in SDK
});

The OpenClaw Plugin defaults to self-custody (walletMode: 'local'). The SDK defaults to 'custodial' for backwards compatibility.

Create a Wallet

// Keys generated and encrypted locally
const created = await wallet.wallet.create();
 
console.log('Seed phrase:', created.seedPhrase); // Save securely!
console.log('Solana:', created.wallet.solanaAddress);
console.log('EVM:', created.wallet.evmAddress);
// Public addresses auto-registered with API

The seed phrase is the only way to recover your wallet. Store it securely and never share it.

Security Best Practices:

  • Never log or print seed phrases in production code
  • Store seed phrases in encrypted secret managers (e.g., AWS Secrets Manager, HashiCorp Vault)
  • Never commit seed phrases to version control
  • Use environment variables only for development; use proper secrets management in production

Supported Chains

The SDK derives addresses for multiple chains from a single BIP39 seed phrase:

ChainDerivation PathCurveAddress Format
Solanam/44'/501'/0'/0'ed25519Base58
Ethereumm/44'/60'/0'/0/0secp256k10x-prefixed hex
BaseSame as Ethereumsecp256k10x-prefixed hex
ArbitrumSame as Ethereumsecp256k10x-prefixed hex

All EVM chains share the same address derived from the secp256k1 curve. Solana uses a separate ed25519-based address.

Get Wallet Info

const info = await wallet.wallet.get();
 
console.log('Addresses:', {
  solana: info.solanaAddress,
  evm: info.evmAddress,
});
 
// Solana balances
console.log('SOL:', info.balances.solana.native);
for (const token of info.balances.solana.tokens) {
  console.log(`${token.symbol}: ${token.balance}`);
}
 
// EVM balances (aggregated across chains)
console.log('ETH:', info.balances.ethereum.native);

Balance Caching: Balances are fetched in real-time from chain RPC nodes and portfolio APIs. For high-frequency applications, consider caching balances locally and refreshing periodically (e.g., every 30 seconds) to reduce API calls.

Import an Existing Wallet

Import an existing seed phrase into a local self-custody wallet (self-custody mode only):

const imported = await wallet.wallet.importSeed(
  'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
  'my-secure-passphrase'
);
 
console.log('Imported:', imported.wallet.solanaAddress);
// Addresses auto-registered with API

Register External Addresses

Register externally-managed wallet addresses with the API for balance lookups:

await wallet.wallet.register({
  solanaAddress: 'your-solana-address',
  evmAddress: '0xyour-evm-address', // Optional
});

Export Private Keys

Only export keys when absolutely necessary. Exposing private keys risks losing all funds.

// Decrypts locally using passphrase — no API call needed
const keys = await wallet.wallet.exportKeys('any-seed-phrase');
 
console.log('Solana private key:', keys.solana.privateKey);
console.log('EVM private key:', keys.evm.privateKey);

Portfolio View

Get a comprehensive view of holdings across all chains:

const portfolio = await wallet.portfolio.get();
 
for (const holding of portfolio.holdings) {
  console.log(`${holding.symbol} on ${holding.chain}`);
  console.log(`  Balance: ${holding.balance}`);
  console.log(`  Value: $${holding.valueUsd}`);
}
 
console.log('Total portfolio value:', portfolio.totalValueUsd);

Transaction History

const history = await wallet.portfolio.history({
  limit: 20,
  chain: 'solana', // Optional filter
});
 
for (const tx of history.transactions) {
  console.log(`${tx.type}: ${tx.amount} ${tx.token}`);
  console.log(`  Hash: ${tx.txHash}`);
  console.log(`  Time: ${tx.timestamp}`);
}

Type Definitions

interface WalletCreateResult {
  seedPhrase: string;
  solanaAddress: string;
  evmAddress: string;
}
 
interface WalletInfo {
  solanaAddress: string;
  evmAddress: string;
  balances: {
    solana: ChainBalance;
    ethereum: ChainBalance;
    // ... other chains
  };
}
 
interface ChainBalance {
  native: string;
  tokens: TokenBalance[];
}
 
interface TokenBalance {
  address: string;
  symbol: string;
  name: string;
  balance: string;
  decimals: number;
}