NEAR Intents (formerly Defuse Protocol) coordinates intent-based settlement across 18+ chains. 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.
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 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
Tokens use defuse-style prefixed identifiers:
nep141:usdc.near-- NEP-141 fungible tokensnep171:issuer.near:token_id-- NEP-171 NFTsnep245:contract.near:token_id-- NEP-245 multi-tokens
User → [Centralized Solver Relay (WebSocket)] → Solvers
↓
Best quote selected
↓
User signs intent + Solver signs counter-intent
↓
[intents.near Verifier Contract]
↓
Atomic settlement
The Solver Relay is a JSON-RPC/WebSocket service that:
- Receives quote requests from frontends
- Broadcasts to all connected solvers
- Collects responses within the 3s window
- Returns best quote to the frontend
- Handles intent publication and status tracking
Replace the centralized relay with Mosaik's P2P streams and replicated Raft state machine:
Users (tag: "user")
│
▼ Stream<Intent>
Solvers (tag: "solver")
│ subscribe_if(|p| p.tags().contains("user"))
│
▼ Stream<SolverQuote>
Auctioneers (tag: "auctioneer") ── 3-node Raft Group
│ VerifierStateMachine
│ SubmitIntent → SubmitQuote → ClearRound
│
▼ Stream<Settlement>
Relayers (tag: "relayer")
│ subscribe_if(|p| p.tags().contains("auctioneer"))
│
▼ On-chain submission
| Role | Mosaik Tags | Function |
|---|---|---|
| Users | ["user"] |
Produce Stream<Intent> |
| Solvers | ["solver"] |
Consume intents, produce Stream<SolverQuote> |
| Auctioneers | ["auctioneer"] |
3-node Raft group running VerifierStateMachine |
| Relayers | ["relayer"] |
Consume settlements, submit on-chain |
struct VerifierStateMachine {
pending_intents: BTreeMap<u64, Intent>,
quotes: BTreeMap<u64, Vec<SolverQuote>>, // intent_id -> quotes
current_round: u64,
round_results: Vec<Settlement>,
}Commands: SubmitIntent(Intent), SubmitQuote(SolverQuote), ClearRound
ClearRound matching logic:
- For each intent with quotes, verify token_diff compatibility (opposite signs)
- Pick the best quote by
amount_out(best price for user) - Verify aggregate flow is zero-sum
- 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.
Implementation: zmanian/near-intents
| 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 |
-
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.