Skip to content

Instantly share code, notes, and snippets.

@zmanian
Created February 16, 2026 05:09
Show Gist options
  • Select an option

  • Save zmanian/be111b941d6f56de880ac95658be56cc to your computer and use it in GitHub Desktop.

Select an option

Save zmanian/be111b941d6f56de880ac95658be56cc to your computer and use it in GitHub Desktop.
Mosaik as an Intent Settlement Coordination Layer -- analysis of how Mosaik's p2p primitives map onto orderflow/solver/auction systems and NEAR Intents

Mosaik as an Intent Settlement Coordination Layer

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.

What Mosaik Is

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)

How Typed Messages Work

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.

How Peers Learn About New Message Types

There's no separate schema registry. Stream availability is metadata in signed peer entries:

  1. Producer created -- stream ID added to local PeerEntry.streams, entry re-signed and version bumped
  2. Gossip announcement -- updated SignedPeerEntry broadcast to all gossip neighbors in real-time
  3. Catalog sync -- full bidirectional catalog exchange for consistency
  4. 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.

Real NEAR Intents Protocol

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.

Supported Intent Types

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

RFQ-Based Solver Competition

Solvers compete via Request-for-Quote (not a traditional auction):

  1. All connected solvers receive quote requests simultaneously
  2. Each has a 3-second window to respond with pricing
  3. Best quote is selected and presented to user
  4. Solver signs a counter-intent (their own complementary token_diff)
  5. User intent + solver counter-intent are bundled and submitted to the Verifier
  6. Verifier executes both atomically -- all succeed or all fail

Current Architecture (Centralized)

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

Mapping to Orderflow / Solver / Auction Systems

Role Mapping

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"]

Data Flow

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

Message Types (Token Diff Model)

/// 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 tokens
  • nep171:issuer.near:token_id -- NEP-171 NFTs
  • nep245:contract.near:token_id -- NEP-245 multi-tokens

The Auction as a Replicated State Machine

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:

  1. For each intent with quotes, verify token_diff compatibility (opposite signs on matching tokens)
  2. Pick the best quote by amount_out (best price for user)
  3. Verify aggregate flow is zero-sum across all participants
  4. Emit Settlement

Selective Solver Subscriptions

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.

Improvements Over the Centralized Relay

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

Mosaik API Patterns Learned

These patterns emerged from building the working implementation:

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

  2. 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());
    }
  3. Group is not Clone -- You can't share a Raft group handle across tasks. In the NEAR Intents demo, solvers produce solutions on a stream rather than calling group.execute() directly. The auctioneer node consumes the solution stream and feeds them into the Raft group.

  4. Use Consistency::Weak on followers -- Consistency::Strong queries forward to the leader but can panic on followers if the forwarding path isn't implemented. Wait for group.when().committed().reaches(target_index) then query with Consistency::Weak.

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

What This Doesn't Change

  • 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

Gaps and Open Questions

  • Quote window timing: The centralized relay enforces a strict 3s quote window server-side. In the decentralized design, this is approximated via tokio::time::sleep before ClearRound. 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 StateMachine abstraction 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).

Implementation

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.

Key Insight

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment