DEV Community

Cover image for How I Built a Custom Rust Blockchain for On-Chain Ride Lifecycle
Mehran Mazhar
Mehran Mazhar

Posted on • Edited on

How I Built a Custom Rust Blockchain for On-Chain Ride Lifecycle

Browser-ready demo and technical trade-offs

I wanted ride-sharing operations — request, offer, accept, pay, cancel — to be first-class on-chain transactions, not generic smart-contract calls wrapped in app logic. So I built Clutch Protocol: a custom non-EVM blockchain in Rust, a GraphQL bridge for apps, a JavaScript SDK for client-side signing, and a public stage testnet you can try without installing anything.

This post is the technical story: what I built, why I didn't use Ethereum, how a ride actually flows through the stack, and — just as importantly — what I haven't solved yet.

The problem I was solving

Traditional ride apps centralize trust: the platform owns matching, payments, and dispute resolution. Putting the ride state machine on-chain changes the contract between riders, drivers, and app builders:

  • Every step is a signed, auditable transaction
  • Private keys stay on the client (Bitcoin-style)
  • App developers can earn on-chain referrer fees when users complete rides
  • Drivers receive CLT directly via RidePay, not through a platform ledger

The tradeoff is real: you lose EVM composability and must ship custom SDKs. For a domain-specific protocol, that tradeoff felt acceptable.

Architecture at a glance

Demo App / Your dApp
        │
        ▼
  clutch-hub-sdk-js   (client-side signing, RLP, secp256k1)
        │
        ▼
  clutch-hub-api      (GraphQL + WebSocket + faucet)
        │
        ▼
  clutch-node         (Aura consensus, WebSocket JSON-RPC)
        │
        ▼
  clutch-explorer     (indexer → Postgres → REST UI)
Enter fullscreen mode Exit fullscreen mode
Component Role Stack
clutch-node Blockchain core Rust, Aura, libp2p
clutch-hub-api App bridge Rust, async-graphql
clutch-hub-sdk-js Client SDK TypeScript, npm
clutch-hub-demo-app Reference UI React, Vite, Leaflet
clutch-deploy Full stack Docker Compose

Docs: https://docs.clutchprotocol.io

Why a custom chain (and not Ethereum)

Clutch is non-EVM. Ride operations are native transaction types with RLP encoding:

Tag Type Purpose
1 RideRequest Passenger requests a ride
2 RideOffer Driver offers to fulfill
3 RideAcceptance Passenger accepts; fare debited
4 RidePay Payment installment to driver + referrers
5 RideCancel Cancel trip; refund unpaid fare
8 RideRequestCancel Cancel pending request

(Tags 6–7 are intentionally unused — they held two arrival-confirmation types I removed while simplifying the state machine.)

Apps don't deploy contracts. They call the Hub API for unsigned payloads, sign locally, and submit signed RLP hex. The node validates signatures, nonces, and applies the ride state machine.

What you gain: simpler app surface, predictable tx format, ride logic enforced in the node.

What you lose: DeFi composability, existing wallet/tooling, large validator ecosystem.

How a ride works (end to end)

RideRequest → RideOffer(s) → RideAcceptance → RidePay → completed
     ↓                              ↓
RideRequestCancel              RideCancel
Enter fullscreen mode Exit fullscreen mode

1. Build unsigned transaction (server)

The Hub API constructs the payload and injects referrer addresses from config:

mutation {
  createUnsignedRideRequest(
    pickupLatitude: 35.7,
    pickupLongitude: 51.4,
    dropoffLatitude: 35.8,
    dropoffLongitude: 51.5,
    fare: 1000
  )
}
Enter fullscreen mode Exit fullscreen mode

Returns JSON like:

