DepthMap Chain
Pin depth maps and photos together on IPFS to preserve immersive photo data immutably.
OUSD payouts & splits· split revenue
Section · Onchain
full primer →The primitive.
Every 3D photography sale is split automatically in OUSD between collaborators, so photographers each see their share arrive with its own proof link.
Why this primitivePinata JWT links image and depth layers securely with permanent CID storage.
Kernel
One payment is split into several OUSD transfers to collaborators by fixed percentages.
Drives the UI as
A split editor (names + %) and a payout ledger with one explorer link per transfer.
Mainnet · real OUSDOUSD has no testnet. Demo with small amounts (≤ 1 OUSD). Pick your chain right above the prompt.
OUSD contract addresses → Contracts page
Appendix · SecretsRequired keys.
PRIVY_APP_ID
Required. Google sign-in + embedded wallet on your chain. Read it in a route loader — Lovable rejects VITE_* secret names.
open ↗ETHEREUM_RPC_URL / SOLANA_RPC_URL
Optional, server-only. A private RPC (Alchemy, Helius) if the public one rate-limits you.
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 the secrets above are set first. read the build strategy →
Build on
Each chain gets its own complete, self-contained prompt.
Build "DepthMap Chain" in ONE Lovable message. A single-page app that uses OUSD (Open USD) on mainnet.
CONCEPT
Pin depth maps and photos together on IPFS to preserve immersive photo data immutably.
Discipline: Photography (3D photography). Primitive: OUSD payouts & splits — One payment is split into several OUSD transfers to collaborators by fixed percentages.
USER FLOW
A creator defines a split (e.g. 70/20/10) for a 3D photography sale. A buyer pays once; the app sends the OUSD shares sequentially from the buyer's wallet and lists each transfer with its explorer link.
UI: A split editor (names + %) and a payout ledger with one explorer link per transfer.
CHAIN — Base MAINNET (real money, no testnet)
- chainId 8453 · RPC https://mainnet.base.org · Explorer https://basescan.org
- OUSD on Base: 0xB2000000000000000000002fEb517dFeC7415344
- Tx link: https://basescan.org/tx/<hash> · Contract page: https://basescan.org/address/0xB2000000000000000000002fEb517dFeC7415344
- Gas: ETH on Base (cheap). Turn on Privy gas sponsorship for Base so users never need ETH.
- viem: `import { base } from 'viem/chains'` — use it as Privy `defaultChain` and in `supportedChains`.
- OUSD is an ERC-20. Transfer with `encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: [to, amount] })`, `value: 0n`, sent TO the OUSD address.
- Read `decimals()` from the contract once at boot — never hardcode it.
- Pass `{ sponsor: true }` to Privy `sendTransaction` (needs Base enabled in Privy → Gas Sponsorship).
CODE — paste these files as-is (install: `@privy-io/react-auth viem`)
// src/lib/ousd.ts
import { base as CHAIN } from 'viem/chains';
export { CHAIN };
import { createPublicClient, http, erc20Abi, encodeFunctionData, parseUnits, formatUnits } from 'viem';
export const OUSD = '0xB2000000000000000000002fEb517dFeC7415344' as const;
export const pub = createPublicClient({ chain: CHAIN, transport: http('https://mainnet.base.org') });
let dec: number | undefined;
export async function ousdDecimals() { return dec ??= await pub.readContract({ address: OUSD, abi: erc20Abi, functionName: 'decimals' }); }
export async function ousdBalance(owner: `0x${string}`) {
const raw = await pub.readContract({ address: OUSD, abi: erc20Abi, functionName: 'balanceOf', args: [owner] });
return Number(formatUnits(raw, await ousdDecimals()));
}
export async function transferTx(to: `0x${string}`, amount: string) {
const value = parseUnits(amount, await ousdDecimals());
return { to: OUSD, value: 0n, chainId: CHAIN.id,
data: encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: [to, value] }) };
}
export const txUrl = (h: string) => 'https://basescan.org/tx/' + h;
export async function waitFor(hash: `0x${string}`) { return pub.waitForTransactionReceipt({ hash, timeout: 90_000 }); }
// src/components/privy-root.tsx (default export; loaded with React.lazy inside <ClientOnly>)
import { PrivyProvider } from '@privy-io/react-auth';
import { CHAIN } from '@/lib/ousd';
export default function PrivyRoot({ appId, children }: { appId: string; children: React.ReactNode }) {
return <PrivyProvider appId={appId} config={{ loginMethods: ['google'], defaultChain: CHAIN, supportedChains: [CHAIN],
embeddedWallets: { ethereum: { createOnLogin: 'users-without-wallets' } } }}>{children}</PrivyProvider>;
}
// Sending (inside a component under PrivyRoot)
// const { sendTransaction } = useSendTransaction(); // from '@privy-io/react-auth'
// const { hash } = await sendTransaction(await transferTx(to, '1.00'), { sponsor: true });
// await waitFor(hash); show txUrl(hash).
STACK
- TanStack Start (the Lovable default). One page at the index route.
- Privy for sign-in (Google) and embedded wallets on the chain below. Load Privy with
`React.lazy(() => import('./privy-root'))` inside `<ClientOnly>` — a static import crashes SSR.
- Secret `PRIVY_APP_ID` (paste in Project Settings → Secrets). Read it in a route loader via a server
function (`process.env.PRIVY_APP_ID`) and pass it to the provider. NEVER name a secret `VITE_*`.
- Show amounts in OUSD with 2 decimals; convert with the on-chain decimals.
- No smart contract deploy is needed: OUSD is already live. Every action is an OUSD token transfer.
SHARED CODE — paste as-is
// src/lib/privy.functions.ts
import { createServerFn } from '@tanstack/react-start';
export const getPrivyAppId = createServerFn({ method: 'GET' })
.handler(async () => ({ appId: process.env['PRIVY_APP_ID'] ?? null }));
// src/routes/index.tsx (shape)
// loader: () => getPrivyAppId()
// const { appId } = Route.useLoaderData();
// const PrivyRoot = React.lazy(() => import('@/components/privy-root'));
// if (!appId) render the full UI in demo mode with a banner "Add PRIVY_APP_ID to go live" (no sends).
// else: <ClientOnly fallback={<Skeleton/>}><Suspense><PrivyRoot appId={appId}><App/></PrivyRoot></Suspense></ClientOnly>
// Inside <App/>: const { ready, authenticated, login, logout } = usePrivy(); wait for `ready` before reading wallets.
REAL MONEY RULES (mainnet only)
- Above every Pay button show: "Mainnet · real OUSD". Default demo amounts ≤ 1 OUSD.
- Before sending, check the OUSD balance; if too low, show "Get OUSD" linking to https://joinopenstandard.com/integrate.
- Always confirm the wallet is on the right chain before sending; refuse with a clear "switch network" message otherwise.
- After sending, wait for confirmation, then show the explorer link. Never show success before confirmation.
TROUBLESHOOTING
- "window is not defined" / blank SSR page → Privy was imported statically. Only import it via React.lazy inside <ClientOnly>.
- Wrong network / chain mismatch → defaultChain and supportedChains must be the same chain object; refuse to send otherwise.
- Solana "TokenAccountNotFound" → the idempotent create-ATA instruction must precede the transfer; balance = 0 when no ATA.
- "insufficient funds for gas" → enable Privy gas sponsorship for this chain, or tell the user which gas coin to add.
- Balance too low → disable Pay and show "Get OUSD" linking to https://joinopenstandard.com/integrate.
COMPLIANCE (mandatory — OUSD is issued by Bridge, a Stripe company)
- Show a footer notice: "OUSD is not available in the European Economic Area or sanctioned regions. See https://joinopenstandard.com/legal/geo-restrictions-and-prohibited-use-cases".
- Never build or market for prohibited uses: gambling, games of chance, lotteries, sweepstakes, unlawful
prediction markets, adult content, cannabis, tobacco, weapons, pharmaceuticals, luxury goods, jewelry,
watches, precious metals, mixing, multi-level marketing, counterfeit goods or deceptive practices.
- OUSD is acquired through partners (Stripe/Bridge/Privy, Coinbase, Visa, Mastercard) or exchanges
(Coinbase, Kraken, Uniswap). The app itself never mints or redeems.
DEFINITION OF DONE
1. Google sign-in via Privy creates an embedded wallet on the chain above and shows its OUSD balance.
2. The 3D photography flow sends a real OUSD transfer, waits for confirmation, and shows a working explorer link.
3. The compliance footer and the real-money notice are visible.
4. The page works with zero secrets (demo mode with a "Add PRIVY_APP_ID to go live" banner).
Credit in the footer: "Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14".Market sizing.
TAM
$2.4B
global photo software market
SAM
$110M
3D and depth photo tools
SOM
$14M
blockchain 3D photo users
Indicative figures for hackathon pitches — refine with your own research before raising.
RestrictionsOUSD is not available in the European Economic Area or sanctioned regions, and may not be used for gambling, lotteries, prediction markets, adult content, weapons and other prohibited uses. Read the full list ↗
Adjacent entries.
photo archiving
ChainFrame Archive
Securely store and verify photo archives with immutable IPFS-backed metadata for trusted provenance.
photojournalism documentationPinPhoto Journal
Verify and timestamp news photos on IPFS to ensure authentic, unaltered media for journalism.
color gradingColorChain Palette
Save and share color grading presets and images as permanent IPFS assets for consistent workflow.
metadata embeddingMetaFrame Vault
Embed and immutably store photo metadata on IPFS to preserve creator rights and data integrity.