REELCAST · x402
A pay-per-view casting vault where actors pay to pin their career to IPFS and casting directors pay to unlock high-res reels. No subscriptions, just 0.01 USDC to view a performance or drop a digital headshot into a production folder. Facilitator handles the EIP-3009 signature, ensuring actors are compensated for 'audition data' and agents pay for access.
The primitive.
The onchain primitive runs at the right moment in the flow and surfaces a clear, verifiable result that directors can act on without web3 jargon.
Why this primitiveLegacy casting platforms lock talent behind monthly fees; this meters the interaction. By treating every click on a reel as a transaction, it filter-proofs the talent pool and creates a micro-economy for talent scouts.
Required keys.
Add these in your Lovable project under Settings → Secrets before pasting the prompt below.
The build prompt.
Paste into a fresh Lovable project. Make sure all five secrets above are set first. read the build strategy →
Build "REELCAST" in ONE Lovable message. Single-page x402-native paid app on Arbitrum Sepolia.
CONCEPT
A pay-per-view casting vault where actors pay to pin their career to IPFS and casting directors pay to unlock high-res reels. No subscriptions, just 0.01 USDC to view a performance or drop a digital headshot into a production folder. Facilitator handles the EIP-3009 signature, ensuring actors are compensated for 'audition data' and agents pay for access.
Discipline: Theater & Live Performance (performance showcase).
Onchain primitive: x402 micropayments on Arbitrum Sepolia (USDC via EIP-3009). Why: Legacy casting platforms lock talent behind monthly fees; this meters the interaction. By treating every click on a reel as a transaction, it filter-proofs the talent pool and creates a micro-economy for talent scouts.
5-CREDIT BUDGET (HARD LIMIT):
- ONE single-page app. No Lovable Cloud, no database, no auth flows beyond Privy drop-in.
- NO smart contract deploy. x402 settles USDC via EIP-3009 `transferWithAuthorization` — the USDC contract already exists at 0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d on Arbitrum Sepolia (chainId 421614).
- Privy is the auth + signing layer (Google login + embedded wallet on Arbitrum Sepolia).
- At most ONE AI call per user action (Lovable AI Gateway with LOVABLE_API_KEY, only if AI is part of the idea).
- Skip tests, skip CI, skip docs. Ship the demo.
STACK
- React + Vite + TanStack Start (the template Lovable ships).
- Privy embedded wallet wraps <App /> in src/main.tsx:
<PrivyProvider appId={import.meta.env.VITE_PRIVY_APP_ID}
config={{ loginMethods:['google'], embeddedWallets:{createOnLogin:'users-without-wallets'},
defaultChain:{ id: 421614, name:'Arbitrum Sepolia' } }}>
- viem public client uses Alchemy Arbitrum Sepolia — put the URL in src/data/rpc.json as
{ "arbitrumSepolia": "https://arb-sepolia.g.alchemy.com/v2/<KEY>" } and use
createPublicClient({ chain: arbitrumSepolia, transport: http(rpc.arbitrumSepolia) }).
The default public RPC is rate-limited — Alchemy is required for USDC balanceOf.
TEN NON-OBVIOUS x402 RULES (get these wrong and it silently fails)
1. CORS: proxy the facilitator. Public x402 facilitators (including x402.payai.network) do NOT send
Access-Control-Allow-Origin. A direct browser fetch throws "TypeError: Failed to fetch" BEFORE
you see the 402. Wrap the upstream call in a same-origin TanStack server route at
src/routes/api/public/x402-proxy.ts and forward PAYMENT-SIGNATURE (request) + PAYMENT-RESPONSE (response).
2. x402 v2 envelope shape. The base64-encoded PAYMENT-SIGNATURE payload is NOT
{ scheme, network, payload } at top level — that's v1 and PayAI rejects it as invalid_payload.
It MUST be:
{ "x402Version": 2,
"accepted": { /* the full PaymentRequirement you picked, echoed back verbatim */ },
"payload": { "signature": "0x…",
"authorization": { from, to, value, validAfter, validBefore, nonce } } }
3. Network id is CAIP-2: "eip155:421614" for Arbitrum Sepolia (NOT "arbitrum-sepolia"). Match on this
when picking a requirement from accepts[].
4. Amount field renamed. v2 uses `amount` (atomic units, string), NOT v1's `maxAmountRequired`.
USDC has 6 decimals — "10000" = 0.01 USDC.
5. Header names are literal-cased and non-standard: PAYMENT-SIGNATURE (request) and PAYMENT-RESPONSE (response).
6. EIP-3009 domain fields come from requirement.extra. Use extra.name ("USDC") and extra.version ("2")
in the EIP-712 domain — NOT hardcoded. chainId is 421614; verifyingContract is requirement.asset.
7. nonce is bytes32 random, generated client-side (crypto.getRandomValues(new Uint8Array(32)) → 0x-hex).
Never reuse. validAfter = now-60s, validBefore = now + maxTimeoutSeconds (default 300).
8. Sign via Privy embedded wallet's provider, NOT React hooks: get the EIP-1193 provider
(const provider = await embedded.getEthereumProvider()) and call
provider.request({ method: "eth_signTypedData_v4", params: [address, JSON.stringify(typedData)] }).
9. Alchemy RPC in src/data/rpc.json (rule above). USDC balanceOf against the default public RPC will flake.
10. Fund flow uses Circle faucet (https://faucet.circle.com/, choose Arbitrum Sepolia). Show the user's
Privy address prominently, link to the faucet, then a "Refresh balance" button. ~10s arrival.
FILE LAYOUT
src/data/x402.json { endpoint, proxy, usdcAddress: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
chainId: 421614, network: "eip155:421614",
faucetUrl: "https://faucet.circle.com/",
explorer: "https://sepolia.arbiscan.io" }
src/data/rpc.json { "arbitrumSepolia": "https://arb-sepolia.g.alchemy.com/v2/<KEY>" }
src/lib/x402.ts fetchChallenge / pickRequirement / signPayment / fetchPaid
src/routes/api/public/x402-proxy.ts same-origin GET proxy (below)
src/routes/index.tsx demo UI: sign-in → fund → 4-step flow log
PROXY ROUTE (drop-in — copy verbatim):
```ts
// src/routes/api/public/x402-proxy.ts
import { createFileRoute } from "@tanstack/react-router";
import x402Cfg from "@/data/x402.json";
export const Route = createFileRoute("/api/public/x402-proxy")({
server: {
handlers: {
GET: async ({ request }) => {
const sig = request.headers.get("PAYMENT-SIGNATURE");
const upstream = await fetch(x402Cfg.endpoint, {
method: "GET",
headers: sig ? { "PAYMENT-SIGNATURE": sig } : {},
});
const body = await upstream.arrayBuffer();
const out = new Headers();
const ct = upstream.headers.get("content-type");
if (ct) out.set("Content-Type", ct);
const pr = upstream.headers.get("PAYMENT-RESPONSE");
if (pr) out.set("PAYMENT-RESPONSE", pr);
return new Response(body, { status: upstream.status, headers: out });
},
},
},
});
```
The /api/public/* prefix bypasses Lovable's published-site auth — desired for a demo endpoint.
USER FLOW (log every step in the UI so the user sees what happened)
1. Land on page → "Sign in with Google" (Privy) → embedded wallet auto-provisioned on Arbitrum Sepolia.
2. Fund: show wallet address + "Get 0.01 USDC on Arbitrum Sepolia" → link to https://faucet.circle.com/
→ "Refresh balance" button reads USDC balanceOf via viem + Alchemy RPC.
3. Click the primary action button for this idea (performance showcase). App runs:
(a) Challenge — GET /api/public/x402-proxy → expect 402 → parse { x402Version:2, accepts:[…], error }.
Pick accepts[] where network === "eip155:421614" && scheme === "exact".
(b) Sign — Build EIP-3009 typed data (see rules 6–8), call Privy signTypedData,
wrap into the v2 envelope (rule 2), base64.
(c) Retry — GET /api/public/x402-proxy with header PAYMENT-SIGNATURE: <base64>.
(d) Settle — On 200, read PAYMENT-RESPONSE header, base64-decode →
{ success, transaction, network, payer }. Link tx to `${explorer}/tx/${transaction}`.
4. Footer renders: "Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14"
FACILITATOR
Default endpoint: https://x402.payai.network/api/arbitrum-sepolia/paid-content (returns 402 with an
accepts[] challenge, settles 0.01 USDC per successful call, returns the protected payload).
REQUIRED SECRETS (Lovable → Project Settings → Secrets):
- PRIVY_APP_ID Google sign-in + embedded wallet. Docs: https://docs.privy.io/llms-full.txt
- ALCHEMY_ARBITRUM_SEPOLIA_RPC_URL Free at https://dashboard.alchemy.com/ (create app, Arbitrum Sepolia,
copy HTTPS URL). Also expose as VITE_ALCHEMY_ARBITRUM_SEPOLIA_RPC_URL
so the frontend viem client can read balances.
COMMON FAILURE MODES (fix these before shipping)
- "TypeError: Failed to fetch" at step (a): called the facilitator directly from the browser. Use the proxy.
- "invalid_payload" at step (c): sent v1 envelope. Wrap under `accepted` (rule 2).
- "invalid_signature": wrong domain — chainId mismatch, or extra.version hardcoded to "1".
Use chainId 421614 and extra.version from the requirement.
- "insufficient_funds": wallet has ETH but no USDC. Fund via Circle faucet, then Refresh balance.
- Balance stays at 0 after faucet: reading against default public RPC. Wire Alchemy via rpc.json.
- expires_at errors on retry: clock skew or reused nonce. Generate fresh nonce + timestamps per attempt.
CREDIT (must appear in UI footer):
Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
Market sizing.
Indicative figures for hackathon pitches — refine with your own research before raising.