PatternProof Chain
Verify pattern originality and ownership in decentralized on-chain records.
Arc smart contract· onchain logic
Section · Onchain
full primer →The primitive.
Pattern authenticity gets a tiny Solidity contract deployed to Circle's Arc Testnet (USDC is the gas token); fashion designers see a 'verified on Arc' badge with the live address and a one-tap arcscan / Blockscout link.
Why this primitiveImmutability secures pattern provenance against unauthorized reuse.
Kernel
a Solidity contract deployed to Arc Testnet (Circle L1, chain 5042002) using USDC as the gas token, then verified on the Arc block explorer (arcscan / Blockscout)
Drives the UI as
a 'verified on Arc' badge with the live contract address and a one-tap arcscan link
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 "PatternProof Chain" in ONE Lovable message. Single-page demo on Circle's Arc Testnet.
CONCEPT
Verify pattern originality and ownership in decentralized on-chain records.
Discipline: Fashion & Textile Design (pattern authenticity).
Onchain primitive: Arc smart contract. Why this primitive: Immutability secures pattern provenance against unauthorized reuse.
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/PatternProofChain.sol):
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title PatternProofChain
/// @notice Verify pattern originality and ownership in decentralized on-chain records.
/// @notice Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
contract PatternProofChain {
// 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 PatternProofChain`
- 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/PatternProofChain.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. User performs a pattern authenticity action; the app calls `log(token, amount, payload)` (token = USDC/EURC/cirBTC) via Privy and shows the arcscan link as proof.
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.2B
fashion design software market
SAM
$120M
digital pattern authentication
SOM
$9M
designers protecting pattern creations
Indicative figures for hackathon pitches — refine with your own research before raising.
Adjacent entries.
material provenance
FabricTrace Ledger
Track and verify organic fabric sources to assure sustainable fashion claims.
design copyrightDesignAuth Mint
Securely register and timestamp original fashion designs to prevent copying.
color curationColorPalette DAO
Collaboratively create and share color palettes with verified ownership and royalty rules.
virtual try-onVirtualFitting NFT
Create unique virtual fitting sessions minted as NFTs for secure client access.