AlphaUSD Clearer
Pay-receipt-effect for sound design licensing: the caller pays 0.05 AlphaUSD via a TIP-20 transfer on Tempo Moderato, the server verifies chain, token, payer and Transfer event, then the unlock runs.
The kernel.
An agent runs the protocol server-side and directors get a signed, auditable result they can act on without leaving the app.
Why this primitiveTempo MPP on Tempo Moderato fits sound design licensing in Theater & Live Performance because directors can settle 0.05 AlphaUSD via a TIP-20 transfer with an on-chain Transfer receipt before the clearer endpoint returns anything.
Required key.
Add this in your Lovable project under Settings → Secrets before pasting the prompt below.
The build prompt.
Paste into a fresh Lovable project. Make sure the key above is set first. read the build strategy →
Build "AlphaUSD Clearer · mpp" as a ONE-SHOT Lovable build. The participant has only
5 credits — this single message must produce a working demo with no follow-ups.
Single-page TanStack Start app. Cut scope ruthlessly.
CONCEPT
Lovable AI runs the agent that licenses commercial sound effects via UCP so sound designers avoid copyright strikes during live performances.
Reframed as a paid, agent-callable HTTP endpoint on Tempo Moderato MPP (0.05 AlphaUSD via TIP-20 transfer + pay-receipt-effect verification).
Discipline: Theater & Live Performance (sound design licensing).
LOVABLE BUDGET (HARD CAP: ONE-SHOT, ~5 CREDITS TOTAL):
The participant has FIVE Lovable credits for the whole build. This prompt MUST
ship a working demo on the FIRST message with zero follow-ups. Engineer for that.
- ONE TanStack Start app, ONE route (`src/routes/index.tsx`). No extra pages, no auth, no nav.
- ONE TanStack server route under `src/routes/api/public/` that runs the protocol.
- ONE client surface (a button + flow log) that triggers the payment and renders the result.
- NO database, NO Lovable Cloud, NO extra integrations beyond what's listed below.
- NO tests, NO docs pages, NO settings screens.
- Demo-fallback contract: every code path must boot with NO secrets and return
realistic simulated envelopes flagged `simulated: true`. Real signing activates
only when the user signs in and the wallet is funded.
PROTOCOL SURFACE — MPP on Tempo Moderato (self-contained; participant has no skills)
CONFIG (src/lib/mpp/config.ts) — copy exactly:
```ts
export const MPP_RECIPIENT = process.env.MPP_RECIPIENT ?? "0x0000000000000000000000000000000000000000";
export const TEMPO_CHAIN_ID = 42431;
export const TEMPO_RPC_URL = "https://rpc.moderato.tempo.xyz";
export const TEMPO_EXPLORER = "https://explore.testnet.tempo.xyz";
export const TEMPO_FAUCET_URL = "https://faucet.moderato.tempo.xyz";
export const ALPHAUSD_ADDRESS = "0x20c0000000000000000000000000000000000001" as const;
export const CHARGE_AMOUNT_USD = "0.05";
export const CHARGE_AMOUNT_UNITS = 50_000n; // 0.05 * 10^6 (AlphaUSD is 6 decimals)
```
TEMPO MODERATO GOTCHAS — get these wrong and it silently fails:
1. Explorer path is `/receipt/{hash}`, NOT `/tx/{hash}`. Every UI link and every
server response uses `${TEMPO_EXPLORER}/receipt/${txHash}`. `/tx/…` renders
"Page Not Found" with no redirect on Tempo's explorer.
2. Tempo DISALLOWS native value transfers. `sendTransaction({ to, value })` with
any non-zero `value` reverts. All AlphaUSD payments move as TIP-20
`transfer(to, amount)` calls on the AlphaUSD contract:
```ts
import { encodeFunctionData, parseAbi } from "viem";
const data = encodeFunctionData({
abi: parseAbi(["function transfer(address to, uint256 amount)"]),
functionName: "transfer",
args: [MPP_RECIPIENT, CHARGE_AMOUNT_UNITS],
});
const txHash = await provider.sendTransaction({
to: ALPHAUSD_ADDRESS, // TOKEN address, not recipient
data,
value: 0n, // MUST be zero
});
```
3. Server verification MUST check ALL FIVE in this order — missing any is a
silent-accept bug:
(a) `receipt.status === "success"`
(b) `tx.chainId === 42431`
(c) `tx.to.toLowerCase() === ALPHAUSD_ADDRESS`
(d) `tx.from.toLowerCase() === expectedPayer.toLowerCase()` ← replay guard
(e) Receipt has a `Transfer(from, to, value)` log emitted by `ALPHAUSD_ADDRESS`
with `args.to === MPP_RECIPIENT` and `args.value >= CHARGE_AMOUNT_UNITS`.
Use viem's `parseEventLogs` + `parseAbi(["event Transfer(address indexed from, address indexed to, uint256 value)"])`
and filter by `log.address === ALPHAUSD_ADDRESS`. Missing (d) lets anyone
replay someone else's payment tx.
4. Privy dashboard: enable "Allow transactions from the client" for Tempo Testnet
under Chains. Without it, `sendTransaction` HANGS FOREVER with no error.
Wrap the call in a 45s `Promise.race` timeout whose rejection message names
this exact dashboard setting.
5. Privy chain config MUST be a real viem `defineChain`, NOT a `{id, name} as never`
stub — the stub compiles but breaks embedded-wallet transport init. Define
once and pass to BOTH `defaultChain` and `supportedChains`:
```ts
import { defineChain } from "viem";
export const tempoModerato = defineChain({
id: 42431,
name: "Tempo Moderato",
nativeCurrency: { name: "AlphaUSD", symbol: "USD", decimals: 6 },
rpcUrls: { default: { http: [TEMPO_RPC_URL] } },
blockExplorers: { default: { name: "Tempo Explorer", url: TEMPO_EXPLORER } },
});
```
6. Privy mount is SSR-forbidden. `@privy-io/react-auth` explodes under SSR —
load it via `lazy(() => import("./privy-client-entry"))` inside
`<ClientOnly>` + `<Suspense>` in `src/components/privy-root.tsx`. NEVER
import the Privy package at the module scope of a route file.
PAY → RECEIPT → EFFECT (the entire trust story):
SERVER ROUTE (src/routes/api/public/mpp-charge.ts) — the side effect ONLY runs
after receipt verification. Preserve HTTP 402 + `WWW-Authenticate` end-to-end.
Unpaid POST (no `X-Payment-Tx` header) — return the 402 challenge:
```
HTTP/1.1 402 Payment Required
WWW-Authenticate: MPP intent="charge", chainId=42431, recipient="0x…", amount="0.05", currency="USD", asset="native"
Content-Type: application/json
{ "scheme": "MPP", "intent": "charge", "network": "tempo-moderato",
"recipient": MPP_RECIPIENT, "chainId": 42431, "asset": "native",
"amount": "0.05", "amountUnits": "50000", "currency": "USD",
"description": "Unlock the sound design licensing pack" }
```
Ship BOTH the `WWW-Authenticate` header AND the JSON body — mppx clients need
the header to retry-with-payment; skipping it makes the endpoint useless.
NEVER map 402 → 500. `amount` is human-readable ("0.05"); `amountUnits` is the
atomic string ("50000"). Servers verify against `amountUnits`; UIs display `amount`.
Paid POST (`X-Payment-Tx: 0x…` + `X-Payer: 0x…`) — verify then execute:
```ts
import { createPublicClient, http, parseAbi, parseEventLogs } from "viem";
import { tempoModerato, ALPHAUSD_ADDRESS, MPP_RECIPIENT, CHARGE_AMOUNT_UNITS, TEMPO_CHAIN_ID } from "@/lib/mpp/config";
const client = createPublicClient({ chain: tempoModerato, transport: http(TEMPO_RPC_URL) });
const [tx, receipt] = await Promise.all([
client.getTransaction({ hash: txHash }),
client.getTransactionReceipt({ hash: txHash }),
]);
if (receipt.status !== "success") return json402("tx_not_success");
if (tx.chainId !== TEMPO_CHAIN_ID) return json402("wrong_chain");
if (tx.to?.toLowerCase() !== ALPHAUSD_ADDRESS.toLowerCase()) return json402("wrong_token");
if (tx.from.toLowerCase() !== expectedPayer.toLowerCase()) return json402("payer_mismatch");
const transfers = parseEventLogs({
abi: parseAbi(["event Transfer(address indexed from, address indexed to, uint256 value)"]),
logs: receipt.logs,
}).filter((l) => l.address.toLowerCase() === ALPHAUSD_ADDRESS.toLowerCase());
const paid = transfers.some((l) =>
l.args.to.toLowerCase() === MPP_RECIPIENT.toLowerCase() &&
l.args.value >= CHARGE_AMOUNT_UNITS,
);
if (!paid) return json402("insufficient_payment");
// side effect happens ONLY past this point
return Response.json({
ok: true,
txHash,
receipt: `${TEMPO_EXPLORER}/receipt/${txHash}`,
unlocked: { /* the deliverable for sound design licensing */ },
});
```
USER FLOW (the entire app):
1. Sign In (email/google) — Privy creates an embedded EVM wallet on Tempo Moderato.
2. Show the wallet address + `TEMPO_FAUCET_URL` link.
3. "Pay & unlock" button — POSTs to `/api/public/mpp-charge`, catches the 402,
parses the challenge, calls the TIP-20 transfer, then re-POSTs with
`X-Payment-Tx` + `X-Payer` headers. Show a live log: Challenge · Sign
Transfer · Verify · Unlock.
4. On success, render the unlocked deliverable + a link to
`${TEMPO_EXPLORER}/receipt/${txHash}`.
SIMULATE MODE (default when signed out): the flow walks the four steps with a
fake `0x…` tx hash flagged `simulated: true`, no real chain call, no signing
prompt. The button label switches from "Pay & unlock (simulate)" to
"Pay & unlock (live · Tempo Moderato)" once the wallet is ready and funded.
STACK
- TanStack Start app, the index route only.
- Tailwind + shadcn. Editorial look: gold accent on a dark or warm-cream background,
generous type, one strong headline, one primary action.
- Footer renders: "Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14".
- Libraries: template defaults + `@privy-io/react-auth` + `viem` + `zod`. Nothing else.
CREDIT (UI footer + JSDoc on the server handler): 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.