@0xinfrax/mpc-sdk
v0.4.0
Published
InfraX MPC SDK — lightweight independent client for the MPC wallet microservice (email-based key-shard wallets). Wallet module (sendCode/register/recover/status/listWallets/createWallet) + Session module (unlock/lock/status) + Chain module (balance/signMe
Maintainers
Readme
@0xinfrax/mpc-sdk
InfraX MPC 独立轻量 SDK —— 面向 MPC 微服务(邮箱分片托管钱包)的零依赖 TypeScript 客户端。
不依赖整包 infrax-dk,版本独立演进(MQ-10 补充 E-5)。
当前覆盖四个模块(19 方法,E-4④ 起支持单邮箱 1:N 多子钱包;链上模块 E-5d 接 TSS 分片签名;Solana 模块 MPC-SOL-5 接 FROST Ed25519 分片签名):
| 模块 | 方法 | 端点 |
|---|---|---|
| 钱包 | wallet.sendCode | POST /api/v2/mpc/send-code |
| 钱包 | wallet.register | POST /api/v2/mpc/register(每次注册新建一个子钱包,返回 walletId) |
| 钱包 | wallet.recover | POST /api/v2/mpc/recover(walletId 可指定子钱包,缺省首个) |
| 钱包 | wallet.status | GET /api/v2/mpc/status(email 可带 walletId 定位子钱包) |
| 钱包 | wallet.listWallets | GET /api/v2/mpc/wallets(同邮箱全部子钱包) |
| 钱包 | wallet.createWallet | POST /api/v2/mpc/send-code(组合入口) |
| 会话 | session.unlock | POST /api/v2/mpc/session/unlock(walletId 可指定子钱包) |
| 会话 | session.lock | POST /api/v2/mpc/session/lock |
| 会话 | session.status | GET /api/v2/mpc/session/status |
| 链上(EVM) | chain.balance | POST /api/v2/mpc/balance(原生币 + 可选 ERC20) |
| 链上(EVM) | chain.signMessage | POST /api/v2/mpc/sign-message(EIP-191,TSS 分片签名) |
| 链上(EVM) | chain.signTypedData | POST /api/v2/mpc/sign-typed-data(EIP-712,TSS 分片签名) |
| 链上(EVM) | chain.sendTransaction | POST /api/v2/mpc/send-transaction(原生币 / ERC20 transfer) |
| 链上(EVM) | chain.contractRead | POST /api/v2/mpc/contract-read(只读 eth_call) |
| 链上(EVM) | chain.contractWrite | POST /api/v2/mpc/contract-write(staticCall 模拟 + TSS 签名广播) |
| 链上(EVM) | chain.gasEstimate | POST /api/v2/mpc/gas-estimate |
| Solana | solana.sign | POST /api/v2/mpc/solana/sign(原始 message → 64B Ed25519 签名,FROST 2-of-3) |
| Solana | solana.sendTransaction | POST /api/v2/mpc/solana/send-transaction(base64 已签名交易 → 广播 + 确认轮询) |
| Solana | solana.balance | POST {chainRpcUrl}/v1/rpc/solana(getBalance,读 chain-rpc 网关,非 MPC 服务) |
E-4④ 单邮箱 1:N:一个邮箱可派生多个 Agent 子钱包(如 50 子钱包并发模型)。
register每次创建新钱包返回walletId;recover/status/session.unlock带walletId精确命中子钱包,缺省作用于同邮箱首个(向后兼容)。token 一经解锁即绑定到该子钱包,后续链上操作无需再带walletId。
E-5d 链上模块(TSS 就绪):服务端四签名/交易端点已迁移 cggmp24 TSS 2-of-2 分片签名(服务端仅持分片、不再重建完整私钥,见
docs/TSS_EVALUATION.md);SDK 侧为纯 HTTP 封装——签名端点返回 65B 序列化签名(0x + r||s||v),交易端点返回txHash;交易类数量参数统一用字符串(服务端 parseUnits/parseEther 自理)。
MPC-SOL-5 Solana 模块(ed25519 / FROST 就绪):钱包须为 ed25519 曲线(
register({ curve: 'ed25519' }),或用seed导入存量 32B 种子),服务端走独立 FROST 2-of-3 签名链路(projects/mpc-ed25519),与 EVM 路径物理隔离(独立进程 9210/9211/9212)。三处与 EVM 模块的关键差异:
- 待签数据是原始 message 字节(hex,≤ 4096B),不是 32B 摘要 —— Ed25519 内部才做 SHA-512,且 Ed25519ph 预哈希变体 Solana 运行时不支持。
- 签名是 64B
R‖z(128 hex,无0x),且无恢复 idv—— 验签靠公钥而非恢复地址(@solana/web3.jsv1 未导出 Ed25519 验签 API,用tweetnacl)。- 交易模型由客户端持有:
@solana/web3.js构建交易 →serializeMessage()交solana.sign→tx.addSignature()组装 → base64 交solana.sendTransaction。服务端不重写交易模型。EVM 专属端点对 ed25519 钱包返 400(不静默降级):
balance/sign-message/sign-typed-data/sign-digest/sign/send-transaction/contract-read/contract-write/gas-estimate;反向/solana/*只接受 ed25519 钱包。因此solana.balance不经 MPC 服务,由 SDK 直读 chain-rpc 网关(构造时传chainRpcUrl+chainRpcReadKey)。
安装
npm i @0xinfrax/mpc-sdkNode.js >= 18(使用原生 fetch,零运行时依赖)。
QuickStart
import { MpcClient, MpcApiError } from '@0xinfrax/mpc-sdk';
const mpc = new MpcClient({
baseUrl: process.env.MPC_URL ?? 'http://127.0.0.1:9104', // 生产按实际可达地址
apiKey: process.env.MPC_API_KEY, // MPC 服务鉴权 key(出站 X-API-Key)
});
// 1. 发验证码 → 邮箱收码
await mpc.wallet.sendCode({ email: '[email protected]' });
// 2. 注册托管钱包
const reg = await mpc.wallet.register({ email: '[email protected]', code: '123456' });
console.log(reg.data.walletAddress);
// 3. 解锁会话(后续链上操作的凭证)
const s = await mpc.session.unlock({ email: '[email protected]', code: '123456' });
const token = s.data.token;
// 4. 会话状态
const st = await mpc.session.status({ token });
console.log(st.data.unlocked, st.data.remainingSeconds);
// 5. 链上操作(E-5d,token 即凭证)
const bal = await mpc.chain.balance({ token, chain: 'sepolia' });
console.log(bal.data.nativeBalance, bal.data.nativeSymbol);
const sig = await mpc.chain.signMessage({ token, message: 'hello' });
console.log(sig.data.signature, sig.data.address);
const tx = await mpc.chain.sendTransaction({
token, to: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', amount: '0.01', chain: 'sepolia',
});
console.log(tx.data.txHash);
// 6. 锁定会话
await mpc.session.lock(token);
// 7. 邮箱恢复(封装「验证码 → 分片重建 → 地址校验」完整流程)
const recovered = await mpc.wallet.recover({
email: '[email protected]',
code: '654321',
expectedAddress: reg.data.walletAddress, // 可选:客户端二次地址校验
});
console.log(recovered.data.walletAddress === reg.data.walletAddress);Solana(ed25519)用法
import { Connection, PublicKey, SystemProgram, Transaction } from '@solana/web3.js';
const mpc = new MpcClient({
baseUrl: process.env.MPC_URL!, // MPC 服务(sign / sendTransaction)
apiKey: process.env.MPC_API_KEY,
chainRpcUrl: process.env.CHAIN_RPC_URL, // 默认 http://127.0.0.1:9130
chainRpcReadKey: process.env.CHAIN_RPC_READ_KEY, // 平台读 key 或订阅 rx_ key(仅 solana.balance 需要)
});
// 0) 钱包(一次性):创建 ed25519 钱包;已有存量 Solana 私钥可用 seed 导入(地址逐字节一致)
await mpc.wallet.sendCode({ email: '[email protected]' });
const reg = await mpc.wallet.register({
email: '[email protected]', code: '123456',
curve: 'ed25519',
// seed: 'a5c9…' // 可选:32B 种子 hex(64 hex),导入存量 Solana 私钥
});
console.log(reg.data.walletAddress, reg.data.curve); // base58 地址 / 'ed25519'
// 1) 解锁会话(token 绑定该子钱包,后续 solana.* 凭 token)
const { data: sess } = await mpc.session.unlock({ email: '[email protected]', code: '654321' });
// 2) 余额(经 chain-rpc 网关 getBalance;lamports 与 SOL 双单位)
const { data: bal } = await mpc.solana.balance({ address: sess.address });
console.log(bal.lamports, bal.sol); // '4995000' '0.004995'
// ⚠️ fee payer 余额须 > 890880 lamports(免租门槛),否则广播报 InsufficientFundsForRent
// 3) 构建交易(交易模型由客户端持有)→ message 原文交 MPC 签名 → 组装 → 广播
const conn = new Connection('https://api.mainnet-beta.solana.com');
const tx = new Transaction().add(SystemProgram.transfer({
fromPubkey: new PublicKey(sess.address),
toPubkey: new PublicKey('GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB'),
lamports: 1000,
}));
tx.feePayer = new PublicKey(sess.address);
tx.recentBlockhash = (await conn.getLatestBlockhash()).blockhash;
const { data: signed } = await mpc.solana.sign({
token: sess.token,
message: tx.serializeMessage().toString('hex'), // ← 原始 message(非 32B 摘要)
});
tx.addSignature(new PublicKey(signed.address), Buffer.from(signed.signature, 'hex'));
if (!tx.verifySignatures()) throw new Error('signature mismatch'); // web3.js 内部验签
const { data: sent } = await mpc.solana.sendTransaction({
token: sess.token,
transaction: tx.serialize({ requireAllSignatures: true }).toString('base64'),
});
console.log(sent.txHash, sent.confirmed, sent.signatureStatus?.confirmationStatus);要点:
- FROST 签名非确定性:同一 message 两次调用返回不同签名,均有效(每方独立随机 nonce)。
sign的partnerIndex:1(默认,片2)/2(片3,仅 2-of-3 钱包可用)。sendTransaction的wait:默认true轮询getSignatureStatuses至confirmed/finalized;false仅广播(confirmed:false/reason:'wait=false');reason:'timeout'不代表失败,须以txHash复核。- base58 地址大小写敏感:任何环节(含
recover({ expectedAddress })客户端校验)都不可toLowerCase()。 - 服务端计费:
solana_sign1e14 wei /solana_send_transaction1e15 wei(余额不足 → 402)。
错误处理
所有失败均抛 MpcApiError(含 HTTP 状态 + 业务 code + 语义 kind);网络/超时抛 MpcNetworkError。
| kind | 语义 | 典型场景 |
|---|---|---|
| unauthorized (401) | 未授权 | 缺/错 key;会话不存在或过期 |
| forbidden (403) | 禁止 | 外部签发 key 被禁用 |
| bad_request (400) | 参数错误 | 验证码错误/过期(业务 code 1001) |
| not_found (404) | 未找到 | 未注册钱包(1004) |
| conflict (409) | 冲突 | 恢复地址与期望不一致(SDK 40900) |
| rate_limited (429) | 限流 | 验证码尝试次数超限 |
| server_error (5xx) | 服务端错误 | 分片解密失败(500/1007) |
try {
await mpc.wallet.register({ email, code: '000000' });
} catch (e) {
if (e instanceof MpcApiError) {
console.log(e.kind, e.status, e.code, e.message);
// → bad_request 400 1001 Invalid code
}
}与 MCP 对应关系
本 SDK 方法对应 Wallet MCP 工具(mcp-server/src/index.ts):mpc_send_code ↔ wallet.sendCode、mpc_register ↔ wallet.register、mpc_recover ↔ wallet.recover、mpc_status ↔ wallet.status、mpc_create_wallet ↔ wallet.createWallet、mpc_session_unlock/lock/status ↔ session.unlock/lock/status。入站(经 MCP 调用)沿用 inboundAuth(MCP_API_KEY),出站(本 SDK)统一 X-API-Key。
开发
npm run build # tsc → dist/
npm publish # 发布 @0xinfrax/mpc-sdk(@0xinfrax scope)