💃 Dance & Choreography · group ownership

NFT Dance Collectives

Form dance groups managing collective NFTs and revenue with gasless transactions.

TON Connect wallet· wallet UX
Section · Onchain

The primitive.

full primer →

Choreographers connect Tonkeeper through TON Connect — one QR or deep link, no extension — and every group ownership action becomes a signed testnet transaction they can read back on tonscan.

Why this primitiveTON Connect social and sponsored tx enable easy group wallet management.

Kernel
TON Connect wired to Tonkeeper, MyTonWallet and Tonhub through a public tonconnect-manifest.json — one QR or deep link and the user is on-chain, with every transaction pinned to CHAIN.TESTNET
Drives the UI as
a single 'Connect Tonkeeper' button that swaps into the friendly address and a testnet badge
Appendix · Secrets

Required keys.

TON_DEPLOYER_MNEMONIC
24-word testnet seed for the deploy script. Fund it free via the Telegram test giver.
open ↗
TONCENTER_API_KEY
Optional. Higher rate limits on the testnet indexer.
open ↗
TON_RECEIVER_ADDRESS
Collects Jetton paywall payments. Can equal the deployer wallet.
open ↗
TON_JETTON_MASTER
Jetton metered by the paywall. Leave empty to meter in Toncoin.
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.

Target

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

Build "NFT Dance Collectives" in ONE Lovable message. Single-page demo on the TON testnet.

CONCEPT
Form dance groups managing collective NFTs and revenue with gasless transactions.
Discipline: Dance & Choreography (group ownership).
Onchain primitive: TON Connect wallet. Why this primitive: TON Connect social and sponsored tx enable easy group wallet management.

