Lighting Pass Archive
Lighting artists mint each lighting pass as a Sui object to prove their contribution to the final image.
Sui object NFT· onchain authorship
Section · Onchain
full primer →The primitive.
The onchain primitive runs at the right moment in the flow and surfaces a clear, verifiable result that filmmakers can act on without web3 jargon.
Why this primitiveEach Sui object NFT carries a Walrus blobId of the EXR or render pass with a timestamp, documenting the lighting process.
Kernel
a Move `key + store` object minted to the creator's address, holding a Walrus blobId and a timestamp, viewable on Suiscan as a native Sui object
Drives the UI as
a 'mint to claim authorship' button that returns the objectId, the creator address, and a Suiscan link
Required keys.
SUI_DEPLOYER_KEY
Sui keypair (base64) used once by `sui client publish`. Fund the address at the Sui Testnet faucet.
open ↗SUI_WALLET_SALT
64-char random string (generate via Lovable Cloud secrets). HKDF-derives a per-user Ed25519 Sui keypair server-side.
open ↗WALRUS_PUBLISHER_URL
https://publisher.walrus-testnet.walrus.space — used only when the idea pins media to Walrus.
open ↗WALRUS_AGGREGATOR_URL
https://aggregator.walrus-testnet.walrus.space — reads blobs pinned above. Optional.
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 "Lighting Pass Archive" in ONE Lovable message. Single-page demo. Sui Testnet.
CONCEPT
Lighting artists mint each lighting pass as a Sui object to prove their contribution to the final image.
Discipline: Filmmaking & Animation (3D lighting design).
Onchain primitive: Sui object NFT. Why: Each Sui object NFT carries a Walrus blobId of the EXR or render pass with a timestamp, documenting the lighting process.
BUDGET (5 credits, hard limit)
- One page. No extra routes, no dashboards, no docs.
- Enable Lovable Cloud + Google sign-in only. No email/password, no other providers.
- One Move module (<=80 lines), published ONCE to Sui Testnet.
- All signing runs server-side in ONE createServerFn.
- Walrus only if the idea needs blob storage.
- Skip tests, skip CI, skip landing marketing.
STACK
- Whatever the Lovable session ships with (TanStack Start today; classic Vite+React works too).
- `bun add @mysten/sui zod`. No @mysten/dapp-kit, no @mysten/enoki.
- All Sui signing is server-side inside createServerFn (or the equivalent server route on the classic template).
- src/lib/walrus.ts: `PUT ${process.env.WALRUS_PUBLISHER_URL}/v1/store?epochs=5` with the file body; read via `${process.env.WALRUS_AGGREGATOR_URL}/v1/<blobId>`. Do the upload inside the same server fn (or a sibling fn) so the browser never sees keys.
AUTH + WALLET (custodial demo — be honest in the UI)
- Enable Lovable Cloud. Add Google sign-in via `supabase--configure_social_auth`
with providers=["google"], disable_providers=["email"]. No Google Cloud
console, no OAuth client — Lovable brokers it.
- Sign-in button:
import { lovable } from "@/integrations/lovable";
await lovable.auth.signInWithOAuth("google", { redirect_uri: window.location.origin });
- src/lib/sui-wallet.server.ts derives a per-user Ed25519 keypair:
import { hkdfSync } from "node:crypto";
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
export function deriveKeypair(userId: string) {
const salt = process.env.SUI_WALLET_SALT!;
const seed = hkdfSync("sha256", Buffer.from(salt), Buffer.from(userId),
Buffer.from("sui-wallet:ed25519:v1"), 32);
return Ed25519Keypair.fromSecretKey(new Uint8Array(seed));
}
- UI shows the derived Sui address + Suiscan link
(https://suiscan.xyz/testnet/account/<addr>) + "Fund via Testnet faucet"
button linking to https://faucet.sui.io/?address=<addr>.
- Disclosure copy under the address card:
"Demo wallet held by the server — safe for Sui Testnet, do not use for real value."
MOVE MODULE (move/sources/lighting_pass_archive.move)
```move
/// Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
module creative::lighting_pass_archive {
use sui::object::{Self, UID};
use sui::transfer;
use sui::tx_context::{Self, TxContext};
public struct Artefact has key, store {
id: UID,
blob_id: vector<u8>,
creator: address,
minted_at: u64,
}
/// Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
public entry fun mint(blob_id: vector<u8>, ctx: &mut TxContext) {
let artefact = Artefact {
id: object::new(ctx),
blob_id,
creator: tx_context::sender(ctx),
minted_at: tx_context::epoch_timestamp_ms(ctx),
};
transfer::public_transfer(artefact, tx_context::sender(ctx));
}
}
```
PUBLISH (one-time, from the sandbox)
- Write move/Move.toml:
[package]
name = "creative"
[addresses]
creative = "0x0"
[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }
- sui client switch --env testnet
- sui client publish --gas-budget 100000000 --json move > publish.json
- Extract packageId:
jq '.objectChanges[] | select(.type=="published") | .packageId' publish.json
- Write src/data/sui-package.json:
{ "packageId": "0x...", "module": "<mod>", "network": "testnet", "publishedAt": "<iso>" }
Import it in the server fn. Never hardcode the package id in a component.
SERVER FN (src/lib/lighting_pass_archive.functions.ts)
import { createServerFn } from "@tanstack/react-start";
import { requireSupabaseAuth } from "@/integrations/supabase/auth-middleware";
import { SuiClient, getFullnodeUrl } from "@mysten/sui/client";
import { Transaction } from "@mysten/sui/transactions";
import { z } from "zod";
import pkg from "@/data/sui-package.json";
import { deriveKeypair } from "./sui-wallet.server";
const MIN_GAS_MIST = 5_000_000n;
export const submit = createServerFn({ method: "POST" })
.middleware([requireSupabaseAuth])
.inputValidator((d) => z.object({ blobId: z.string().min(1).max(512) }).parse(d))
.handler(async ({ data, context }) => {
const kp = deriveKeypair(context.userId);
const addr = kp.toSuiAddress();
const client = new SuiClient({ url: getFullnodeUrl("testnet") });
const bal = await client.getBalance({ owner: addr });
if (BigInt(bal.totalBalance) < MIN_GAS_MIST) {
return { ok: false, address: addr,
error: "Fund the wallet at https://faucet.sui.io/?address=" + addr };
}
const tx = new Transaction();
tx.moveCall({
target: `${pkg.packageId}::${pkg.module}::mint`,
arguments: [tx.pure.vector("u8", Array.from(new TextEncoder().encode(data.blobId)))],
});
const res = await client.signAndExecuteTransaction({ signer: kp, transaction: tx });
return { ok: true, address: addr, digest: res.digest,
explorer: "https://suiscan.xyz/testnet/tx/" + res.digest };
});
UI (single page)
- Signed out: hero + "Sign in with Google" button (the lovable.auth call above).
- Signed in: address card (copy button, faucet link, Suiscan account link) +
disclosure copy + <action form for 3D lighting design> that calls the server fn via useServerFn+useMutation.
- After success: render the returned digest as a Suiscan tx link. Keep the last 5 in local state.
- Footer: "Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14"
SECRETS (Lovable Cloud -> Secrets)
- SUI_WALLET_SALT generate 64-char random via `generate_secret`.
- SUI_DEPLOYER_KEY Sui keypair (base64) used ONCE to publish the Move package.
Fund at https://faucet.sui.io/?address=<deployer-address>.
- WALRUS_PUBLISHER_URL https://publisher.walrus-testnet.walrus.space
- WALRUS_AGGREGATOR_URL https://aggregator.walrus-testnet.walrus.space
Suiscan note: a freshly derived address shows "no results" until its first tx confirms. That is expected, not a bug.
On submit, upload the 3D lighting design artefact to Walrus, then call `lighting_pass_archive::mint(blob_id)` server-side. Render the new objectId, Walrus aggregator preview, and Suiscan link.
Market sizing.
TAM
$15B
3D production and rendering services
SAM
$2B
lighting departments in animation and VFX
SOM
$70M
freelance lighters and boutique CG houses
Indicative figures for hackathon pitches — refine with your own research before raising.
Adjacent entries.
3D lighting design
Lighting Rig Chain
Lighting artists publish custom lighting rigs on-chain, enabling verified licensing of their scene illumination setups.
traditional cel animationCel Mint Archive
Animators mint each hand-drawn cel as a Sui object NFT to prove creation order and originality.
storyboard revision trackingStoryboard Chain
Storyboard artists mint each panel revision on-chain so directors can trace every creative decision.
3D character riggingRig Genesis Vault
Riggers mint their character rigs as Sui objects to license them while proving authorship.