
PlayFrens
Play poker with frens via ENS names. Zero gas per hand, instant settlement on Yellow Network.
Demo
About
# PlayFrens — On-Chain Multiplayer Gaming Without Gas Fees **PlayFrens** is an on-chain multiplayer game platform that solves one of the biggest UX problems in blockchain gaming — **gas fees and latency on every action**. Built on **Yellow Network state channels (ERC-7824)**, PlayFrens lets friends find each other by ENS name, deposit tokens once, and play unlimited poker hands with **zero gas fees** and **instant settlement**. --- ## Judge Clarification An earlier experimental project was developed within the GetFrens monorepo (https://github.com/getfrens/monorepo), but it was based on a different and more complex concept — a MetaMask-like wallet extension designed to interact with memecoins directly from Twitter posts. Due to technical complexity, unresolved blockers, and reduced team interest, that effort was discontinued. PlayFrens is a completely new project built independently from scratch by me (Deven Rathod) as a solo developer for this hackathon submission. All gameplay, state channel integration, and architecture described here were implemented specifically for this project. No production code from the earlier repository is reused in this submission. ## Core Insight A 4-player poker game with 100 hands should produce **zero on-chain transactions during gameplay**. Only the **initial deposit** and **final withdrawal** touch the blockchain. Everything in between — dealing cards, placing bets, showdowns, and pot distribution — happens **off-chain** through cryptographically signed state updates on Yellow Network’s **Clearnode**, giving players: - The **speed of a centralized server** - The **security of on-chain funds** --- ## How a Typical Session Works 1. Players connect their wallet and authorize with Yellow Network. 2. They create or join a poker table. 3. The host sets the buy-in, blinds, and chip denominations. 4. When the first hand is dealt, all players co-sign a multi-party app session that locks their buy-in from their **Unified Balance on Clearnode**. 5. Gameplay happens in real-time via **Socket.io**: - Check - Bet - Fold - Raise 6. The server computes updated chip allocations and submits signed state updates to Clearnode after each hand. **Result:** No gas. No block confirmations. Just instant poker. When the session ends: - Funds return to each player’s Unified Balance proportional to their chip count. - Players can withdraw back to their wallet at any time. --- ## Social Layer (ENS Integration) PlayFrens integrates **ENS** to make multiplayer discovery seamless: - Search for friends by their `.eth` name - Display ENS avatars at the poker table - Invite friends directly to rooms --- ## Game-Agnostic Architecture While poker is the first game, the platform is designed to support **any turn-based game that tracks chip or score balances**. The following components are reusable across games: - Fund flow - Session management - Settlement layer --- ## Smart Contract Usage PlayFrens does **not** deploy custom smart contracts. Instead, it uses Yellow Network’s existing infrastructure: - **Custody Contract** — fund security - **Adjudicator Contract** — dispute resolution - **ytest.usd** — game token All deployments currently run on **Base Sepolia**.
How it's made
# PlayFrens Technical Architecture PlayFrens is built as a **pnpm monorepo** powered by **Turborepo**, split into three main packages: - **Frontend** — React 19 + Vite - **Game Server** — Node.js - **Shared Package** — Types and constants compiled with **tsup** --- ## Poker Engine & Real-Time Layer The server wraps the **poker-ts** library inside a `PokerRoom` class that extends an abstract `GameRoom` base. All game logic runs server-side: - The **poker-ts engine** handles: - Card dealing - Betting rounds - Showdowns - Pot calculation Game state is broadcast individually via **Socket.io** so that: - Each player only sees their own hole cards - Opponents’ cards remain hidden until showdown ### Fault Tolerance The server supports **deferred player removal**: - If a player disconnects mid-hand: - They are automatically folded when their turn arrives - They are cleanly removed once the hand completes --- ## Yellow Network Integration This is the core infrastructure layer. The server runs a **YellowClient** that maintains a persistent WebSocket connection to **Clearnode** and authenticates using **EIP-712 signed challenges**. The server wallet acts as a **Trusted Judge** with: - weight = 100 - quorum = 100 This makes the server the sole authority on state updates during gameplay, while still requiring all players to co-sign the initial session creation. --- ## Multi-Party Signing Flow The initial session creation involves a coordinated multi-signature process: 1. When the host clicks **Deal Cards** for the first time: - The server prepares an app session request using `createAppSessionMessage` from `@erc7824/nitrolite`. 2. The server signs the request with its own session key using raw ECDSA (`createECDSAMessageSigner`, not EIP-191 prefixed). 3. The unsigned payload is broadcast to each player's browser via Socket.io. 4. Each browser: - Uses its own `YellowRpcClient` - Authenticates independently with Clearnode using an ephemeral session key - Signs the payload locally 5. The server’s `YellowSessionManager`: - Collects all player signatures - Bundles them into a single multi-sig message containing the request and all signatures 6. The bundled request is submitted to Clearnode. 7. Clearnode: - Recovers each signer - Maps session keys to wallet addresses - Verifies all required signatures - Creates the app session After the initial co-sign: - Subsequent hands start instantly - The server submits state updates unilaterally as the Trusted Judge --- ## Important Implementation Detail All addresses sent to Clearnode must be **EIP-55 checksummed**, as Clearnode performs case-sensitive comparisons. PlayFrens normalizes addresses using `getAddress()` from **viem** before any RPC call. --- ## State Updates & Settlement After every hand: - The server calls `submitAppState` with updated chip-to-token allocations: playerChips × chipUnit = ytest.usd amount When a player cashes out: - `closeAppSession` sends final allocations - Funds return to each player's **Unified Balance** --- ## ENS Integration **wagmi** is configured with: - **Base Sepolia** (gameplay) - **Ethereum Mainnet** (ENS resolution) The frontend uses: - `useEnsName` - `useEnsAvatar` - `useEnsAddress` These enable: - Friend search by ENS - Player identity display The RainbowKit chain selector is hidden using `chainStatus="none"` so users are not confused by the dual-chain setup, while mainnet remains enabled for ENS lookups. --- ## Frontend Stack - **React 19** - **Tailwind CSS v4** - Uses `@import "tailwindcss"` and `@theme` block syntax - No `tailwind.config.js` - **Motion (Framer Motion v11+)** for animations - **RainbowKit v2 + wagmi v2** for wallet connectivity The app uses **state-driven routing**: - Lobby - Room - Game Views are rendered based on game state rather than URL routing. --- ## Deposit & Withdrawal Flow Players can obtain testnet tokens via: - Clearnode faucet endpoint - Or a real on-chain deposit using ERC20 `approve` + `Custody.deposit()` Contracts and ABIs: - CustodyAbi - Erc20Abi - Exported from `@erc7824/nitrolite` Withdrawals use: - `Custody.withdraw()` to return tokens to the wallet. Frontend hooks: - `useCustody` - `useYellow` These manage deposit, withdrawal, and balance operations. --- ## Deployment The game server is containerized using a **multi-stage Dockerfile**: - Builds shared and server packages with **tsup** - Produces a slim production image Deployment: - Hosted on **Fly.io** - WebSocket support enabled - Auto-scaling configured