Script-to-Screen Sync
Sync script changes and video edits onchain for teams without forcing gas payments.
Privy social wallet on Arc· wallet UX
Section · Onchain
full primer →The primitive.
Videographers sign in with Google through Privy — no seed phrase, no MetaMask popup — and their production alignment actions are sent on Arc Testnet with USDC gas paid transparently.
Why this primitivePrivy wallet’s gasless tx make real-time script and edit synchronization user-friendly.
Kernel
Privy embedded wallet bootstrapped with PRIVY_APP_ID — Google sign-in, embedded wallet, and Arc Testnet (chain 5042002) set as the default network so users pay gas in USDC without seeing it
Drives the UI as
a one-click 'Sign in with Google' that drops the user straight into the app with an Arc wallet already funded via Circle Faucet
Required keys.
CIRCLE_API_KEY
YOU PASTE. Backend Circle Console key — bootstrap uses it to mint everything else.
open ↗CIRCLE_ENTITY_SECRET
AUTO — `node scripts/bootstrap-circle.mjs` generates + registers it via Circle's RSA pubkey.
open ↗CIRCLE_TREASURY_WALLET_ID
AUTO — bootstrap mints an ARC-TESTNET dev wallet. Then fund the address via the Circle Faucet.
open ↗VITE_CIRCLE_TREASURY_ADDRESS
AUTO — the wallet address, shown in the UI faucet card with a copy button.
open ↗Add these in your Lovable project under Settings → Secrets before pasting the prompt below.
Appendix · Mega-prompt
The build prompt.
budget · 1 message
Paste into a fresh Lovable project. Make sure all five secrets above are set first. read the build strategy →
Build "Script-to-Screen Sync" in ONE Lovable message. Single-page demo on Circle's Arc Testnet.
CONCEPT
Sync script changes and video edits onchain for teams without forcing gas payments.
Discipline: Videography & Film (production alignment).
Onchain primitive: Privy social wallet on Arc. Why this primitive: Privy wallet’s gasless tx make real-time script and edit synchronization user-friendly.
5-CREDIT BUDGET (HARD LIMIT):
- ONE single-page app. No router, no Lovable Cloud, no database, no auth flows beyond Privy drop-in.
- ONE Solidity contract, <=100 lines, deployed to Arc Testnet via Circle SCP, verified on arcscan.
- Privy is always the auth + wallet layer (Google login, embedded wallet, defaultChain = Arc 5042002).
- USDC is the gas token on Arc (no ETH). Support USDC + EURC + cirBTC as payment tokens.
- At most ONE AI call per user action (Lovable AI Gateway with LOVABLE_API_KEY).
- Skip tests, skip CI, skip docs pages. Ship the demo, nothing else.
STACK
- React + Vite single page (the index route).
- Arc chain definition (viem `defineChain`, decimals: 6 — USDC is native gas):
```ts
// src/lib/arc-chain.ts
import { defineChain } from "viem";
// CRITICAL: nativeCurrency.decimals = 6 because USDC is the gas token on Arc.
// Using 18 (the default EVM assumption) will corrupt every balance/fee display by 10^12.
export const arcTestnet = defineChain({
id: 5042002,
name: "Arc Testnet",
network: "arc-testnet",
nativeCurrency: { name: "USD Coin", symbol: "USDC", decimals: 6 },
rpcUrls: { default: { http: [import.meta.env.VITE_ARC_RPC_URL ?? "https://rpc.testnet.arc.network"] } },
blockExplorers: { default: { name: "Arcscan", url: "https://testnet.arcscan.app" } },
});
```
- Privy embedded wallet wraps `<App />` in src/main.tsx:
import { PrivyProvider } from "@privy-io/react-auth";
import { arcTestnet } from "@/lib/arc-chain";
<PrivyProvider appId={import.meta.env.VITE_PRIVY_APP_ID}
config={{ loginMethods:['google'],
embeddedWallets:{createOnLogin:'users-without-wallets'},
defaultChain: arcTestnet,
supportedChains:[arcTestnet] }}>
(Both `defaultChain` AND `supportedChains` are required — omitting `supportedChains` silently drops the custom chain.)
- Multi-stablecoin support is MANDATORY. Ship src/lib/tokens.ts exactly:
```ts
// src/lib/tokens.ts - the three stablecoins every Creative Blockchain app must support
export const ARC_CHAIN_ID = 5042002;
export const ARC_RPC_URL = import.meta.env.VITE_ARC_RPC_URL ?? "https://rpc.testnet.arc.network";
export const ARC_EXPLORER = "https://testnet.arcscan.app";
export const TOKENS = {
USDC: { symbol: "USDC", address: "0x3600000000000000000000000000000000000000", decimals: 6, label: "US Dollar (native gas)" },
EURC: { symbol: "EURC", address: "0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a", decimals: 6, label: "Euro Coin" },
cirBTC: { symbol: "cirBTC", address: import.meta.env.VITE_CIRBTC_ADDRESS ?? "0x0000000000000000000000000000000000000000", decimals: 8, label: "Circle Wrapped BTC" },
} as const;
export type TokenKey = keyof typeof TOKENS;
```
- Token switcher UI: three chips (USDC · EURC · cirBTC). The user picks one before paying.
- Circle App Kit (optional): `@circle-fin/kit` with `import.meta.env.VITE_CIRCLE_KIT_KEY` for Bridge / Swap widgets.
- Frontend reads with viem: `createPublicClient({ chain: arcTestnet, transport: http() })`.
- Expose `PRIVY_APP_ID` as `VITE_PRIVY_APP_ID` and `CIRCLE_KIT_KEY` as `VITE_CIRCLE_KIT_KEY` (Vite only exposes VITE_-prefixed env to the client).
CONTRACT (contracts/ScripttoScreenSync.sol):
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title SocialLogScripttoScreenSync
/// @notice Sync script changes and video edits onchain for teams without forcing gas payments.
/// @notice Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
contract SocialLogScripttoScreenSync {
// token = one of USDC / EURC / cirBTC (ERC-20 addresses configured off-chain)
event Logged(address indexed author, address indexed token, uint256 amount, string cid, uint256 at);
/// @notice Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
function log(address token, uint256 amount, string calldata cid) external {
emit Logged(msg.sender, token, amount, cid, block.timestamp);
}
}
```
BOOTSTRAP (MANDATORY — runs once in the Lovable sandbox before deploy):
- The participant pastes ONLY `CIRCLE_API_KEY` + `PRIVY_APP_ID`. Everything else is generated here.
- Ship `scripts/bootstrap-circle.mjs` exactly as below and run it once (`node scripts/bootstrap-circle.mjs`).
It generates + registers CIRCLE_ENTITY_SECRET, mints the ARC-TESTNET treasury wallet, and prints
the three values (secret, wallet ID, address) for the participant to paste into Project Settings -> Secrets.
- The deploy script MUST refuse to run if `CIRCLE_TREASURY_WALLET_ID` or `CIRCLE_ENTITY_SECRET` is missing,
with the exact message: `Run: node scripts/bootstrap-circle.mjs`.
```js
// scripts/bootstrap-circle.mjs
// MANDATORY first step. Runs once in the Lovable sandbox. Idempotent.
// Uses CIRCLE_API_KEY to (1) generate + register CIRCLE_ENTITY_SECRET,
// (2) create the ARC-TESTNET developer-controlled treasury wallet,
// (3) print all three values for the participant to paste into Project Settings -> Secrets.
import crypto from "node:crypto"; import fs from "node:fs";
const API = "https://api.circle.com/v1/w3s";
const KEY = process.env.CIRCLE_API_KEY;
if (!KEY) { console.error("Paste CIRCLE_API_KEY first (Project Settings -> Secrets)."); process.exit(1); }
const H = { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" };
// 1. Reuse an existing recovery file if present (re-runs after a remix).
const RECOVERY = "circle-entity-recovery.json";
let entitySecret = fs.existsSync(RECOVERY) ? JSON.parse(fs.readFileSync(RECOVERY,"utf8")).entitySecret
: crypto.randomBytes(32).toString("hex");
// 2. RSA-OAEP-SHA256 encrypt with Circle's public key and register (409 = already registered, OK).
const { data: { publicKey } } = await fetch(`${API}/config/entity/publicKey`, { headers: H }).then(r => r.json());
const ciphertext = crypto.publicEncrypt(
{ key: crypto.createPublicKey(publicKey), oaepHash: "sha256", padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
Buffer.from(entitySecret, "hex")
).toString("base64");
const reg = await fetch(`${API}/config/entity/entitySecret`, {
method: "PUT", headers: H, body: JSON.stringify({ entitySecretCiphertext: ciphertext })
});
if (reg.status !== 200 && reg.status !== 409) { console.error("register failed", reg.status, await reg.text()); process.exit(1); }
fs.writeFileSync(RECOVERY, JSON.stringify({ entitySecret, warning: "Keep safe. Circle never reveals this again." }, null, 2));
// 3. Create treasury wallet on ARC-TESTNET (or reuse via wallet-set list on re-run).
const idempotencyKey = crypto.randomUUID();
const freshCipher = crypto.publicEncrypt(
{ key: crypto.createPublicKey(publicKey), oaepHash: "sha256", padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
Buffer.from(entitySecret, "hex")
).toString("base64");
const setRes = await fetch(`${API}/developer/walletSets`, {
method: "POST", headers: H,
body: JSON.stringify({ idempotencyKey, entitySecretCiphertext: freshCipher, name: "arc-treasury" })
}).then(r => r.json());
const walletSetId = setRes.data?.walletSet?.id;
const walletCipher = crypto.publicEncrypt(
{ key: crypto.createPublicKey(publicKey), oaepHash: "sha256", padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
Buffer.from(entitySecret, "hex")
).toString("base64");
const walletRes = await fetch(`${API}/developer/wallets`, {
method: "POST", headers: H,
body: JSON.stringify({ idempotencyKey: crypto.randomUUID(), entitySecretCiphertext: walletCipher,
walletSetId, blockchains: ["ARC-TESTNET"], count: 1, accountType: "EOA" })
}).then(r => r.json());
const wallet = walletRes.data?.wallets?.[0];
if (!wallet) { console.error("wallet create failed", JSON.stringify(walletRes)); process.exit(1); }
console.log("\n=== PASTE THESE INTO Project Settings -> Secrets ===\n");
console.log("CIRCLE_ENTITY_SECRET =", entitySecret);
console.log("CIRCLE_TREASURY_WALLET_ID =", wallet.id);
console.log("VITE_CIRCLE_TREASURY_ADDRESS =", wallet.address);
console.log(`\nThen fund the address at https://faucet.circle.com/ (USDC gas + EURC + cirBTC on Arc Testnet).`);
console.log(`Address: ${wallet.address}`);
```
DEPLOY (Circle Smart Contract Platform — USDC gas paid from the treasury wallet, no MetaMask, no Hardhat):
- Install: `bun add solc`
- Prereq: bootstrap step above completed AND treasury address funded via https://faucet.circle.com/.
- Deploy script:
```js
// scripts/deploy-arc.mjs — compile with solc, deploy via Circle SCP (USDC gas).
// Run once: `node scripts/register-entity-secret.mjs` (below) to mint CIRCLE_ENTITY_SECRET.
// Then: `node scripts/deploy-arc.mjs <ContractName> <ConstructorArg1> ...`
import fs from "node:fs"; import crypto from "node:crypto"; import solc from "solc";
const API = "https://api.circle.com/v1/w3s";
const KEY = process.env.CIRCLE_API_KEY;
const WID = process.env.CIRCLE_TREASURY_WALLET_ID;
const ES = process.env.CIRCLE_ENTITY_SECRET; // 32-byte hex
const H = { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" };
async function encryptEntitySecret() {
const { data } = await fetch(`${API}/config/entity/publicKey`, { headers: H }).then(r => r.json());
const pub = crypto.createPublicKey(data.publicKey);
return crypto.publicEncrypt(
{ key: pub, oaepHash: "sha256", padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
Buffer.from(ES, "hex")
).toString("base64"); // single-use ciphertext, regen every call
}
function compile(name) {
const source = fs.readFileSync(`contracts/${name}.sol`, "utf8");
const input = { language: "Solidity", sources: { [`${name}.sol`]: { content: source } },
settings: { optimizer: { enabled: true, runs: 200 },
outputSelection: { "*": { "*": ["abi", "evm.bytecode.object"] } } } };
const out = JSON.parse(solc.compile(JSON.stringify(input)));
const c = out.contracts[`${name}.sol`][name];
return { abi: c.abi, bytecode: "0x" + c.evm.bytecode.object };
}
const [, , name, ...ctorArgs] = process.argv;
const { abi, bytecode } = compile(name);
const ctor = abi.find(x => x.type === "constructor");
const sig = ctor ? `constructor(${ctor.inputs.map(i => i.type).join(",")})` : "constructor()";
const body = { walletId: WID, blockchain: "ARC-TESTNET", name, bytecode,
abiFunctionSignature: sig, abiParameters: ctorArgs,
entitySecretCiphertext: await encryptEntitySecret(),
fee: { type: "level", config: { feeLevel: "MEDIUM" } } };
const { data: { contractId } } = await fetch(`${API}/contracts/deploy`,
{ method: "POST", headers: H, body: JSON.stringify(body) }).then(r => r.json());
let addr;
for (let i = 0; i < 60 && !addr; i++) {
await new Promise(r => setTimeout(r, 2000));
const { data: { contract } } = await fetch(`${API}/contracts/${contractId}`, { headers: H }).then(r => r.json());
if (contract.state === "COMPLETE") addr = contract.contractAddress;
}
fs.writeFileSync("src/data/contract.json", JSON.stringify({ address: addr, abi, chainId: 5042002, explorer: "https://testnet.arcscan.app" }, null, 2));
console.log(`Deployed ${name} -> ${addr} (${'https://testnet.arcscan.app'}/address/${addr})`);
```
- Run: `node scripts/deploy-arc.mjs ScripttoScreenSync`
- The script writes the address + ABI to `src/data/contract.json`; the UI links to `https://testnet.arcscan.app/address/<address>`.
VERIFY (arcscan / Blockscout REST — no API key, no Etherscan, no Hardhat plugin):
```js
// scripts/verify-arc.mjs — Blockscout REST verify. NO API key. NO plugin.
import fs from "node:fs";
const addr = JSON.parse(fs.readFileSync("src/data/contract.json", "utf8")).address;
const source = fs.readFileSync(process.argv[2], "utf8"); // path to flattened .sol
const form = new FormData();
form.append("compiler_version", "v0.8.24+commit.e11b9ed9"); // match the solc you compiled with
form.append("license_type", "mit");
form.append("optimization", "true");
form.append("optimization_runs", "200");
form.append("source_code", source);
const res = await fetch(`https://testnet.arcscan.app/api/v2/smart-contracts/${addr}/verification/via/flattened-code`,
{ method: "POST", body: form });
console.log(res.status, await res.text()); // poll GET /api/v2/smart-contracts/{addr} for is_verified
```
- Flatten with `bunx sol-merger contracts/ScripttoScreenSync.sol build/flat.sol` before running.
- On success, source + ABI are readable at `https://testnet.arcscan.app/address/<address>?tab=contract`.
USER FLOW
1. Land on page -> 'Sign in with Google' (Privy) -> embedded Arc wallet auto-provisioned.
2. TREASURY CARD (mandatory UI): render a callout that reads
`import.meta.env.VITE_CIRCLE_TREASURY_ADDRESS`, shows the address with a copy button,
and links to https://faucet.circle.com/ with the text
"Fund the treasury with USDC (gas) + EURC + cirBTC on Arc Testnet, then reload."
If the env var is empty, render: "Run `node scripts/bootstrap-circle.mjs` first."
3. Every production alignment action the user performs is sent as an Arc tx (`log(USDC, 0, payload)`) and displayed with an arcscan link. USDC pays the gas transparently.
4. Footer renders: "Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14"
REQUIRED SECRETS
--- YOU PASTE (2 keys, that's it) ---
- CIRCLE_API_KEY Circle Console API key (backend). https://console.circle.com/
- PRIVY_APP_ID Google sign-in + embedded Arc wallet. https://dashboard.privy.io
--- AUTO-GENERATED by `node scripts/bootstrap-circle.mjs` (DO NOT paste manually) ---
- CIRCLE_ENTITY_SECRET 32-byte hex, registered with Circle via RSA-OAEP.
- CIRCLE_TREASURY_WALLET_ID Developer-controlled wallet on ARC-TESTNET.
- VITE_CIRCLE_TREASURY_ADDRESS The wallet address, exposed to the UI faucet card.
--- OPTIONAL ---
- CIRCLE_KIT_KEY Circle App Kit client key for Bridge / Swap widgets.
- LOVABLE_API_KEY Only if the idea calls an LLM. Lovable AI Gateway.
NO MetaMask key. NO Etherscan key. NO Hardhat. NO Alchemy. Circle pays gas in USDC from the treasury wallet.
CREDIT (must appear in UI footer AND as NatSpec on every deployed contract):
Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
Market sizing.
TAM
$1.1B
film and video editing workflow tools
SAM
$70M
script and production coordination software
SOM
$4M
studios using onchain syncing
Indicative figures for hackathon pitches — refine with your own research before raising.
Adjacent entries.
collaborative editing
Seamless Clip Shares
Share video edits instantly without gas fees, enabling smooth collaboration among creators.
edit reviewTokenized Cut Approval
Enable editors and clients to approve cuts securely via onchain, without gas barriers.
authenticity trackingClip Provenance Ledger
Verify original footage sources with onchain provenance that users access gaslessly.
visual effects tradingSponsored Effects Marketplace
Buy and sell video effects licenses seamlessly with gasless wallet transactions.