🎥 Videography & Film · visual concepting

Moodboard Mint

Create NFT-backed moodboards to validate and share artistic vision securely with collaborators.

Algorand smart contract· onchain logic
Section · Onchain

The primitive.

full primer →

Visual concepting runs through a PuyaPy smart contract on Algorand TestNet; videographers see a 'live on Algorand' badge with the App ID and a one-tap AlgoExplorer link, settled in ~3 seconds.

Why this primitiveAlgorand's AVM settles a contract call in ~3 seconds with a fixed 0.001 ALGO fee, so authorship or state proofs land instantly and can be re-verified from any AlgoExplorer link.

Kernel
a PuyaPy smart contract compiled and deployed to Algorand TestNet with AlgoKit — the AVM instruction set is Python-friendly, deploys settle in ~3 s, and every call is inspectable on AlgoExplorer
Drives the UI as
a 'live on Algorand TestNet' badge with the App ID and an AlgoExplorer link
Appendix · Secrets

Required keys.

ALGORAND_DEPLOYER_MNEMONIC
24-word mnemonic of the account that deploys apps and mints ASAs. Fund on TestNet via the Algorand dispenser.
open ↗
SPONSOR_MNEMONIC
24-word mnemonic of the sponsor account that fee-pools user transactions (0.001 ALGO each).
open ↗
ALGOD_SERVER
Public Algorand TestNet algod endpoint. Nodely's free tier requires no token.
open ↗
PINATA_JWT
Pins images / JSON / manifests to IPFS.
open ↗

Add these in your Lovable project under Settings → Secrets before pasting the prompt below.

Appendix · Mega-prompt

The build prompt.

Paste into a fresh Lovable project. Make sure all four secrets above are set first. read the build strategy →

Build "Moodboard Mint" in ONE Lovable message. Single-page demo.

CONCEPT
Create NFT-backed moodboards to validate and share artistic vision securely with collaborators.
Discipline: video (visual concepting).
Onchain primitive: Algorand smart contract. Why this primitive: Algorand's AVM settles a contract call in ~3 seconds with a fixed 0.001 ALGO fee, so authorship or state proofs land instantly and can be re-verified from any AlgoExplorer link.
5-CREDIT BUDGET (HARD LIMIT):
- ONE single-page app. No router, no Lovable Cloud, no database, no auth flows beyond the wallet connect.
- ONE PuyaPy contract (<=80 lines) OR one ASA acfg — deployed to Algorand TestNet, inspectable on AlgoExplorer.
- Users sign via TxnLab use-wallet (Pera / Defly / Exodus / Lute / WalletConnect); a sponsor account fee-pools the atomic group so users never fund an account.
- Pinata/IPFS only if the idea genuinely needs to store a file or metadata.
- 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). Algorand JS SDK: `algosdk` (npm).
- Wallet layer (recommended): TxnLab **use-wallet** — one hook, every Algorand wallet.
    npm: `@txnlab/use-wallet-react @txnlab/use-wallet-ui @perawallet/connect @blockshake/defly-connect @agoralabs-sh/avm-web-provider lute-connect @walletconnect/sign-client`
    docs: https://txnlab.gitbook.io/use-wallet  (llms.txt: https://txnlab.gitbook.io/use-wallet/llms.txt)
    Setup (client-only — never at SSR module scope of a route file):
      // src/wallet.tsx
      import { WalletProvider, WalletManager, WalletId, NetworkId } from "@txnlab/use-wallet-react";
      import "@txnlab/use-wallet-ui/dist/style.css";
      const manager = new WalletManager({
        wallets: [WalletId.PERA, WalletId.DEFLY, WalletId.EXODUS, WalletId.LUTE],
        defaultNetwork: NetworkId.TESTNET,
      });
      export const Wallets = ({ children }) => <WalletProvider manager={manager}>{children}</WalletProvider>;
    Use in a component:
      import { useWallet } from "@txnlab/use-wallet-react";
      import { WalletButton } from "@txnlab/use-wallet-ui";
      const { activeAddress, transactionSigner, algodClient } = useWallet();
      // <WalletButton /> renders connect/disconnect/account UI for you.
    Fallback (minimal builds only): raw `@perawallet/connect`
      (https://github.com/perawallet/connect). Same SSR rule — client-only.
- Contracts: prefer PuyaPy (`pipx install puyapy`, compile with `puyapy contract.py`).
  If you cannot install the Python toolchain in this sandbox, hand-write TEAL v10
  directly — it's the compiled output anyway — and compile it at deploy time via
  `algod.compile(teal).do()`. A minimal approval program that just approves every call:
    #pragma version 10
    int 1
    return
  Clear program is identical. That's enough for a demo app that logs via `note`.
- Fee pooling / sponsored tx (users pay 0 ALGO — Algorand has NO paymaster/bundler,
  fees pool natively across an atomic group):
    const sp = await algod.getTransactionParams().do();
    const userTx = algosdk.makeApplicationNoOpTxnFromObject({
      from: activeAddress, appIndex: APP_ID, appArgs: [...],
      suggestedParams: { ...sp, fee: 0, flatFee: true },
    });
    const sponsorTx = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
      from: sponsorAddr, to: sponsorAddr, amount: 0,
      suggestedParams: { ...sp, fee: 2000, flatFee: true }, // covers both txs
    });
    algosdk.assignGroupID([userTx, sponsorTx]);
    // user signs userTx via use-wallet's transactionSigner (works for every wallet);
    // sponsor signs sponsorTx server-side; submit the group with algod.sendRawTransaction.
