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.
Set NEXT_PUBLIC_HOW_I_BUILT_VIDEO_URL to replace the embedded video.
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 order via CLOB (L2 authenticated request)
- Read position from Data API to confirm the result
Events vs Markets (Quick Glossary)
In Polymarket terms, an event is the parent prediction topic, and a market is a specific tradable outcome under that event.
- Event: The umbrella question/theme (for example, an election, CPI release, or sports match).
- Market: A specific YES/NO contract you can buy or sell inside that event.
In this app, Step 1 selects the event first so the learner understands context, then Step 2 picks one specific market to trade.
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 USDC.e and CTF across the exchange contracts used by normal and negative-risk markets. In-app, I execute six transactions and verify completion.
- USDC.e
approveto CLOB Exchange - CTF
setApprovalForAllto CLOB Exchange - USDC.e
approveto Neg-Risk Exchange - CTF
setApprovalForAllto Neg-Risk Exchange - USDC.e
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 USDC.e amount, and the app computes estimated shares from current outcome price. Then I submit a BUY order via the L2 client:
const l2Client = createL2Client(walletClient, creds);
await l2Client.createAndPostOrder({
tokenID: activeTokenId,
side: Side.BUY,
price: marketOrderPrice,
size: estimatedShares,
});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.
6) Confirming Trade Outcome
After submission, I show a human-readable summary (status, filled shares, USDC.e spent, 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
createAndPostOrder()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.