Bitcoin testnet4 · Spark REGTEST · no mainnet paths
Appendix — Build Strategy

The Bitcoin-Native Protocol.

Every entry in this repo ships as a single Lovable build against Bitcoin testnet4 and Spark REGTEST. No EVM, no MetaMask, no gas token. Two secrets, one paste, and the demo is provably on Bitcoin.

Why Bitcoin testnet4 (and not mainnet)?

testnet4 is the current Bitcoin Core test network — same script rules, same PSBT flow, same wallet UX — but coins are free from the faucet and blocks confirm every ~10 minutes. Hackathon judges can verify every OP_RETURN and inscription on mempool.space/testnet4 without spending a satoshi of real money.

Spark is a Bitcoin L2 (statechain-style) that Privy integrates natively via chainType: 'spark'. It gives you instant, sub-cent sats transfers with a social-login wallet — the ergonomics of L2 payments with Bitcoin as the settlement layer.

Attribution requirement

Every showcase built from a Creative Bitcoin prompt MUST carry the credit visibly in the footer: "Built during the Creative AI & Quantum Hackathon — organised by StreetKode Fam during Indian Krump Festival 14." On-chain artefacts (OP_RETURN payloads, inscription metadata, IPFS manifests) should include a short version of the same credit.

1. Sign in with Privy Spark

Google or email → embedded wallet with a Spark address and a testnet4 BTC deposit address. No seed phrase, no browser extension.

// src/main.tsx — Privy Spark: Bitcoin L2 wallet via Google sign-in, no seed phrase
import { PrivyProvider } from "@privy-io/react-auth";
import { useCreateWallet, useWallets } from "@privy-io/react-auth/extended-chains";

<PrivyProvider
  appId={import.meta.env.VITE_PRIVY_APP_ID}
  config={{ loginMethods: ["google", "email"], appearance: { theme: "dark" } }}
>
  {/* inside a child component: */}
  {/*   const { createWallet } = useCreateWallet();                       */}
  {/*   await createWallet({ chainType: 'spark' });   // network REGTEST  */}
  {/*   const spark = useWallets().wallets.find(w => w.chainType==='spark') */}
  {/*   await spark.getStaticDepositAddress();  // testnet4 BTC deposit   */}
  <App />
</PrivyProvider>

2. Anchor a SHA-256 in OP_RETURN

Hash the payload client-side, POST to a server route that composes a PSBT with an OP_RETURN output, sign, broadcast via mempool.space. The txid is your proof.

// src/routes/api/public/anchor.ts — anchors a SHA-256 into OP_RETURN on testnet4
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/api/public/anchor")({
  server: {
    handlers: {
      POST: async ({ request }) => {
        const { hash } = await request.json();               // 32-byte hex
        // 1. compose a PSBT that spends a testnet4 UTXO and adds an OP_RETURN
        //    output carrying `hash`. Sign with your Privy Spark wallet's derived
        //    BTC key (getStaticDepositAddress) or a server-held test key.
        // 2. broadcast via mempool.space
        const res = await fetch("https://mempool.space/testnet4/api/tx", {
          method: "POST",
          headers: { "content-type": "text/plain" },
          body: signedTxHex,
        });
        const txid = await res.text();
        return Response.json({ txid, url: `https://mempool.space/testnet4/tx/${txid}` });
      },
    },
  },
});

3. Gate a route behind a Spark sats transfer

User signs a Spark transfer via Privy for N sats. A merchant-owned Spark wallet (server-side, using PRIVY_APP_SECRET) polls getTransfersto confirm receipt, then releases the payload.

// src/routes/api/public/paywall-confirm.ts — merchant-side sats receipt check
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/api/public/paywall-confirm")({
  server: {
    handlers: {
      POST: async ({ request }) => {
        const { sparkTxId, expectedSats } = await request.json();
        // Call Privy Spark RPC as the *merchant* wallet, using PRIVY_APP_SECRET.
        const rpc = await fetch(
          `https://api.privy.io/v1/wallets/${process.env.MERCHANT_SPARK_WALLET_ID}/rpc`,
          {
            method: "POST",
            headers: {
              authorization: `Basic ${Buffer.from(
                process.env.PRIVY_APP_ID + ":" + process.env.PRIVY_APP_SECRET,
              ).toString("base64")}`,
              "content-type": "application/json",
            },
            body: JSON.stringify({ method: "getTransfers", params: { limit: 10 } }),
          },
        );
        const { data } = await rpc.json();
        const ok = data.transfers?.some(
          (t: { id: string; amount_sats: number; direction: string }) =>
            t.id === sparkTxId && t.direction === "incoming" && t.amount_sats >= expectedSats,
        );
        return Response.json({ ok });
      },
    },
  },
});

4. Ship

# 1. In Lovable, add two runtime secrets:
PRIVY_APP_ID=cl...
PRIVY_APP_SECRET=...              # server-side, for merchant Spark RPC
PINATA_JWT=eyJhbGciOi...

# 2. Fund the Privy-derived BTC address on testnet4:
open https://mempool.space/testnet4/faucet

# 3. Copy any mega-prompt from this catalog into Lovable. The prompt:
#    - wires Privy Spark sign-in (Google/email)
#    - pins the payload to IPFS via Pinata
#    - anchors a SHA-256 in an OP_RETURN on testnet4 (or inscribes on ordinals)
#    - gates a route behind a Spark sats transfer to the merchant address
#    - exposes the mempool.space + Sparkscan links in the UI

# 4. Open the live mempool.space link. Your demo is provably on Bitcoin.
  • · No MetaMask. No gas token. No EVM contracts to verify.
  • · Always link the mempool.space testnet4 tx and (for Spark payments) the Sparkscan tx.
  • · Keep the OP_RETURN payload short — 32-byte SHA-256 or a hash of the IPFS CID.