Build Notes 🛠️
How I Built This App
I built PolyTeacher as a practical, step-by-step onboarding experience for new developers who want to place a first Polymarket trade using an EOA flow. This guide documents exactly how I structured the app and why each step exists.
Tooling Note (Cursor + model)
I built this app in Cursor and used gpt-5.3-codex as my coding assistant model for implementation and iterative refinement.
0) Project Bootstrap (Next.js + RainbowKit)
I started with RainbowKit's starter so wallet UX was solved first, then layered Polymarket logic on top.
npm init @rainbow-me/rainbowkit@latestWhy this matters: first-time builders often get blocked on wallet setup. Starting here gave me working wallet connection, chain config, and sane defaults before I touched trading logic.
1) The Product Goal
My goal was to teach the full path from "wallet connected" to "trade submitted" without making the user dig through docs first. The app is intentionally linear:
- Discover event (Gamma)
- Select specific market + outcome (Gamma)
- Derive CLOB credentials from wallet signature (CLOB L1 auth)
- Set on-chain approvals on Polygon (ERC20 + ERC1155)
- Submit a BUY market order via CLOB V2 using pUSD notional input (L2 authenticated request)
- Read position from Data API to confirm the result
2) API Routes I Added (and Why)
I use a thin API-proxy layer inside Next.js so the frontend talks to stable local routes, not directly to every external endpoint. This improves reliability, centralizes logging, and avoids CORS headaches.
/api/gamma/markets- Step 1 market/event discovery.
Supportslimit,tutorial, andpreset=specificso I can control which events appear in the tutorial./api/gamma/event-markets?slug=...- Step 2 sub-market list for a chosen event.
Keeps event selection and market selection as two separate learning actions./api/gamma/market?marketId=...- Focused market refresh/polling.
Used to keep YES/NO prices updated in the right rail every few seconds./api/data/positions?user=0x...&limit=5- Sidebar position snapshot.
Normalizes/validates the address and returns a consistent shape for UI rendering.
3) EOA Auth Flow for CLOB (Critical Step)
Wallet connection alone is not enough to trade on CLOB. I first create an L1 wallet-authenticated client, then derive or fetch API credentials tied to that EOA signature:
const l1Client = createL1Client(walletClient);
const creds = await l1Client.createOrDeriveApiKey();Why this matters: these credentials are what authorize subsequent trading calls. I surface this explicitly as its own tutorial step so builders understand "connect wallet" and "authenticate for trading" are separate concerns.
4) On-Chain Approvals Before Trading
Before placing orders, I run one-time Polygon approvals for pUSD and CTF across the V2 exchange contracts used by normal and negative-risk markets. If a wallet starts with only USDC.e, I wrap to pUSD first via Collateral Onramp. In-app, I execute six approval transactions and verify completion.
- pUSD
approveto CLOB Exchange V2 - CTF
setApprovalForAllto CLOB Exchange V2 - pUSD
approveto Neg-Risk Exchange V2 - CTF
setApprovalForAllto Neg-Risk Exchange V2 - pUSD
approveto Neg-Risk Adapter - CTF
setApprovalForAllto Neg-Risk Adapter
Why this matters: the most common first-trade failure is missing allowance. I built clear progress text and post-transaction verification so builders can debug this quickly.
5) Order Execution: First Trade
I keep order entry simple: user enters pUSD amount, and the app computes estimated shares from the current outcome price. Then I submit a BUY market order via the L2 client using pUSD amount as notional:
const l2Client = createL2Client(walletClient, creds);
await l2Client.createAndPostMarketOrder({
tokenID: activeTokenId,
side: Side.BUY,
amount: numericUsdcAmount,
}, undefined, OrderType.FAK);Why this matters: first-time developers usually overcomplicate order sizing. Framing it as "how much do you want to spend?" is easier to reason about and maps cleanly to user intent. FAK behavior also avoids leaving unexpected resting orders when only partial liquidity is available.
6) Confirming Trade Outcome
After submission, I show a human-readable summary (requested spend, estimated shares, matched shares, status, and tx link) instead of raw JSON. I also dispatch an in-app event to refresh positions:
window.dispatchEvent(new CustomEvent('polyteacher:trade-executed'));The sidebar then re-reads Data API positions so the learner immediately sees the top open position update. That closes the learning loop from order click to visible portfolio impact.
7) Recommended Build Order for New Developers
- Bootstrap with RainbowKit starter and verify wallet connect/disconnect.
- Add Gamma proxy routes and render event/market selection UI.
- Implement
createOrDeriveApiKey()and persist creds in app state. - Add allowance transactions + on-chain verification.
- Wire
createAndPostMarketOrder()with clean error handling. - Add Data API positions for post-trade confirmation and confidence.
8) What I'd Improve Next
- Persist tutorial progress per wallet.
- Add richer post-trade analytics and position history.
- Improve mobile-specific layout for dense steps.