- Deploy address / App ID / Asset ID gets written to `src/data/contract.json`
  so the UI links to `https://testnet.explorer.perawallet.app/application/<id>`
  or `.../asset/<id>`.

NON-OBVIOUS RULES (each of these silently breaks the demo — read all 8):
1. Pera "Universal Wallet" mnemonics are 24 words (BIP-39). `algosdk.mnemonicToSecretKey`
   ONLY accepts 25-word Algo25. For 24 words, derive via SLIP-0010 path
   m/44'/283'/0'/0'/0' using `@scure/bip39` + `micro-ed25519-hdkey`:
     import { mnemonicToSeedSync, validateMnemonic } from "@scure/bip39";
     import { wordlist } from "@scure/bip39/wordlists/english";
     import { HDKey } from "micro-ed25519-hdkey";
     import algosdk from "algosdk";
     export function resolveAccount(phrase) {
       const words = phrase.trim().split(/\s+/);
       if (words.length === 25) return algosdk.mnemonicToSecretKey(phrase);
       if (words.length === 24 && validateMnemonic(phrase, wordlist)) {
         const seed = mnemonicToSeedSync(phrase);
         const { privateKey } = HDKey.fromMasterSeed(seed).derive("m/44'/283'/0'/0'/0'");
         const sk = new Uint8Array(64); sk.set(privateKey); sk.set(algosdk.encodeAddress(
           algosdk.decodeAddress(algosdk.encodeAddress(
             (require('tweetnacl').sign.keyPair.fromSeed(privateKey)).publicKey)).publicKey), 32);
         const kp = require('tweetnacl').sign.keyPair.fromSeed(privateKey);
         return { addr: algosdk.encodeAddress(kp.publicKey), sk: new Uint8Array([...privateKey, ...kp.publicKey]) };
       }
       throw new Error("mnemonic must be 24 or 25 words");
     }
2. Pera's Universal Wallet UI address may NOT match the SLIP-0010 derivation
   (Pera uses proprietary BIP32-Ed25519 / ARC-52 that standard libs can't
   reproduce). Fix: print the derived address on first run, then send TestNet
   ALGO from the Pera UI to that derived address. Do NOT try to reproduce Pera's derivation.
3. Every app-create / ASA-mint MUST set `note: new TextEncoder().encode(CREDIT)`
   with the hackathon credit string. Notes are on-chain forever and free.
