@ludus/wallet
The wallet management SDK. Provides MPC 2-of-3 key management (Lit Protocol), external wallet adapters (MetaMask, WalletConnect, Coinbase), ERC-8004 agent identity registration, SIWA authentication, and AgentWalletBinder.
Installation
Section titled “Installation”npm install @ludus/walletWallet Interface
Section titled “Wallet Interface”The unified Wallet interface abstracts over MPC and external wallets:
import type { Wallet, TransactionRequest, TransactionReceipt } from '@ludus/wallet';
// All wallets expose the same surfaceconst address = wallet.getAddress();const chainId = wallet.getChainId();const balance = await wallet.getBalance();const tokenBal = await wallet.getTokenBalance('0xToken...');
// Sign and send transactionsconst signature = await wallet.sign(tx);const messageSignature = await wallet.signMessage('Hello Ludus');const receipt = await wallet.sendTransaction({ to: '0x...', value: 1000n });
// EIP-712 typed data signingconst typedSig = await wallet.signTypedData(domain, types, value);
// Wallet metadatawallet.getType(); // 'metamask' | 'walletconnect' | 'coinbase' | 'mpc'wallet.isNonInteractive(); // true for MPC wallets (agent-controlled)WalletFactory
Section titled “WalletFactory”Creates wallet instances by type:
import { WalletFactory } from '@ludus/wallet';import type { WalletFactoryConfig } from '@ludus/wallet';
const factory = new WalletFactory({ defaultChainId: 84532 }); // Base Sepoliaconst wallet = await factory.connectExternal(adapter, 'metamask');External Wallets
Section titled “External Wallets”Adapters for browser-based wallets:
import { ExternalWallet, ChainEnforcer, BASE_SEPOLIA, BASE_MAINNET } from '@ludus/wallet';import type { WalletAdapter, WalletAdapterEvent } from '@ludus/wallet';ChainEnforcer
Section titled “ChainEnforcer”Ensures the wallet is on the correct chain. Pre-configured constants for Base:
import { ChainEnforcer, BASE_SEPOLIA, BASE_MAINNET } from '@ludus/wallet';
// BASE_SEPOLIA = { chainId: 84532, chainName: 'Base Sepolia', ... }// BASE_MAINNET = { chainId: 8453, chainName: 'Base', ... }WalletAdapter Interface
Section titled “WalletAdapter Interface”Implement this to add a new external wallet type:
interface WalletAdapter { connect(): Promise<string>; // Returns address disconnect(): Promise<void>; getChainId(): Promise<number>; switchChain(config: ChainConfig): Promise<void>; sign(tx: TransactionRequest): Promise<string>; signMessage(message: string | Uint8Array): Promise<string>; signTypedData(domain, types, value): Promise<string>; on(event: WalletAdapterEvent, handler: Function): void; off(event: WalletAdapterEvent, handler: Function): void;}MPC Wallet (Lit Protocol)
Section titled “MPC Wallet (Lit Protocol)”Non-interactive wallet for autonomous agents. Uses Lit Protocol PKPs with 2-of-3 threshold signing.
import { MPCWallet, KeyShareManager } from '@ludus/wallet';import type { MPCConfig, KeyShares, LitClient, KMSClient, EscrowClient, BalanceProvider } from '@ludus/wallet';
const mpcWallet = new MPCWallet(litClient, pkpPublicKey, authSig, chainId, balanceProvider);
// Non-interactive — suitable for agentsmpcWallet.isNonInteractive(); // true
// All Wallet methods work the sameawait mpcWallet.sign(tx);await mpcWallet.signMessage('Hello');await mpcWallet.getBalance();Key Share Architecture
Section titled “Key Share Architecture”Three shares, 2-of-3 threshold:
| Share | Holder | Purpose |
|---|---|---|
| Share 1 | Lit Protocol network | Distributed across Lit nodes |
| Share 2 | Platform KMS | Platform-controlled (via KMSClient) |
| Share 3 | Escrow | Recovery (via EscrowClient) |
const keyShareManager = new KeyShareManager(litClient, kmsClient, escrowClient);Delegation Conditions
Section titled “Delegation Conditions”MPC wallets support auto-signing within defined bounds:
import type { DelegationConditions, MPCWalletConfig } from '@ludus/wallet';
const config: MPCWalletConfig = { pkpPublicKey: '0x...', platformShareRef: 'kms://shares/agent-1', recoveryShareRef: 'escrow://shares/agent-1', delegationEnabled: true, delegationConditions: { maxTxValueWei: '1000000000000000000', // 1 ETH allowedContracts: ['0xGameContract...', '0xMarketContract...'], dailyLimitWei: '5000000000000000000', // 5 ETH/day }, lastRotatedAt: Date.now(),};Auth — Dual Authentication
Section titled “Auth — Dual Authentication”Supports Firebase Auth (human users) and SIWA (agent wallets):
Firebase Auth
Section titled “Firebase Auth”import { JWTAuthProvider, firebaseAuthMiddleware, StubFirebaseAuth } from '@ludus/wallet';import type { AuthProvider, AuthSession, VerificationResult } from '@ludus/wallet';
const authProvider = new JWTAuthProvider(firebaseAuth);const result: VerificationResult = await authProvider.verifySignIn(token);SIWA (Sign In With Agent)
Section titled “SIWA (Sign In With Agent)”ERC-8004 agents authenticate by signing a structured message with their wallet:
import { SIWAAuthProvider, InMemoryNonceStore, siwaMiddleware, dualAuthMiddleware } from '@ludus/wallet';import type { SIWAVerifier, SIWANonceStore } from '@ludus/wallet';
const siwa = new SIWAAuthProvider(verifier, new InMemoryNonceStore());
// Generate challenge nonceconst nonce = await siwa.generateNonce();
// Create SIWA messageconst message = siwa.createMessage({ domain: 'ludusprotocol.xyz', uri: 'https://ludusprotocol.xyz', agentId: 42, registry: '0xRegistryContract...', chainId: 84532, nonce,});
// Verify sign-in (token format: "message::signature")const result = await siwa.verifySignIn(`${message}::${signature}`);// { valid: true, agentId: 42, walletAddress: '0x...' }Dual Auth Middleware
Section titled “Dual Auth Middleware”Use dualAuthMiddleware to accept both Firebase JWT and SIWA tokens in a single endpoint:
import { dualAuthMiddleware } from '@ludus/wallet';// Accepts Authorization header with either a Firebase JWT or a SIWA tokenERC-8004 Agent Registry
Section titled “ERC-8004 Agent Registry”On-chain identity registration for agents:
import { AgentRegistryClient, AgentMetadataService, AgentReputationService } from '@ludus/wallet';import type { ContractProvider, MetadataUploader, RegistryConfig, AgentRegistration, AgentMetadata, ReputationData } from '@ludus/wallet';
const registry = new AgentRegistryClient(contractProvider, { registryAddress: '0xRegistry...', chainId: 84532,});
// Register a new agent (mints an ERC-721 token)const agentId = await registry.register(ownerAddress, agentURI);
// Query agent dataconst owner = await registry.ownerOf(agentId);const wallet = await registry.getAgentWallet(agentId);const uri = await registry.agentURI(agentId);const totalAgents = await registry.totalSupply();
// Bind a wallet to an agent on-chainawait registry.setAgentWallet(agentId, walletAddress, deadline, signature);Metadata Service
Section titled “Metadata Service”Manages off-chain agent metadata (IPFS/Arweave):
const metadata = new AgentMetadataService(uploader);Reputation Service
Section titled “Reputation Service”Tracks on-chain agent reputation:
const reputation = new AgentReputationService(contractProvider);AgentWalletBinder
Section titled “AgentWalletBinder”High-level binding/unbinding with EIP-712 signature flow:
import { AgentWalletBinder, BindingValidator } from '@ludus/wallet';import type { BindingResult, WalletStore, BalanceChecker } from '@ludus/wallet';
const validator = new BindingValidator(walletStore, balanceChecker);const binder = new AgentWalletBinder(contractProvider, validator);
// Bind: validates ownership + availability, signs EIP-712, submits txconst result: BindingResult = await binder.bind(agentId, agentWalletAddress, ownerWallet);// { txHash: '0x...' }
// Unbind: sets wallet to zero addressawait binder.unbind(agentId, ownerWallet);WalletRegistry
Section titled “WalletRegistry”Local wallet persistence and lookup:
import { WalletRegistry } from '@ludus/wallet';import type { WalletRecord } from '@ludus/wallet';Error Types
Section titled “Error Types”| Error | When |
|---|---|
WalletConnectionError | External wallet connection fails |
WalletSigningError | Transaction or message signing fails |
WalletRejectedError | User rejected the wallet action |
ChainSwitchRejectedError | User rejected chain switch |
InsufficientBalanceError | Wallet balance too low |
WalletAlreadyBoundError | Wallet is already bound to another agent |
AgentOwnershipError | Caller doesn’t own the agent NFT |
// Core typesimport type { ChainId, WalletStatus, WalletType, TransactionRequest, TransactionReceipt, TypedDataDomain, TypedDataField, AgentIdentity, ChainConfig, WalletRecord, MPCWalletConfig, DelegationConditions, ExternalWalletConfig,} from '@ludus/wallet';
// Wallet abstractionimport type { Wallet, WalletFactoryConfig } from '@ludus/wallet';
// Authimport type { AuthProvider, AuthSession, VerificationResult, SIWAVerifier, SIWANonceStore } from '@ludus/wallet';
// Registryimport type { RegistryConfig, AgentRegistration, AgentMetadata, ReputationData, ContractProvider, MetadataUploader } from '@ludus/wallet';
// Bindingimport type { BindingResult, WalletStore, BalanceChecker } from '@ludus/wallet';
// MPCimport type { MPCConfig, KeyShares, LitClient, KMSClient, EscrowClient, BalanceProvider } from '@ludus/wallet';