5-CREDIT BUDGET (HARD LIMIT)
- ONE single-page app. No router, no Lovable Cloud, no database, no auth beyond TON Connect.
- ONE FunC contract, <=60 lines, deployed to TON testnet and readable on tonscan.
- TON Connect is always the wallet layer (Tonkeeper, MyTonWallet, Tonhub).
- Pinata/IPFS only if the idea genuinely needs to store a file or metadata.
- At most ONE AI call per user action (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 + TanStack Start, single page (the index route).
- SSR-safe wallet mount is MANDATORY. Never import @tonconnect/ui-react at module
  scope of a route file — it touches `window` and crashes SSR. Use
  lazy(() => import('./ton-client-entry')) inside <ClientOnly> + <Suspense>, and
  keep <TonConnectUIProvider> inside ton-client-entry.tsx only.
- Publish public/tonconnect-manifest.json ({ url, name, iconUrl }) and pass
  manifestUrl={`${window.location.origin}/tonconnect-manifest.json`}.
- TESTNET IS FORCED: send with `network: CHAIN.TESTNET` and refuse to submit when
  `wallet.account.chain !== CHAIN.TESTNET`, with a 'switch your wallet to testnet' hint.
- Amounts are nanotons as strings ("50000000" = 0.05 TON). A text-comment body is
  beginCell().storeUint(0, 32).storeStringTail(text).endCell().toBoc().toString('base64').
- Read chain state from the public indexer https://testnet.toncenter.com/api/v3
  (/transactions?account=…, /jetton/transfers?address=…). Never poll a wallet for history.
- Contracts live in /contracts, compiled with @ton-community/func-js and deployed with
  @ton/ton + @ton/crypto from a node script — keep them out of the Vite bundle.
- Persistent amber "TON testnet only · no real value moves here" banner at the top of the shell.

WALLET LAYER (the whole point of this build)
- TON Connect handles identity: no seed phrase typing, no browser extension required —
  desktop shows a QR, mobile deep-links straight into Tonkeeper.
- Surface four explicit states: not installed, connected, wrong network (mainnet), and
  transaction pending. Never silently retry a rejected transaction.
- Every group ownership action is one `sendTransaction` with a text-comment payload, pinned to CHAIN.TESTNET.

CONTRACT (contracts/NFTDanceCollectives.fc — FunC, compiled with @ton-community/func-js):
```func
;; NFTDanceCollectives — Form dance groups managing collective NFTs and revenue with gasless transactions.
;; Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
slice begin_parse(cell c) asm "CTOS";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
int slice_bits(slice s) asm "SBITS";

() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
    if (in_msg_body.slice_bits() < 32) { return (); }
    int op = in_msg_body~load_uint(32);
    if (op != 0) { return (); }            ;; text-comment messages only
    slice ds = get_data().begin_parse();
    int total = ds.slice_bits() >= 64 ? ds~load_uint(64) : 0;
    set_data(begin_cell().store_uint(total + 1, 64).end_cell());
}

int total_logs() method_id {
    slice ds = get_data().begin_parse();
    if (ds.slice_bits() < 64) { return 0; }
    return ds~load_uint(64);
}
```
The payload itself lives forever in the transaction history; the counter proves the
contract actually processed it. Deploy with a node script that builds
`contractAddress(0, { code, data: beginCell().storeUint(0, 64).endCell() })`, sends 0.05 TON
with `init`, then writes the address into src/data/ton.json.

USER FLOW
1. 'Connect Tonkeeper' -> address chip + testnet badge.
2. Choreographers act on their group ownership; the wallet sheet shows the exact nanoton amount and comment.
3. The confirmed transaction appears in the feed with a tonscan link.
4. Footer renders: "Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14"

TON SURVIVAL KIT (non-obvious rules — follow all of them)
1. BUFFER: browsers have no `Buffer`, but every TON lib returns one, so
   `cell.toBoc().toString("base64")` throws "Buffer is not defined". Add one helper and
   await it at the top of EVERY function touching @ton/core, @ton/crypto or @ton/ton —
   including the TON Connect path, which is the one people forget:
     export async function ensureBuffer() {
       const g = globalThis as typeof globalThis & { Buffer?: unknown };
       if (g.Buffer) return;
       g.Buffer = (await import("buffer")).Buffer;
     }
2. ADDRESS FLAVOURS: one key renders as four strings. `EQ…`/`UQ…` are the mainnet
   flavours (bounceable / non-bounceable), `kQ…`/`0Q…` are the test-network flavours.
   Never string-compare addresses across flavours — normalise first with
   `Address.parse(a).equals(Address.parse(b))`. Render with an explicit flavour:
     addr.toString({ testOnly: true, bounceable: false, urlSafe: true })
   Send user transfers NON-bounceable (`bounce: false`); a bounceable send to an
   uninitialised wallet returns the funds and the demo looks broken.
3. WALLET VERSION: the same mnemonic derives DIFFERENT addresses per wallet version.
   A 24-word TON phrase -> `WalletContractV4` (workchain 0); a 12-word BIP39 phrase
   (Gram, MyTonWallet) -> `WalletContractV5R1`. Derive both, query balances, and use
   the funded one — do not guess. A TON wallet is itself a contract: it needs ~0.05 TON
   and its first send carries `init` with `seqno: 0`. "Not deployed" is normal for a
   fresh address, not an error.
4. EXPLORER: link everything to https://testnet.tonscan.org/address/<addr> and
   https://testnet.tonscan.org/tx/<hash>. Derive the base URL from config, never hardcode.
5. WALLET REALITY CHECK: mobile Tonkeeper and Gram ship mainnet-only, so a test-network
   demo opened on a phone dead-ends with "your wallet is on TON mainnet". Ship a fallback:
   generate a throwaway wallet in the browser (`mnemonicNew()` from @ton/crypto, mnemonic
   in localStorage, funded from the faucet) and sign locally with `WalletContractV4`.
   Lock that browser-held key to the test network — never let it touch real funds.
6. TONCENTER: v3 rate-limits hard (HTTP 429). Wrap every read in a retry with backoff and
   send `X-API-Key: TONCENTER_API_KEY` on anything user-facing.
7. PAYMENT VERIFICATION: verify SERVER-side only, never trust a client-reported hash.
   Mint a one-time `chx-<hex>` memo, then match the recipient's recent transactions on
   amount >= price, decoded comment === memo, `transaction_aborted === false`, and a
   15-minute freshness window. Comments must be sent as a base64 BOC, not plain text:
     beginCell().storeUint(0, 32).storeStringTail(memo).endCell().toBoc().toString("base64")
8. SSR: mount TON Connect client-side only — `lazy(() => import("./ton-client-entry"))`
   inside `<ClientOnly>` + `<Suspense>`. Importing @tonconnect/ui-react at route module
   scope crashes the server render. `tonconnect-manifest.json` must sit at a public https URL.
9. INSTALL: npm i @tonconnect/ui-react @ton/ton @ton/core @ton/crypto buffer
   (add @ton-community/func-js only when you compile and deploy a FunC contract).
END TON SURVIVAL KIT

REQUIRED SECRETS (Lovable -> Project Settings -> Secrets):
- TON_DEPLOYER_MNEMONIC  24-word testnet wallet seed used by the deploy script. Fund it free: https://t.me/testgiver_ton_bot
- TONCENTER_API_KEY      Optional but recommended for rate limits. Get one from https://t.me/tonapibot
- PINATA_JWT            IPFS uploads (only if the app pins media). Docs: https://docs.pinata.cloud/llms-full.txt

CREDIT (must appear in the UI footer AND as a comment header in every deployed contract):
Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
Appendix · Market

Market sizing.

TAM
$5B
dance collaborative economy
SAM
$500M
NFT group ownership market
SOM
$28M
active dance collectives

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

See also

Adjacent entries.