{
  "from": "0x...",
  "nonce": 3,
  "data": {
    "function_call_type": "RideRequest",
    "arguments": { }
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Sign client-side (never send private keys)

The SDK holds your key locally and signs with secp256k1 — the key never touches the API. It's also what signs the auth challenge, so pass it when you construct the SDK:

import { ClutchHubSdk } from 'clutch-hub-sdk-js';

// privateKey stays in the client; it signs both the auth challenge and txs.
const sdk = new ClutchHubSdk('https://api-stage.clutchprotocol.io', publicKey, privateKey);

// ensureAuth signs a timestamped challenge with your key to get a short-lived JWT.
await sdk.ensureAuth();

const unsigned = await sdk.createUnsignedRideRequest({
  pickup: { latitude: 35.7, longitude: 51.4 },
  dropoff: { latitude: 35.8, longitude: 51.5 },
  fare: 1000,
});

const signed = await sdk.signTransaction(unsigned, privateKey);
Enter fullscreen mode Exit fullscreen mode

3. Submit signed transaction

await sdk.submitTransaction(signed.rawTransaction);
Enter fullscreen mode Exit fullscreen mode

The Hub forwards to the node over WebSocket JSON-RPC (send_raw_transaction). Validators include the tx in a block; state updates atomically.

4. Read state (GraphQL or subscriptions)

const requests = await sdk.listRideRequests();
await sdk.subscribeRideRequests((updated) => {
  console.log('Open requests:', updated.length);
});
Enter fullscreen mode Exit fullscreen mode

Subscriptions multiplex over a shared WebSocket to /graphql/ws. Under the hood the API polls the node (~0.5–1s) and pushes snapshots — an honest alpha limitation.

CLT economics (driver-first)

Ride payments and validator rewards are separate:

Layer Mechanism Default
RidePay Referrer fees + driver remainder 2% request + 2% offer
Blocks Fixed reward to block author 50 CLT per block

Example: 10 CLT fare, one full RidePay, both referrers set:

  • Request referrer: 1 CLT
  • Offer referrer: 1 CLT
  • Driver: 8 CLT

App builders: run your own Hub API, set your wallet as default_ride_request_referrer / default_ride_offer_referrer, and earn CLT when users complete rides on your deployment. No separate grants program — rewards come from real ride activity.

Details: https://docs.clutchprotocol.io/getting-started/app-developer-incentives

Security model

  • Client-side signing only — the API receives signed RLP hex, never private keys
  • Wallet auth via a signed challenge — you prove key ownership by signing a timestamped message (no passwords), which mints a short-lived JWT
  • Nonce anti-replay — per-account nonce enforced on-chain
  • Faucet — server-side signer only, testnet Transfer only; disabled in production

Try it in 3 minutes (no install)

  1. Open https://app-stage.clutchprotocol.io
  2. Choose Passenger or Driver → generate wallet → Request CLT (faucet)
  3. Passenger: request a ride on the map · Driver: submit an offer

Full tutorial: https://docs.clutchprotocol.io/getting-started/ride-lifecycle

Run the full stack locally

git clone https://github.com/clutchprotocol/clutch-deploy.git
cd clutch-deploy
cp .env.example .env
docker compose up -d
Enter fullscreen mode Exit fullscreen mode
npm install clutch-hub-sdk-js
Enter fullscreen mode Exit fullscreen mode

What's Alpha

This is an honest work-in-progress, not a finished product:

  • Testnet only — CLT is free test currency; nothing real is charged.
  • No reputation or ratings — nothing scores drivers or riders yet.
  • No dispute resolution — cancellations are on-chain, but there's no arbitration when two parties disagree, and no no-show/fraud handling.
  • Matching is naive — no surge pricing or geospatial optimization.
  • Subscriptions poll the node (~1s) rather than true push.
  • DAO governance is on the roadmap, not in the code.
  • APIs may change without notice.

Open source

Eight public repos under https://github.com/clutchprotocol

Questions I'd love feedback on

  1. Dispute resolution & reputation — this is the part I haven't built and find hardest. With no central operator to force a refund, what actually works: escrow with a timeout, staked third-party arbitrators, something else? If you've shipped marketplace or payments infra, I'd genuinely value your take.
  2. Domain-specific chain vs. smart contracts — worth it for ride-sharing, or would you always reach for an L2?
  3. Referrer-fee model for app builders — sensible incentive, or weird?
  4. What would you build on this stack?

Links

Built by Mehran Mazhar (GitHub). Alpha software — use at your own risk.

Top comments (4)

Collapse
 
nazar-boyko profile image
Nazar Boyko

The limitations section is what makes me trust the rest of the post, and it also points right at the hard part. The chain can prove a RidePay happened, but it can't know the ride itself did, and that gap is exactly where ConfirmArrival and ComplainArrival sit as stubs today. Once you wire those up, you're back to needing someone or something off-chain to vouch that the car actually showed up, which is the classic oracle problem in disguise. How are you thinking about closing that without a trusted middle party quietly becoming the thing the whole design was trying to avoid?

Collapse
 
mehran_mazhar profile image
Mehran Mazhar

Thank you for the thoughtful feedback. You've identified what is probably the most difficult challenge in building a decentralized ride-sharing service.
Rather than solving the oracle problem directly, my approach is to remove the need for an arrival oracle from the critical payment flow.
Once a driver and passenger are matched and a ride is created, the passenger can make incremental (partial) payments throughout the trip. Payments are released progressively as the service is delivered, based on the passenger's own assessment of the distance traveled and the quality of the ride.
The driver is immediately notified of each payment and can decide whether to continue the ride. This creates an incentive structure where both parties continuously signal satisfaction through their actions, without requiring a trusted third party to verify that the destination has been reached.
In other words, instead of asking an oracle to prove that a ride was completed, the protocol restructures the interaction so that payment and service progress together. This makes ConfirmArrival and ComplainArrival optional dispute-resolution features rather than critical components of the core payment mechanism.

Collapse
 
mehran_mazhar profile image
Mehran Mazhar

I'd really appreciate any feedback on this approach.

Collapse
 
mehran_mazhar profile image
Mehran Mazhar

Thanks for reading — happy to answer questions in the comments.

Try it without installing anything: app-stage.clutchprotocol.io
(create wallet → Request CLT → request a ride or submit an offer)

Docs: docs.clutchprotocol.io
GitHub: github.com/clutchprotocol

Alpha testnet — APIs may still change.

Curious what you think: for ride-sharing, would you build on a domain-specific chain with native ride tx types, or always use smart contracts on an L2?