P2Pesa: Decentralized Reputation for Bitcoin & Mobile Money Agents
Project Story
Inspiration
Bitcoin is fundamentally trustless. However, the fiat-to-Bitcoin offramps and onramps (especially in regions like Kenya where M-Pesa dominates) are not.
Local P2P fiat trading is the lifeblood of Bitcoin adoption in Africa, but it suffers from a major bottleneck: trust. Centralized escrow platforms resolve this by imposing heavy KYC, high fees, and holding custodial control over user funds. They create single points of failure.
Our team asked a fundamental question: Can we build a reputation system that belongs entirely to the agents, without relying on a centralized database, surveillance, or custodial escrows?
This inspired P2Pesa. Our core thesis is:
"Bitcoin is trustless. But the people who onboard you to Bitcoin shouldn't have to be."
By combining Nostr (NIP-07) for self-sovereign identity and Bitcoin Message Signing for proof of liquidity, we built a portal where reputation is completely portable, mathematically verified, and Sybil-resistant.
How We Built It
P2Pesa is a Next.js web application engineered around a decentralized, database-free architecture.
- Portable Identity via Nostr: Instead of registering with an email or password, users authenticate using their private keys via a NIP-07 browser extension (like Alby). This maps their identity to an elliptic curve public key.
- Proof of Reserves (Deferred Verification): Agents demonstrate liquidity without sending funds to a third party. We generate a unique cryptographic challenge. The agent signs this challenge with the private key corresponding to their on-chain Bitcoin address.
- Automated Verification: The signature verification is executed locally using client-side libraries. If valid, we query the public ledger via the Mempool.space API to aggregate the agent’s confirmed and unconfirmed balances, proving their trading capacity to the community.
- Earthy, Technical UI: Taking direct inspiration from the minimalist design principles, we built a clean matte-charcoal interface featuring thin technical borders, precise monospace layouts for cryptographic data, and a complete removal of visual noise.
The Mathematical Engine
The cryptographic challenge is bound to the agent's Nostr public key, preventing signature replay attacks. The relationship between the Bitcoin private key \( k \) and the public key \( K \) on the secp256k1 curve is defined as:
$$K = k \cdot G$$
where \( G \) is the curve's base generator point.
For the Sybil-resistant review layer (planned for Epic 2), we modeled a weighted trust score formula. To prevent malicious actors from flooding the system with fake reviews, the weight of any review is tied to a Lightning Network payment (Zap). A review is only weighted by the amount of satoshis (value) locked to it:
$$T_s = \frac{\sum_{i=1}^{n} (Z_i \cdot R_i)}{\sum_{i=1}^{n} Z_i} + \lambda \cdot \log_{10}(1 + L_{\text{sats}})$$
where:
- \( T_s \) is the agent's overall trust score.
- \( Z_i \) is the quantity of satoshis sent via Lightning Zap for review \( i \).
- \( R_i \) is the qualitative rating (e.g., \( +1 \) or \( -1 \)).
- \( L_{\text{sats}} \) is the agent's verified on-chain wallet balance.
- \( \lambda \) is a scaling factor balancing on-chain liquidity against community feedback.
Challenges We Faced
We ran into several key engineering bottlenecks during the intensive hackathon timeline:
- Path-Resolution Webpack Bug: Our local repository directory originally had the name
Bitcoin++ Hackathon. We encountered a persistentChunkLoadErroron boot because Webpack's file-resolution engine struggled to parse the nested++characters inside local file URIs (where+is often decoded as a space). We resolved this by standardizing our repository naming and configuring Next.js's native Rust-based compiler (Turbopack) to handle pathing differently. - Third-Party Hydration Mismatches: Browser extensions (like ScribeHow) injected custom DOM classes (like
data-scribe-recorder-ready) into our<html>tag before React finished loading, throwing hydration mismatch errors on our clean SSG/SSR builds. We resolved this by selectively applyingsuppressHydrationWarningstrictly on the shell tags. - Nostr Relay Latency: Sourcing data directly from public relays on every page navigation was slow. We resolved this by building a custom Stale-While-Revalidate (SWR) caching layer using LocalStorage. The UI now loads profile caches in 0ms, while a background thread silently queries relays for metadata updates.
- NIP-07 Extension Race Conditions: On initial page load, our session-persistence checks ran before extensions (like Alby) had finished injecting
window.nostrinto the DOM. We engineered a 500ms delayed reconnect sequence within our React Auth Context to completely resolve this timing issue.
What We Learned
We gained a deep appreciation for the Nostr protocol (NIP-01/Kind 0) and the Nostr Dev Kit (NDK). We learned that database-free applications are not only possible but yield incredibly resilient privacy models. We also explored the nuances of Bitcoin message signing formats (handling Legacy, P2SH, and Native SegWit bech32 signatures safely) on the client side without relying on heavy backend validation servers.
Built With
- bitcoinjs-message
- html5
- javascript-(es6+)
- mempool.space
- next.js
- nostr-protocol
- nostr-tools
- typescript
- vercel
Log in or sign up for Devpost to join the conversation.