"Players own their identity across games the same way Bitcoin users own their money — your match history, reputation, and trust network live on a blockchain you control, not inside Steam or Riot's database."
Prototype built for MIT Bitcoin Hackathon 2026. I'd love to see a system like this integrated with game providers.
Prerequisites: Node.js 18+
# Install dependencies
cd server && npm install
cd ../client && npm install# Terminal 1 — API (http://localhost:3001)
cd server && npm start
# Terminal 2 — Frontend (http://localhost:5173)
cd client && npm run devOpen http://localhost:5173. The demo loads with ShadowFox pre-logged in and a seeded match history.
Optional — Lightning Network tips:
Copy server/.env.example to server/.env and fill in your LND node credentials. If left unconfigured, the tip UI runs in demo mode with a simulated payment button.
In competitive video games, you get matched with strangers. These people will likely never see you again, so there is no social consequence for toxic behavior — harassment, hate speech, and cyberbullying happens all the time.
Game providers like Steam, Riot Games, Epic Games, etc know who you encounter online. None of them talk to each other, and none of that history belongs to you.
PlayerGraph is a decentralized network that tracks every player you encounter, the games you played together, and the reputation they've earned through peer ratings. It works across Steam, Riot Games, and Epic Games simultaneously.
When you enter a lobby, you see:
- Every game you've played with each player, across all platforms
- Games your friends have played with them — even if you weren't there
- Trust badges based on ratings your friends have given them
- Warnings if trusted people in your network have flagged a player as unfriendly
Because the history lives on a blockchain rather than inside a company's database, it can be used across all game providers. Your history is yours.
Every match and every rating is written as a block on the PlayerGraph Chain — a SHA-256 hash chain where each block cryptographically references the one before it.
[Genesis: 0000…0000]
↓ prev_hash
[Block 1] type=match game=CS2 hash=00a3f9…
↓ prev_hash
[Block 2] type=rating 👍 Nova→Blaze hash=00c821…
↓ prev_hash
[Block 3] type=match game=Valorant hash=007d44…
Two block types are anchored to the chain:
- Match blocks — record which players played together, in which game, on which teams, with the final score
- Rating blocks — record a signed thumbs-up or thumbs-down from one player to another, tied to a specific match
Each block contains the data payload plus prev_hash — the hash of the block before it. To alter any historical record, an attacker must recompute that block and every block that follows, faster than the live network adds new blocks. This makes the history tamper-evident without requiring a trusted central authority.
Simply hashing a payload is cheap — an attacker could rewrite history instantly. PlayerGraph requires proof of work before any block is accepted onto the chain.
When a match ends or a rating is submitted, the server searches for a nonce — an integer that, when included in the block payload, produces a hash beginning with a required number of leading zeros:
let nonce = 0;
do {
blockHash = SHA - 256({ ...payload, prev_hash, nonce });
nonce++;
} while (!blockHash.startsWith("00"));This means producing a valid block requires hundreds of hash computations on average. Rewriting a block buried deep in the chain means re-mining it and every block above it — a cost that grows exponentially with chain depth.
The nonce and resulting hash are stored on every block and are visible in the Block Explorer.
Every player on PlayerGraph has a unique ECDSA P-256 keypair generated when they join:
pg_1001 (ShadowFox)
├── pg_id: pg_1001 (universal identifier)
├── public_key: (stored on-chain, visible to everyone)
└── private_key: (held only by the player)
The pg_id maps to usernames across all providers:
pg_1001 (ShadowFox)
├── steam_username: shadowfox_steam (linked via Steam OAuth)
├── riot_username: shadowfox_val (linked via Riot OAuth)
└── epic_username: shadowfox_epic (linked via Epic OAuth)
When a match record arrives from any provider, PlayerGraph resolves the provider username to a pg_id. The chain stores pg_id values — not platform usernames — so your history is portable even if you rename yourself on any platform.
When a player submits a rating, the rating payload is cryptographically signed with their private key before being anchored to the chain:
{
"rater_pg_id": "pg_1001",
"ratee_pg_id": "pg_1002",
"match_id": 7,
"rating": 1,
"created_at": "2026-04-11T15:00:00Z",
"signature": "3045022100a9f2..."
}The signature is embedded in the block payload and covered by the proof-of-work hash. This means:
- No impersonation — the server cannot fabricate a rating on a player's behalf
- No repudiation — a player cannot deny a rating they signed
- Trustless verification — any node can verify the signature using only the public key, without trusting PlayerGraph's servers
Players can tip each other sats directly in the lobby using the Lightning Network — Bitcoin's Layer 2 payment protocol for instant, near-zero-fee transactions.
When a player clicks ⚡ Tip on a card:
- The server calls
POST /v1/invoiceson a connected LND testnet node, generating a BOLT11 payment request - The UI renders the invoice as a QR code — scan it with any Lightning wallet (Phoenix, Muun, Zeus) to send the sats
- The server polls
GET /v1/invoice/{hash}every 2 seconds until LND reports the invoice settled - The tip is recorded in the PlayerGraph database with the payment hash
Tipper LND Node (testnet) Tippee
│ │ │
│── POST /api/tips/invoice ───►│ │
│◄── BOLT11 + QR code ─────────│ │
│ │ │
│ [scan QR in Lightning wallet] │
│─────── Lightning payment ────────────────────────────►│
│ │ │
│── GET /api/tips/check ──────►│ │
│◄── { status: "paid" } ───────│ │
All payments run on testnet — no real money is involved. The server connects to LND via its REST API using a macaroon for authentication and a TLS certificate for transport security.
If no LND node is configured, the UI runs in demo mode: a realistic lntb… invoice string is generated and displayed, and a "Simulate Payment" button stands in for an actual wallet.
LND is configured via environment variables in server/.env:
LND_REST_HOST=localhost:8080
LND_MACAROON_HEX=<hex-encoded invoice macaroon>
LND_TLS_CERT_BASE64=<base64-encoded tls.cert> # optional
See server/.env.example for setup instructions.
The current demo runs on a single local node. In production, the PlayerGraph Chain would be replicated across a peer-to-peer network:
[Node A] [Node B] [Node C] [Node D]
\ | | /
PlayerGraph Chain
Each node independently validates proof-of-work and signature integrity before accepting any block. No single node — including PlayerGraph's own infrastructure — can rewrite history without the rest of the network rejecting the fork.
Players can run their own node and hold a personal copy of their complete match history.
Who runs the nodes? Game providers are the natural operators:
| Node operator | Incentive |
|---|---|
| Valve / Steam | Their match data becomes portable and trusted across platforms — increasing Steam's relevance as an identity layer |
| Riot Games | Cross-game reputation makes Valorant players more invested in their account; reduces smurf accounts |
| Epic Games | Fortnite players carry verified history into new titles without starting from zero |
| Independent platforms | Tournament organizers, esports leagues, and community servers can anchor results without relying on a single publisher |
| Players | Anyone can run a node and hold a personal, uncensorable copy of their own history |
No provider controls the network alone. A publisher who tries to alter a player's history would be rejected by every other node — the same way no single bank can rewrite the Bitcoin ledger.
In the demo, matches are simulated locally. In production, match records would be sourced from official game APIs via trusted oracles — services that fetch, sign, and submit match proofs without any player being able to fabricate a result:
Riot API ──► Oracle Node (fetches + signs) ──► PlayerGraph Chain
Multiple independent oracles attest to each match. A threshold of 3-of-5 oracles agreeing is required before a match block is accepted — so no single oracle can forge a result.
The PlayerGraph Chain is anchored to the Bitcoin Testnet blockchain in real-time from the Block Explorer.
When you click Anchor to Bitcoin, the server:
- Takes the current chain tip hash (the latest block's SHA-256 hash)
- Constructs a Bitcoin transaction with an
OP_RETURNoutput embedding that 32-byte hash - Broadcasts it to the Bitcoin Testnet network via the Blockstream API
- Records the transaction ID so it can never be re-anchored for the same chain state
PlayerGraph Chain tip hash
↓
Bitcoin OP_RETURN tx
(0 satoshis output, 1000 sat fee)
↓
Testnet blockchain
(immutable, public, permanent)
This means the entire PlayerGraph history — every match, every rating, every signature — is timestamped on the most battle-tested public ledger in existence. Anyone can look up the transaction on any Bitcoin block explorer and verify that this exact chain state existed at that moment in time.
No match data is stored on Bitcoin. Only the 32-byte hash is anchored — making it extremely cheap (a fraction of a cent on mainnet) while providing cryptographic proof of the full history.
On mainnet this would cost ~$0.01–0.05 per anchor. For production, anchoring could run automatically every hour or after every N blocks.
- Lobby view — ShadowFox enters a lobby of 9 other players. Familiarity badges and trust warnings appear immediately based on cross-game history and friend ratings.
- Player cards — Click any player to open their full timeline: every match you've shared, the score, which team you were on, which friends also played with them, and all ratings given and received.
- Simulate Match — Generates a real match block, mines it with proof-of-work, and anchors it to the chain.
- Rate teammates — Ratings are signed with the player's keypair and anchored as a separate block.
- Block Explorer — Live view of the PlayerGraph Chain showing block IDs, types, hashes, and nonces.
- Anchor to Bitcoin — Click "Anchor to Bitcoin" in the Block Explorer to publish the current chain tip hash to the Bitcoin Testnet blockchain via
OP_RETURN. The transaction ID links directly to Blockstream's explorer.
- Game API oracle implementation — Integrate with Steam Web API, Riot Games API, and Epic Games API to pull real match data.
- P2P node network — Build a gossip protocol so multiple nodes can replicate the chain. Players who run a node hold their own history independent of PlayerGraph's servers.
- Threshold oracle signing — Require 3-of-5 independent oracles to sign a match proof before it's accepted, eliminating any single point of trust.
- Matchmaking integration — Work with game providers to use PlayerGraph trust scores during matchmaking. Players who've rated each other positively get paired together more often, building persistent respectful communities.
- Cross-game friend discovery — Surface "you've played 12 games with this person across CS2 and Valorant but haven't added them as a friend" recommendations.
- DAO governance — Let long-standing players vote on oracle operators, mining difficulty adjustments, and protocol changes, so no company controls the rules of the network.




