Analysis of Mosaik (v0.2.1) -- a Rust runtime for self-organizing, leaderless distributed systems built on iroh (QUIC-based p2p networking) -- and how its primitives map onto intent-based orderflow systems like NEAR Intents.
Nodes deployed on plain VMs self-organize into a functioning topology using just a secret key, a gossip seed, and role tags. No orchestration needed. Four subsystems:
- Discovery -- gossip-based peer announcement + catalog sync
- Streams -- typed pub/sub data channels (producer/consumer)
- Groups -- availability groups with Raft consensus and replicated state machines
- Store -- synchronized data store (WIP)
The Datum trait auto-implements for any serializable type. Introducing a new message type is just defining a struct:
#[derive(Serialize, Deserialize)]
struct MyNewOrderType { bid: u64, ask: u64, pair: String }
let producer = network.streams().produce::<MyNewOrderType>();
let consumer = network.streams().consume::<MyNewOrderType>();Stream IDs are derived from blake3 hash of the Rust type name. Wire format is postcard (compact binary) with length-delimited framing over QUIC.
There's no separate schema registry. Stream availability is metadata in signed peer entries:
- Producer created -- stream ID added to local
PeerEntry.streams, entry re-signed and version bumped - Gossip announcement -- updated
SignedPeerEntrybroadcast to all gossip neighbors in real-time - Catalog sync -- full bidirectional catalog exchange for consistency
- Consumer subscribes -- looks up producers for the matching stream ID in local catalog, opens QUIC connection
Type discovery is piggy-backed on peer discovery. A peer's catalog entry lists which stream IDs it produces, which group IDs it belongs to, and its role tags.
NEAR Intents (formerly Defuse Protocol) coordinates intent-based settlement across 18+ chains. The core primitive is token_diff -- a map of desired balance changes:
{
"intent": "token_diff",
"diff": {
"nep141:usdc.near": "-1000",
"nep141:wrap.near": "500"
}
}Negative values = willing to give. Positive values = want to receive. The Verifier contract (intents.near) matches complementary diffs and settles atomically. Settlement must be zero-sum across all participants.
| Type | Description |
|---|---|
token_diff |
Primary trading intent (swaps, bridges) |
transfer |
Direct token transfer to another account |
ft_withdraw |
Withdraw NEP-141 fungible tokens from the Verifier |
native_withdraw |
Withdraw native NEAR tokens |
Solvers compete via Request-for-Quote (not a traditional auction):
- All connected solvers receive quote requests simultaneously
- Each has a 3-second window to respond with pricing
- Best quote is selected and presented to user
- Solver signs a counter-intent (their own complementary
token_diff) - User intent + solver counter-intent are bundled and submitted to the Verifier
- Verifier executes both atomically -- all succeed or all fail
The current architecture relies on a centralized Solver Relay (solver-relay-v2.chaindefuser.com) as the sole coordination point between users and solvers. This is a single point of failure, a censorship vector, and a trust assumption on the relay operator.
User --> [Centralized Solver Relay (WebSocket)] --> Solvers
|
Best quote selected
|
User signs intent + Solver signs counter-intent
|
[intents.near Verifier Contract]
|
Atomic settlement
| Role | Mosaik Primitive | Tags |
|---|---|---|
| Users (intent submitters) | Stream producers of Intent types |
["user"] |
| Solvers | Stream consumers of intents, producers of quotes | ["solver"] |
| Auctioneers / Matchers | Groups with replicated state machines | ["auctioneer"] |
| Relayers | Group members that consume settlements | ["relayer"] |
Users (tag: "user")
|
v Stream<Intent>
Solvers (tag: "solver")
| subscribe_if(|p| p.tags().contains("user"))
|
v Stream<SolverQuote>
Auctioneers (tag: "auctioneer") -- 3-node Raft Group
| VerifierStateMachine
| SubmitIntent -> SubmitQuote -> ClearRound
|
v Stream<Settlement>
Relayers (tag: "relayer")
| subscribe_if(|p| p.tags().contains("auctioneer"))
|
v On-chain submission
/// Token balance changes -- the core NEAR Intents primitive.
/// Negative = willing to give, positive = want to receive.
pub type TokenDiff = BTreeMap<String, i128>;
#[derive(Serialize, Deserialize)]
struct Intent {
id: Digest,
action: IntentAction,
constraints: Vec<Constraint>,
deadline: u64,
}
enum IntentAction {
TokenDiff { diff: TokenDiff }, // Primary: "give X, receive Y"
Transfer { token: String, to: String, amount: u64 },
FtWithdraw { token: String, amount: u64 },
NativeWithdraw { amount: u64 },
}
#[derive(Serialize, Deserialize)]
struct SolverQuote {
intent_id: Digest,
solver_id: String,
solver_token_diff: TokenDiff, // Solver's complementary diff
amount_out: u64, // What user receives
expiration_ms: u64,
}
#[derive(Serialize, Deserialize)]
struct Settlement {
round: u64,
settled_intents: Vec<Digest>,
winning_quotes: Vec<Digest>,
aggregate_flow: TokenDiff, // Must be zero-sum
}Asset identifiers use defuse-style prefixed format:
nep141:usdc.near-- NEP-141 fungible tokensnep171:issuer.near:token_id-- NEP-171 NFTsnep245:contract.near:token_id-- NEP-245 multi-tokens
The auctioneer is a Raft group running a state machine that implements the RFQ matching logic:
struct VerifierStateMachine {
pending_intents: BTreeMap<Digest, Intent>,
current_round: u64,
quotes: BTreeMap<Digest, Vec<SolverQuote>>, // intent_id -> quotes
round_results: Vec<Settlement>,
}
impl StateMachine for VerifierStateMachine {
type Command = AuctionCommand; // SubmitIntent, SubmitQuote, ClearRound
type Query = AuctionQuery; // PendingIntents, RoundResult, CurrentRound
}ClearRound matching logic:
- For each intent with quotes, verify token_diff compatibility (opposite signs on matching tokens)
- Pick the best quote by
amount_out(best price for user) - Verify aggregate flow is zero-sum across all participants
- Emit
Settlement
Mosaik's subscribe_if predicates enable solvers to selectively subscribe to specific intent producers:
// Solver specializing in NEAR/USDC swaps
let user_tag = Tag::from("user");
let mut consumer = network.streams()
.consumer::<Intent>()
.subscribe_if(move |peer| peer.tags().contains(&user_tag))
.build();In production, solvers could filter by asset type, chain, volume tier, or user reputation -- something the centralized relay's broadcast-to-all model doesn't support.
| Aspect | Centralized Solver Relay | Mosaik-Based Design |
|---|---|---|
| Coordination | Single WebSocket server | Decentralized P2P streams |
| Fault tolerance | Server down = system down | 3-node Raft group survives 1 failure |
| Censorship | Relay operator can filter intents/solvers | No central gatekeeper |
| Trust | Trust the relay operator | Trust the Raft quorum |
| Solver discovery | Connect to relay WebSocket | Mosaik peer discovery with tags |
| Intent routing | Broadcast to all solvers | Selective via subscribe_if predicates |
| Quote aggregation | Relay collects and selects | VerifierStateMachine matches atomically |
| Scaling | Vertical (bigger server) | Horizontal (more auctioneer nodes) |
| Transparency | Opaque server-side selection | Replicated state machine, auditable |
These patterns emerged from building the working implementation:
-
Producer ordering matters -- Create stream producers BEFORE calling
discover_all(). If peers sync catalogs before producers exist, the catalog entries won't include stream IDs and consumers won't find producers. -
Tag propagation is not instant --
discovery.feed(signed_entry)updates the local catalog only. For other nodes to see tag changes immediately, broadcast the signed entry to each node directly:for other_net in &other_networks { other_net.discovery().feed(signed_entry.clone()); }
-
Groupis notClone-- You can't share a Raft group handle across tasks. In the NEAR Intents demo, solvers produce solutions on a stream rather than callinggroup.execute()directly. The auctioneer node consumes the solution stream and feeds them into the Raft group. -
Use
Consistency::Weakon followers --Consistency::Strongqueries forward to the leader but can panic on followers if the forwarding path isn't implemented. Wait forgroup.when().committed().reaches(target_index)then query withConsistency::Weak. -
Create consumers before sending data -- If a settlement is produced before the relayer's consumer is connected, it will be missed. Create consumers and wait for
consumer.when().subscribed()before the producing side sends data.
- The NEAR Intents Verifier contract (
intents.near) remains the on-chain trust anchor - Token deposits, withdrawals, and cross-chain bridges are unchanged
- The signing standards (NEP-413, ERC-191, Raw Ed25519) are unchanged
- Solvers still compete on price and execution quality
- Quote window timing: The centralized relay enforces a strict 3s quote window server-side. In the decentralized design, this is approximated via
tokio::time::sleepbeforeClearRound. A more robust approach could use Raft-committed timestamps. - Solver reputation/staking: Without a central relay tracking solver behavior, reputation and anti-spam mechanisms need to move on-chain or into the Raft state machine.
- Cross-chain settlement: The current demo handles the matching/quoting layer. Actual cross-chain settlement still requires Chain Signatures MPC or the PoA Bridge -- those are orthogonal to the relay decentralization.
- MEV protection: A replicated state machine makes intent ordering transparent. This could be a feature (auditability) or a risk (MEV extraction). Threshold encryption of intents until matching time could address this.
- No Byzantine fault tolerance -- Mosaik uses Raft (crash fault tolerant), not BFT consensus. For a system with financial incentives, you'd either need to trust the auction group or add a BFT layer. Commonware provides Simplex, a formally verified BFT consensus protocol -- its
StateMachineabstraction is similar enough to Mosaik's that a Simplex backend could potentially be integrated. - Intent standardization -- type-name-based stream IDs mean all participants need to compile against the same Rust types. For a heterogeneous ecosystem you'd want a more stable schema (protobuf, or custom StreamId derivation from a versioned schema hash).
zmanian/near-intents -- Working Rust implementation demonstrating the full architecture: 6-node demo (1 user, 2 solvers, 3 auctioneers) with realistic intents (USDC->NEAR swaps, USDC->wETH bridges, NEAR->stNEAR staking), competing solver quotes, Raft-replicated auction matching, and zero-sum settlement verification.
Mosaik's streams + groups map directly onto the NEAR Intents lifecycle: streams for dissemination (intents out, quotes back), groups for consensus (fair ordering and matching with zero-sum verification), and tags for capability routing (which solvers handle which intent types). The centralized Solver Relay is the obvious bottleneck, and Mosaik provides a natural decentralized replacement without changing any of the on-chain settlement mechanics.