RigPin Sync
Share and pin character rig files and metadata on IPFS for verified reuse across teams.
IPFS via Pinata· decentralized storage
Section · Onchain
full primer →The primitive.
Every character rig sharing artefact is pinned to IPFS through Pinata; filmmakers get a permanent CID and a public gateway preview instead of a fragile cloud URL.
Why this primitivePinata pins the artifact to IPFS and returns a permanent CID, so the source-of-truth media never lives on a private server that can vanish.
Kernel
a Pinata JWT upload that pins images / JSON / manifests to IPFS and returns a permanent CID
Drives the UI as
a 'pinned to IPFS' chip with the CID and an ipfs.io gateway preview
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 ↗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 four secrets above are set first. read the build strategy →
Build "RigPin Sync" in ONE Lovable message. Single-page demo.
CONCEPT
Share and pin character rig files and metadata on IPFS for verified reuse across teams.
Discipline: film-animation (character rig sharing).
Onchain primitive: IPFS via Pinata. Why this primitive: Pinata pins the artifact to IPFS and returns a permanent CID, so the source-of-truth media never lives on a private server that can vanish.
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
PINATA (server function to pin media):
```ts
// src/lib/pin.functions.ts
import { createServerFn } from "@tanstack/react-start";
export const pin = createServerFn({ method: "POST" })
.inputValidator((d: FormData) => d)
.handler(async ({ data }) => {
const r = await fetch("https://api.pinata.cloud/pinning/pinFileToIPFS", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PINATA_JWT}` },
body: data,
});
const { IpfsHash } = await r.json();
return { cid: IpfsHash as string };
});
```
Preview via `https://ipfs.io/ipfs/<CID>`.
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 character rig sharing 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
Market sizing.
TAM
$350M
rigging software market
SAM
$90M
rig sharing platforms
SOM
$10M
mid-sized animation studios
Indicative figures for hackathon pitches — refine with your own research before raising.
Adjacent entries.
storyboard management
FrameForge Archive
Securely store and share storyboards as immutable IPFS manifests to simplify team collaboration.
material asset libraryTextureVault
Pin and catalog textures on IPFS for reuse and verified provenance in animation projects.
scene version controlAnimScene Sync
Automatically pin scene JSON manifests to IPFS to track animation iterations and changes.
color study curationMoodboardChain
Create decentralized moodboards pinned to IPFS for collaborative color grading projects.