ceres_mpc
English | δΈζ
Two-party ECDSA MPC SDK for Flutter.
Built on sl-dkls23 (DKLs23 protocol) with Rust core and Dart orchestration layer via flutter_rust_bridge.
Features
- Key Generation -- Two-party ECDSA keygen with secp256k1, outputs Keyshare + EVM address
- Key Recovery -- DKLs23 key refresh preserving the original on-chain address
- Transaction Signing -- Two-party ECDSA signing, returns (r, s, recid)
- Backup & Restore -- AES-256-GCM encrypted backup envelope derivation and decryption
- Key Export -- Export MPC wallet to standard wallet by reconstructing full private key
- EVM Address Derivation -- EIP-55 checksummed address from group public key
- Transport Agnostic -- Host app injects its own network layer via
MpcTransport - Batch Message Optimization -- Protocol messages batched per logical round, minimizing HTTP round-trips
- WebSocket Transport Example -- Example app includes both HTTP and WebSocket transport reference implementations
Server-side implementation? See Server Integration Guide and the runnable server demo at SauceWu/ceres-mpc-server-demo.
Architecture
+-------------------------------------------+
| Host Application |
| (implements MpcTransport, manages storage)|
+--------------------+-----------------------+
|
+----------v----------+
| MpcClient | Dart orchestration
| keygen() / recover()|
+----------+----------+
|
+----------v----------+
| MpcEngine | Dart FFI wrapper
+----------+----------+
| flutter_rust_bridge
+----------v----------+
| Rust Core | Cryptography
| sl-dkls23 |
| DKLs23 protocol |
+---------------------+
Key design decisions:
- SDK owns cryptography, host owns network and storage
MpcEngine(Rust FFI) is internal, not exposed to host appsMpcClientis the only public API surface- All sensitive share material is
[REDACTED]intoString()output - Session state is ephemeral (in-memory Mutex maps), cleaned up after each protocol run
Getting Started
Prerequisites
- Flutter >= 3.32.0, Dart SDK >= 3.8.1
- Rust toolchain only when you are developing this package locally, or when your target is not covered by the published precompiled artifacts
Installation
# pubspec.yaml
dependencies:
ceres_mpc: ^0.1.0
web_socket_channel: ^3.0.3 # only needed when using WebSocketMpcTransport
Native Distribution
This package is published as a Flutter FFI plugin and keeps the Rust source in the package.
For common mobile consumers, the intended path is:
- install from
pub.dev - let
cargokitparticipate in the native build - automatically download signed precompiled Rust artifacts from GitHub Releases
In practice, that means most users should not need to install Rust locally.
Fallback behavior:
- if a release artifact exists for the current target, the build uses the precompiled binary
- if a target is not covered by the published release assets, the build can fall back to local Rust compilation
This package does not require users to manually download AARs or XCFrameworks.
Usage
import 'package:ceres_mpc/ceres_mpc.dart';
// 1. Implement transport (your server communication layer)
class MyTransport implements MpcTransport {
@override
Future<String> send(String payload) async {
// POST or forward the JSON-RPC payload to your MPC server, return raw response
}
}
See the runnable app in example/README.md for end-to-end setup, including transport switching.
For a backend reference, see SauceWu/ceres-mpc-server-demo.
WebSocket Transport
The example app ships a reference WebSocketMpcTransport that implements MpcTransport and can be swapped in without changing the MPC client flow.
final transport = WebSocketMpcTransport(
wsUrl: 'ws://your-mpc-server.com/ws',
timeout: const Duration(seconds: 30),
);
Behavior:
- Lazily connects on the first
send() - Matches concurrent responses by JSON-RPC
id - Automatically reconnects on the next request after disconnect
- Throws
WsTransportTimeoutExceptionon connect/response timeout
Precompiled Targets
The release workflow is intended to cover these common mobile targets:
- Android
arm64-v8a - Android
armeabi-v7a - Android
x86_64 - iOS device
arm64 - iOS Simulator
arm64 - iOS Simulator
x86_64
Project Structure
lib/
ceres_mpc.dart # Public API exports
src/
client/
mpc_client.dart # High-level orchestration API
mpc_exceptions.dart # MpcProtocolException, MpcTransportException
dto/
mpc_dtos.dart # KeygenResult, RecoveryResult, SignResult, etc.
bridge/
mpc_engine.dart # Internal Rust FFI wrapper
transport/
mpc_transport.dart # Abstract transport interface
rust/
src/
api/
mpc_engine.rs # Core MPC protocol (keygen, recovery, sign)
session.rs # Ephemeral session state management
types.rs # Shared Rust types (MpcRoundResult, BackupEnvelope)
address.rs # EIP-55 EVM address derivation
Protocol Flow
Keygen (4-round DKLs23 protocol, 3 HTTP round-trips with batch optimization)
Client (Party2) Server (Party1)
| |
| RPC keygen (round=1) |
|--------------------------------->|
| { sessionId, batch R1 } | DKG starts, collect batch
|<---------------------------------|
| |
| [Rust] keygen() |
| |
| RPC keygen (round=2) |
|--------------------------------->|
| { batch R2 } |
|<---------------------------------|
| |
| RPC keygen (round=3) |
|--------------------------------->| Server protocol completes,
| { batch R3 + keyshare persisted}| keyshare pre-persisted
|<---------------------------------|
| |
v KeygenResult v
The DKLs23 protocol has 4 internal rounds, but batch optimization compresses this to 3 HTTP round-trips. Each round batches all protocol messages (ASK + broadcast + P2P) into a single WireEnvelope with a payloads array, reducing DKG from ~13 individual HTTP calls to 3. Recovery and Sign follow the same pattern.
Tip: Use
WebSocketMpcTransportto keep a persistent connection β avoids TCP handshake overhead on each round-trip.
Cryptographic Dependencies
| Crate | Purpose |
|---|---|
| sl-dkls23 1.0.0-beta | DKLs23 threshold ECDSA (keygen, sign, key refresh, key export) |
| sl-mpc-mate 1.0.0-beta | MPC coordination (Relay trait, message routing) |
| k256 0.13 | secp256k1 elliptic curve primitives |
| tokio 1 | Async runtime for protocol bridge |
| aes-gcm 0.10 | AES-256-GCM backup encryption |
Running Tests
# Dart unit tests (mocking Rust layer)
flutter test
# Example app analyzer + widget/transport tests
cd example && flutter analyze && flutter test
# Package publish validation
dart pub publish --dry-run
# Rust unit tests (full cryptographic protocol)
cd rust && cargo test
Roadmap
xRust bridge skeleton via flutter_rust_bridgexShare storage DTOs and boundary layerxReal transaction signing (two-party ECDSA)xAES-256-GCM backup encryption (HKDF-SHA256 key derivation)xKey export (MPC β standard wallet migration)xKey rotation (DKLs23 key refresh)xDKLs23 migration (sl-dkls23 v1.0.0-beta)xWebSocket transport (alongside HTTP)xBatch message optimization (per-round batching via Notify signal)Multi-chain support (beyond EVM)
Security
- Private key shares never leave the Rust layer as plaintext
- All
toString()implementations redact sensitive fields - Session state is ephemeral and cleaned up after protocol completion
- Transport layer is fully controlled by the host application
If you discover a security vulnerability, please open a private report or issue through the repository contact channels at GitHub Issues.
License
This project is licensed under the MIT License. See LICENSE.
Acknowledgments
Built on sl-dkls23 by Silence Laboratories.