Loading
Loading
The repo is a Bun workspace. Install once at the root and every app and package is wired up.
forge, cast, anvil) if you touch contracts or want a local fork.bun run abi (the ABI generator is a script).git clone <repo> collaborative-fund
cd collaborative-fund
bun install
cp apps/web/.env.example apps/web/.env.local
Edit apps/web/.env.local:
NEXT_PUBLIC_INDEX_FACTORY=0x... # the deployed factory, or the zero address for an empty feed
NEXT_PUBLIC_SITE_URL=http://localhost:3000
Then:
bun run dev
Open http://localhost:3000. With the zero address as factory the home page shows "No launcher configured" and the launch button has nothing to call; every other page still renders.
| Command | Does |
|---|---|
bun run dev | next dev for the web app |
bun run build | next build |
bun run start | next start (after a build) |
bun run typecheck | tsc --noEmit across every workspace |
bun run snapshot:stocks | Rescan tradable stocks and rewrite apps/web/lib/stocks.snapshot.json |
bun run abi | forge build then regenerate packages/abi/src/abi.ts |
bun run contracts:build | forge build in contracts/ |
bun run contracts:test | forge test in contracts/ (fork tests; hits the live chain) |
bun run contracts:deploy:mainnet -- <flags> | The deploy wrapper; see Deploying |
bun run video:dev | Remotion studio for the explainer video |
bun run video:render | Render apps/video/out/explainer.mp4 |
Web-only scripts, run from apps/web/: bun run logos:stocks refetches stock logos into public/stocks/, bun run logo:assets regenerates the icon and social card PNGs from public/logo.png.
All in apps/web/.env.local. Only the first one is required.
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_INDEX_FACTORY | Address of the deployed IndexFactory. Zero address disables launches and empties the feed. |
NEXT_PUBLIC_SITE_URL | Public origin. Makes Open Graph image URLs and uploaded logo URLs absolute. Vercel supplies VERCEL_PROJECT_PRODUCTION_URL instead, so it is optional there. |
NEXT_PUBLIC_RPC_URL | JSON-RPC override. Default is the public Robinhood endpoint. |
NEXT_PUBLIC_CHAIN_ID | Chain id override for forks. Default 4663. |
NEXT_PUBLIC_PONS_APP | Base URL for "Trade on Pons" links. Default https://www.ponsfamily.com. |
DATABASE_URL | Postgres connection string. Enables the logo upload button. |
The wallet chooses its RPC by chain id, not from the site. A fork that keeps chain id 4663 would have your wallet sign against the fork's state and send to mainnet. Give the fork its own id.
anvil --fork-url https://rpc.mainnet.chain.robinhood.com --chain-id 31337
In .env.local:
NEXT_PUBLIC_RPC_URL=http://127.0.0.1:8545
NEXT_PUBLIC_CHAIN_ID=31337
The Connect button then offers to add "Robinhood Chain (anvil fork)" as a separate network pointing at the fork. Deploy a factory to the fork (see Deploying, fork rehearsal) and put its address in NEXT_PUBLIC_INDEX_FACTORY.
Anvil's default accounts on a Robinhood fork may have EIP-7702 delegations that forward incoming ETH elsewhere. If a funded default account keeps showing zero, clear its code first:
cast rpc anvil_setCode <account> 0x --rpc-url http://127.0.0.1:8545
The public RPC rate-limits, and a fork startup pulls a lot of state. An archive RPC of your own makes this much smoother.
Set DATABASE_URL to any Postgres. The upload route creates a logos table on first use (id, content_type, bytes, created_at); no migration step. Uploads are capped at 2 MB and limited to PNG, JPEG, WebP, GIF, and SVG. Served from /api/logo/<id> with a one-year immutable cache header and a CSP that sandboxes SVG.
Without DATABASE_URL the button reports itself unconfigured and the URL field works as usual.
apps/web/lib/stocks.ts fetches Robinhood's asset list, asks the Uniswap v3 factory for a WETH pool at each of the three fee tiers, reads each pool's liquidity, and keeps the deepest non-empty pool per stock. Around 600 calls, batched through Multicall3. Result memoized for five minutes per process; on failure it falls back to the snapshot.
GET /api/stocks serves the current list as JSON.
Regenerate the snapshot when the tradable set changes:
bun run snapshot:stocks
git add apps/web/lib/stocks.snapshot.json
cd contracts
forge build
forge test -vv
The tests fork Robinhood Chain and launch against the live Pons factory, so they need network access and a working RPC. foundry.toml defines a robinhood RPC alias.
One thing the tests do that is easy to trip over: they call vm.getBlockTimestamp() rather than block.timestamp when computing warp targets. The optimizer folds repeated TIMESTAMP reads, which makes vm.warp(block.timestamp + delta) warp from a stale value. Keep doing it the tests' way when you add cases.
After changing a contract, regenerate the ABIs the web app imports:
bun run abi
bun run build
bun run start
The build prerenders pages that need the stock list. Memoization keeps that to one scan, and the snapshot fallback keeps a throttled RPC from failing the build. If the build logs live stock scan failed, using snapshot, that fallback fired.
.railwayignore at the root excludes contracts/, scripts/, and the video app from a Railway build; the web app does not need them.
apps/video/ is a Remotion project that walks through the app: home, launch form, harvest, stake. bun run video:dev opens the studio; bun run video:render writes a 1920x1080, 30 fps MP4 about 44 seconds long to apps/video/out/. The primitives in apps/video/src/app/primitives.tsx re-type the web app's design tokens as inline styles, so a change to apps/web/app/globals.css should be mirrored there if it affects the scenes.