Sponsored Beta Access
Grant beta testing access through gasless wallet authentication and sponsored invites.
Mint License Token· consumer flow
Section · Onchain
full primer →The primitive.
Game designers tap once to mint a License Token proving their right to remix or unlock the user testing; if the licensor set a royalty, downstream revenue routes back automatically.
Why this primitivePrivy wallet and sponsored tx enable friction-free beta tester onboarding.
Kernel
`client.license.mintLicenseTokens` mints a License Token from the licensor IP so a consumer can legally remix or unlock the asset; if royalties are set, `commercialRevShare` routes proceeds to the parent IP
Drives the UI as
a one-tap 'mint license' button that returns the licenseTokenId and shows the holder on the explorer
Required keys.
PRIVY_APP_ID
Publishable — hardcode into src/config.ts. Google sign-in + Aeneid embedded wallet + sponsored tx.
open ↗Hardcode both into src/config.ts (they're publishable, not secrets) before pasting the prompt below.
Appendix · Mega-prompt
The build prompt.
budget · 1 message
Paste into a fresh Lovable project. Make sure src/config.ts holds both values above. read the build strategy →
Build "Sponsored Beta Access" in ONE Lovable message. Single-page demo on the DATA Foundation (Aeneid testnet).
CONCEPT
Grant beta testing access through gasless wallet authentication and sponsored invites.
Discipline: Game Design & Interactive Media (user testing).
On-chain primitive: Mint License Token (consumer flow). Why this primitive: Privy wallet and sponsored tx enable friction-free beta tester onboarding.
ipType: GameAsset
5-CREDIT BUDGET (HARD LIMIT):
- ONE single-page app. No router, no Lovable Cloud, no database, no auth flows beyond Privy drop-in.
- Bespoke Solidity IS allowed but must stay tiny and sandbox-deployable: at most 1 contract file, ~120 LOC, no external deps beyond OpenZeppelin, no proxies, no upgradeability. Deploy it from the Lovable sandbox with Foundry (`forge script ... --rpc-url https://aeneid.storyrpc.io --broadcast --private-key $DEPLOYER_PK`) — never from the browser and never as part of the app runtime. Commit the resulting `{ address, chainId: 1315, abi }` into `src/data/<contract>.json` and import from there. For anything the SDKs already cover (register IP, license terms, mint license, CDR), keep using @story-protocol/core-sdk / @piplabs/cdr-sdk — do not re-implement those primitives in Solidity. Never redeploy Aeneid core contracts — they are already live.
- Privy is always the auth + sponsored-tx layer (Google login, embedded wallet).
- Off-chain storage: use IPFS only if the artefact must be public; use CDR (@piplabs/cdr-sdk) if it must be private.
- At most ONE AI call per user action (use Lovable AI Gateway with LOVABLE_API_KEY if AI is part of the idea).
- Skip tests, skip CI, skip docs pages. Ship the demo, nothing else.
STACK
- React + Vite single page (the index route).
- Chain: DATA Foundation Aeneid testnet. chainId: 1315. Explorer: https://aeneid.explorer.datafdn.org Block explorer: https://aeneid.datanetscan.io Faucet: https://aeneid.faucet.datafdn.org
- SDKs (install with `bun add`):
@story-protocol/core-sdk // IP Assets, License Terms, License Tokens, Royalties
@piplabs/cdr-sdk // Confidential Data Rails (encrypt / gate / decrypt) — LAZY-LOAD only
@privy-io/react-auth // Google sign-in + embedded wallet + sponsored tx
viem // walletClient wrapper around Privy embedded provider
- Custom contract path (optional — use only when the idea needs a bespoke event
/ registry / counter the SDKs don't cover; the demo runtime must still be a
pure React + Vite single page):
contracts/ # foundry project committed alongside src/
foundry.toml
src/<Name>.sol // your ~120 LOC contract, OpenZeppelin OK
script/Deploy.s.sol // forge script that broadcasts to Aeneid
Deploy step (runs inside the Lovable sandbox — no local toolchain needed):
cd contracts && forge build && \
forge script script/Deploy.s.sol \
--rpc-url https://aeneid.storyrpc.io \
--broadcast --private-key $DEPLOYER_PK
Fund the deployer once at https://aeneid.faucet.datafdn.org. Store
DEPLOYER_PK as a Lovable Project Secret; never commit it. After deploy,
write `{ address, chainId: 1315, abi }` to `src/data/<name>.json` and read
it from the single-page app via viem `readContract` / `writeContract`.
Reference: `contracts/ChoreoLedger.sol` + `contracts/script/Deploy.s.sol`
+ `src/data/choreo-ledger.json` in this repo.
- SSR-safe Privy mount is mandatory. Never import @privy-io/react-auth at
module scope of a route file — it crashes SSR. Use
lazy(() => import('./privy-client-entry')) inside <ClientOnly> + <Suspense>,
and put <PrivyProvider> only inside privy-client-entry.tsx.
- SSR-safe CDR mount is mandatory. Never top-level-import @piplabs/cdr-sdk.
Do `const cdr = await import('@piplabs/cdr-sdk')` inside the handler that
actually needs to encrypt or decrypt. The bundled WASM must load on the
client, not during SSR (breaks the published bundle otherwise).
- PrivyProvider config (do NOT stub defaultChain — chainId 1315 is passed per-call):
import { PRIVY_APP_ID } from '@/config';
<PrivyProvider appId={PRIVY_APP_ID}
config={{ loginMethods:['google','email'],
embeddedWallets:{ ethereum:{ createOnLogin:'users-without-wallets' } },
appearance:{ theme:'dark' } }}>
- Read the embedded wallet from useWallets, not user.wallet:
const embedded = wallets.find(w => w.walletClientType === 'privy');
- Bridge the Privy embedded wallet into a viem walletClient for the Story SDK:
import { AENEID_RPC_URL } from '@/config';
const provider = await embedded.getEthereumProvider();
const walletClient = createWalletClient({
account: embedded.address as `0x${string}`,
chain: { id: 1315, name: 'Story Aeneid', nativeCurrency: { name:'IP', symbol:'IP', decimals:18 },
rpcUrls:{ default:{ http:[AENEID_RPC_URL] } } },
transport: custom(provider),
});
- Instantiate the Story client (SDK auto-wires Aeneid contracts):
const story = StoryClient.newClient({
wallet: walletClient,
transport: http(AENEID_RPC_URL),
chainId: 'aeneid',
});
- Every raw sendTransaction (rare — mostly the SDK handles it) goes through
Privy `useSendTransaction` with BOTH `address` and `sponsor`, ALWAYS with
an explicit gas fallback (Privy embedded smart wallets reject gas: 0), and
wrapped in a 45s Promise.race timeout:
const gas = await publicClient.estimateGas({ to, data, account: embedded.address })
.then(g => g * 120n / 100n).catch(() => 300_000n);
await Promise.race([
sendTransaction(
{ to, data, chainId: 1315, gas },
{ address: embedded.address, sponsor: true }
),
new Promise((_, r) => setTimeout(() => r(new Error(
"Privy sendTransaction timed out after 45s. Enable Gas sponsorship -> App pays -> Story Aeneid -> Allow transactions from the client. If Aeneid sponsorship is unavailable in your workspace, drop `sponsor: true` and prefund the embedded wallet from https://aeneid.faucet.datafdn.org"
)), 45_000)),
]);
- Do NOT pass uiOptions:{ showWalletUIs:false } — it aborts with
"signal is aborted without reason". The approval sheet still shows on
the embedded-EOA path; the fee reads US$0.00.
- Do NOT add ZeroDev / SmartWalletsProvider / a paymaster URL. Native
Privy sponsorship on Aeneid works with the toggles below and nothing else.
- DASHBOARD PREREQUISITE (one-time): Privy dashboard -> Gas sponsorship
-> App pays -> add "Story Aeneid" (chainId 1315) -> toggle "Allow
transactions from the client" ON. Without this, sendTransaction hangs silently.
Fallback: if your Privy workspace does not offer Aeneid sponsorship yet,
drop `sponsor: true`, tell the user to hit the faucet
(https://aeneid.faucet.datafdn.org) once, then re-run — the fallback branch
in the timeout wrapper already handles this.
ON-CHAIN WRITE (consumer wants to legally use / remix this user testing — mint a License Token):
// Assumes the app has already registered a licensor IP with terms attached
// (either your own seed IP, or a previously-registered user testing). Store licensorIpId + licenseTermsId
// in src/data/seed.json so the demo has something to mint against.
const { licenseTokenIds, txHash } = await story.license.mintLicenseTokens({
licensorIpId,
licenseTermsId,
amount: 1,
maxMintingFee: 0n,
maxRevenueShare: 100,
receiver: embedded.address as `0x${string}`,
});
// The License Token is an NFT the consumer now owns. It gates decryption via CDR
// and proves the right to remix in downstream apps.
const ipMetadata = story.ipAsset.generateIpMetadata({
title: "Sponsored Beta Access",
description: "Grant beta testing access through gasless wallet authentication and sponsored invites.",
createdAt: Math.floor(Date.now() / 1000).toString(),
ipType: "GameAsset",
mediaUrl,
mediaHash, // sha256(await file.arrayBuffer()) as 0x-prefixed hex
mediaType: file.type,
image: previewUrl,
imageHash: mediaHash,
creators: [{ name: displayName, address: embedded.address, contributionPercent: 100 }],
tags: ["user testing", "GameAsset"],
});
USER FLOW
1. Land on page -> 'Sign in with Google' (Privy) -> embedded wallet auto-provisioned on Aeneid.
2. One-tap 'mint license' CTA. On success show the licenseTokenId, the holder address, and an aeneid.explorer.datafdn.org/ipa/<licensorIpId> link. If the licensingHook rejects (cap reached, whitelist miss), surface the revert reason verbatim.
3. Footer renders: "Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14"
CONFIG (hardcode these two publishable values; they are NOT secrets):
- Privy App ID Google sign-in + Aeneid embedded wallet. Hardcode into src/config.ts as
`export const PRIVY_APP_ID = "<your-app-id>"`. Get it at https://dashboard.privy.io
Docs: https://docs.privy.io/llms-full.txt
- Aeneid RPC URL DATA Foundation Aeneid HTTPS endpoint. Hardcode into src/config.ts as
`export const AENEID_RPC_URL = "https://aeneid.storyrpc.io"` (default works;
swap in your own provider under load).
Lovable's `VITE_*` prefix is reserved by the platform, so client-side keys go in
committed config files. Both values above are publishable — they show up in the
built JS bundle and are safe to commit.
OPTIONAL SECRET (Lovable -> Project Settings -> Secrets):
- LOVABLE_API_KEY Only if the idea makes an AI call. Auto-provisioned when
you turn on Lovable AI.
CREDIT (must appear in UI footer AND inside every IP Asset's `description` or `tags`):
Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
Market sizing.
TAM
$1.5B
game beta testing services
SAM
$300M
indie testing platforms
SOM
$60M
gasless invite systems
Indicative figures for hackathon pitches — refine with your own research before raising.
Adjacent entries.
multiplayer coordination
Gasless Guilds
Seamlessly create and join guilds with gas-free onboarding and instant member transactions.
reward distributionSponsored Loot Drops
Distribute in-game rewards directly to players' wallets without any gas fees.
XR social spacesPrivy VR Lobby
Enter virtual lobbies with gas-free wallet login and seamless social interactions.
digital fashionOnchain Avatar Store
Buy and customize avatars with zero gas fees using embedded wallets and sponsored transactions.