Skip to content

@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.

Terminal window
npm install @ludus/wallet

The unified Wallet interface abstracts over MPC and external wallets:

import type { Wallet, TransactionRequest, TransactionReceipt } from '@ludus/wallet';
// All wallets expose the same surface
const address = wallet.getAddress();
const chainId = wallet.getChainId();
const balance = await wallet.getBalance();
const tokenBal = await wallet.getTokenBalance('0xToken...');
// Sign and send transactions
const 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 signing
const typedSig = await wallet.signTypedData(domain, types, value);
// Wallet metadata
wallet.getType(); // 'metamask' | 'walletconnect' | 'coinbase' | 'mpc'
wallet.isNonInteractive(); // true for MPC wallets (agent-controlled)

Creates wallet instances by type:

import { WalletFactory } from '@ludus/wallet';
import type { WalletFactoryConfig } from '@ludus/wallet';
const factory = new WalletFactory({ defaultChainId: 84532 }); // Base Sepolia
const wallet = await factory.connectExternal(adapter, 'metamask');

Adapters for browser-based wallets:

import { ExternalWallet, ChainEnforcer, BASE_SEPOLIA, BASE_MAINNET } from '@ludus/wallet';
import type { WalletAdapter, WalletAdapterEvent } from '@ludus/wallet';

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', ... }

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;
}

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 agents
mpcWallet.isNonInteractive(); // true
// All Wallet methods work the same
await mpcWallet.sign(tx);
await mpcWallet.signMessage('Hello');
await mpcWallet.getBalance();

Three shares, 2-of-3 threshold:

ShareHolderPurpose
Share 1Lit Protocol networkDistributed across Lit nodes
Share 2Platform KMSPlatform-controlled (via KMSClient)
Share 3EscrowRecovery (via EscrowClient)
const keyShareManager = new KeyShareManager(litClient, kmsClient, escrowClient);

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(),
};

Supports Firebase Auth (human users) and SIWA (agent wallets):

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);

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 nonce
const nonce = await siwa.generateNonce();
// Create SIWA message
const 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...' }

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 token

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 data
const 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-chain
await registry.setAgentWallet(agentId, walletAddress, deadline, signature);

Manages off-chain agent metadata (IPFS/Arweave):

const metadata = new AgentMetadataService(uploader);

Tracks on-chain agent reputation:

const reputation = new AgentReputationService(contractProvider);

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 tx
const result: BindingResult = await binder.bind(agentId, agentWalletAddress, ownerWallet);
// { txHash: '0x...' }
// Unbind: sets wallet to zero address
await binder.unbind(agentId, ownerWallet);

Local wallet persistence and lookup:

import { WalletRegistry } from '@ludus/wallet';
import type { WalletRecord } from '@ludus/wallet';
ErrorWhen
WalletConnectionErrorExternal wallet connection fails
WalletSigningErrorTransaction or message signing fails
WalletRejectedErrorUser rejected the wallet action
ChainSwitchRejectedErrorUser rejected chain switch
InsufficientBalanceErrorWallet balance too low
WalletAlreadyBoundErrorWallet is already bound to another agent
AgentOwnershipErrorCaller doesn’t own the agent NFT
// Core types
import type {
ChainId, WalletStatus, WalletType,
TransactionRequest, TransactionReceipt,
TypedDataDomain, TypedDataField,
AgentIdentity, ChainConfig, WalletRecord,
MPCWalletConfig, DelegationConditions, ExternalWalletConfig,
} from '@ludus/wallet';
// Wallet abstraction
import type { Wallet, WalletFactoryConfig } from '@ludus/wallet';
// Auth
import type { AuthProvider, AuthSession, VerificationResult, SIWAVerifier, SIWANonceStore } from '@ludus/wallet';
// Registry
import type { RegistryConfig, AgentRegistration, AgentMetadata, ReputationData, ContractProvider, MetadataUploader } from '@ludus/wallet';
// Binding
import type { BindingResult, WalletStore, BalanceChecker } from '@ludus/wallet';
// MPC
import type { MPCConfig, KeyShares, LitClient, KMSClient, EscrowClient, BalanceProvider } from '@ludus/wallet';