4. Min-balance goes UP with every asset opt-in and every app opt-in:
   base account = 0.1 ALGO, +0.1 per asset, +0.1 per app + schema costs.
   "balance below min" errors are almost always this.
5. `waitForConfirmation` returns BEFORE the indexer has the tx — AlgoExplorer
   / Pera Explorer links may 404 for 5-15s after deploy. Do NOT retry in the
   deploy script; just tell users in the UI "may take ~15 s to appear".
6. Do NOT import `algosdk`, `@txnlab/use-wallet-react`, `@txnlab/use-wallet-ui`,
   or `@perawallet/connect` at module scope of a route file with SSR — they break
   the SSR bundle (Buffer, window, IndexedDB). Import inside the handler, behind
   a `ClientOnly` boundary, or in a `.client.tsx` file, and wrap the app in
   `<WalletProvider>` on the client only.
7. `ALGOD_TOKEN` for Nodely public endpoints is an EMPTY STRING, not "-".
   Only set a real token for a self-hosted algod or a paid provider.
8. TestNet faucet is Google Cloud's Web3 faucet, not the deprecated
   bank.testnet.algorand.network. URL:
   https://cloud.google.com/application/web3/faucet/algorand/testnet
CONTRACT (contracts/app.py — PuyaPy):
```python
# Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
from algopy import ARC4Contract, String, arc4

class MoodboardMint(ARC4Contract):
    @arc4.abimethod
    def log(self, cid: String) -> String:
        # Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
        return cid
```
Compile: `algokit compile py contracts/app.py`
Deploy (scripts/deploy.ts, run locally):
```ts
import algosdk from "algosdk";
const algod = new algosdk.Algodv2(process.env.ALGOD_TOKEN ?? "", process.env.ALGOD_SERVER!, "");
const deployer = algosdk.mnemonicToSecretKey(process.env.ALGORAND_DEPLOYER_MNEMONIC!);
// ...build appl-create tx from compiled TEAL, sign with deployer, submit, log APP_ID
```
Write the resulting App ID to `src/data/contract.json` and link to
`https://testnet.explorer.perawallet.app/application/<APP_ID>`.
USER FLOW
1. Land on page -> tap `<WalletButton />` -> pick Pera / Defly / Exodus / Lute -> address in ~1 tap, sponsor fees the group so no funding needed.
2. User performs a visual concepting action; the app builds an atomic group (user call + sponsor pay), use-wallet's `transactionSigner` signs the user tx via whichever wallet is connected, the sponsor signs server-side, the group submits; UI shows the AlgoExplorer link.
3. Footer renders: "Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14"

REQUIRED SECRETS (Lovable -> Project Settings -> Secrets):
- ALGORAND_DEPLOYER_MNEMONIC   24-word (Pera Universal / BIP-39) OR 25-word (Algo25) mnemonic for the deploy account. Fund on TestNet via https://cloud.google.com/application/web3/faucet/algorand/testnet (the classic bank.testnet faucet is deprecated).
- SPONSOR_MNEMONIC             Mnemonic for the account that pays fees for the user's atomic group (can equal the deployer). Fund the same way.
- ALGOD_SERVER                 Algorand node HTTPS endpoint. Nodely free tier: https://testnet-api.4160.nodely.dev
- ALGOD_TOKEN                  Leave as an EMPTY STRING for Nodely free tier — do NOT put "-" or a placeholder.
- PINATA_JWT                   IPFS uploads (only if app pins media). Docs: https://docs.pinata.cloud/llms-full.txt
- LOVABLE_API_KEY              Only if the idea calls Lovable AI Gateway.

CLIENT-SIDE ENV (Vite exposes these to the browser):
- VITE_ALGOD_SERVER            same value as ALGOD_SERVER

CREDIT (must appear in UI footer AND in every app-create / ASA-mint `note` bytefield):
Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
Appendix · Market

Market sizing.

TAM
$1.1B
creative asset management sector
SAM
$120M
video directors and concept artists
SOM
$7M
small teams using NFT for creative validation

Indicative figures for hackathon pitches — refine with your own research before raising.

See also

Adjacent entries.