@kalp_studio/tresori-mpc-sdk
v1.0.4
Published
Kalp MPC Wallet SDK - Sign and execute blockchain transactions using Multi-Party Computation (MPC) threshold signatures
Readme
@kalp_studio/tresori-mpc-sdk
A TypeScript SDK for creating and managing MPC (Multi-Party Computation) wallets with 2-of-3 threshold ECDSA signatures. Sign and execute blockchain transactions without exposing private keys.
Features
- MPC Wallet Creation — Distributed Key Generation (DKG) protocol, no single party holds the full private key
- Email OTP Authentication — Create or restore wallets via email verification
- Token Transfers — ERC-20 transfers with automatic gas estimation
- Gasless Transactions — Meta-transactions via ERC-2771 relay (users pay zero gas)
- Bulk Transfers — Send tokens to multiple recipients in a single transaction
- EIP-712 Signing — Typed structured data signing for permits, approvals, and more
- Multi-Chain — Ethereum, Base, Polygon, Arbitrum, Optimism, BSC, Avalanche (mainnet + testnet)
- Storage Abstraction —
localStoragefor browsers, in-memory for Node.js, or bring your own
Installation
npm install @kalp_studio/tresori-mpc-sdkPeer dependency:
npm install ethers@^5.7.0Quick Start
import { KalpMPCWallet } from '@kalp_studio/tresori-mpc-sdk';
const wallet = new KalpMPCWallet({
apiKey: 'YOUR_API_KEY',
chainId: 80002, // Polygon Amoy
});
// Try loading existing wallet from storage
const loaded = await wallet.init();
if (!loaded) {
// Create via email OTP
await wallet.sendEmailOtp('[email protected]');
// User receives 4-digit code...
const info = await wallet.verifyEmailOtp('[email protected]', '1234');
console.log('Wallet address:', info.address);
}
// Check balance
const balance = await wallet.getBalance();
console.log('Balance:', balance, 'ETH');API Reference
Constructor
const wallet = new KalpMPCWallet({
apiKey: string; // Required — Kalp Studio API key
chainId: number; // Required — Target chain ID
baseUrl?: string; // API base URL (default: https://wallet-api.kalp.studio)
rpcUrl?: string; // Custom RPC endpoint
keyStore?: KeyStore; // Custom storage (default: localStorage in browser)
});Wallet Lifecycle
// Load wallet from storage
await wallet.init(): Promise<boolean>
// Create wallet via email OTP
await wallet.sendEmailOtp(email: string): Promise<SendEmailOtpResult>
await wallet.verifyEmailOtp(email: string, otp: string): Promise<WalletInfo>
// Create wallet directly (requires userId)
await wallet.createWallet(userId: string): Promise<WalletInfo>
// Import existing wallet
await wallet.importWallet(address: string, sessionId: string, clientShare: string): Promise<void>
// Disconnect and clear stored keys
await wallet.disconnect(): Promise<void>Properties
wallet.isInitialized: boolean // Whether wallet is ready
wallet.address: string | null // Wallet address (0x...)
wallet.sessionId: string | null // MPC session ID
wallet.currentChainId: number // Active chain IDSending Transactions
Token Transfer (User Pays Gas)
const result = await wallet.sendTokenTransfer({
tokenAddress: '0xTokenAddress',
to: '0xRecipient',
amount: '10.5', // Human-readable amount
decimals: 18, // Token decimals (default: 18)
});
console.log('TX Hash:', result.txHash);Raw Transaction
const result = await wallet.sendTransaction(
'0xContractAddress', // to
'0xCalldata', // encoded function call
'0.1' // value in ETH (optional)
);Contract Call
const result = await wallet.callContract({
contractAddress: '0xContract',
abi: ['function transfer(address to, uint256 amount) returns (bool)'],
functionName: 'transfer',
params: ['0xRecipient', ethers.utils.parseEther('1.0')],
});Read Contract (No Gas)
const [balance] = await wallet.readContract(
'0xTokenAddress',
['function balanceOf(address) view returns (uint256)'],
'balanceOf',
['0xAddress']
);Gasless Transactions
Users pay zero gas. Transactions are relayed through Kalp's infrastructure.
// Single gasless transfer
const result = await wallet.sendGaslessTransfer({
tokenAddress: '0xToken',
to: '0xRecipient',
amount: '100',
decimals: 18,
});
// Bulk gasless transfer
const result = await wallet.sendGaslessBulkTransfer({
tokenAddress: '0xToken',
recipients: [
{ to: '0xAlice', amount: '50' },
{ to: '0xBob', amount: '25' },
],
decimals: 18,
});Bulk Transfer (User Pays Gas)
const result = await wallet.sendBulkTransfer({
tokenAddress: '0xToken',
recipients: [
{ to: '0xAlice', amount: '100' },
{ to: '0xBob', amount: '200' },
],
decimals: 18,
});Message Signing
// Sign a raw message hash
const sig = await wallet.signMessage(messageHash);
// sig = { r, s, v }
// Sign EIP-712 typed data
const signature = await wallet.signTypedData({
domain: { name: 'MyApp', version: '1', chainId: 80002, verifyingContract: '0x...' },
types: { Transfer: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }] },
primaryType: 'Transfer',
message: { to: '0xRecipient', amount: '1000000000000000000' },
});Chain Management
// Switch to a different chain
wallet.switchChain(8453); // Base Mainnet
wallet.switchChain(1, 'https://my-custom-rpc.com'); // With custom RPCUtilities
const balance = await wallet.getBalance(); // Native token balance
const tokenBal = await wallet.getTokenBalance('0xToken', 18); // ERC-20 balance
const nonce = await wallet.getNonce(); // Transaction count
const facilitator = wallet.getFacilitatorAddress(); // Facilitator contract
const relayer = wallet.getRelayerAddress(); // Relayer contractSupported Chains
| Chain | Mainnet | Testnet | |-------|---------|---------| | Ethereum | 1 | 11155111 (Sepolia) | | Base | 8453 | 84532 (Sepolia) | | Polygon | 137 | 80002 (Amoy) | | Arbitrum | 42161 | 421614 (Sepolia) | | Optimism | 10 | 11155420 (Sepolia) | | BSC | 56 | 97 (Testnet) | | Avalanche | 43114 | 43113 (Fuji) |
Custom Storage
Implement the KeyStore interface for custom key storage:
import { KalpMPCWallet, KeyStore } from '@kalp_studio/tresori-mpc-sdk';
class MySecureStore implements KeyStore {
async get(key: string): Promise<string | null> { /* ... */ }
async set(key: string, value: string): Promise<void> { /* ... */ }
async remove(key: string): Promise<void> { /* ... */ }
}
const wallet = new KalpMPCWallet({
apiKey: 'YOUR_KEY',
chainId: 80002,
keyStore: new MySecureStore(),
});Built-in options:
LocalStorageKeyStore— Browser localStorage (default in browser)InMemoryKeyStore— In-memory Map (default in Node.js)
Architecture
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Client App │────▶│ Kalp MPC SDK │────▶│ Secure TEE │
│ │ │ │ │ (Share #2) │
│ │ │ Share #1 │ │ │
└─────────────┘ └──────┬───────┘ └──────┬───────┘
│ │
│ Partial Sig #1 │ Partial Sig #2
│ │
└────────┬────────────┘
│
Combined Signature
│
┌─────▼─────┐
│ Blockchain │
└───────────┘The private key is never reconstructed. Each party computes a partial signature independently, and the SDK combines them into a valid ECDSA signature using Lagrange interpolation.
License
MIT
