Skip to content

[Infra] Add contract_address column for multi-contract support #233

@realproject7

Description

@realproject7

Problem

When the StoryFactory contract is redeployed (e.g. for Phase 9 title support), the new contract starts fresh with zero storylines. All existing Supabase data references the old contract and becomes orphaned. Currently there's no way to distinguish which contract created which data, forcing a full DB wipe on every redeploy.

Solution

Add a contract_address column to all data tables. The frontend filters by NEXT_PUBLIC_CONTRACT_ADDRESS (already an env var), so switching contracts is just an env var change — no data loss, no migration scripts.

DB Migration

Add contract_address TEXT NOT NULL to all 6 tables, defaulting existing rows to the old contract:

ALTER TABLE storylines ADD COLUMN contract_address TEXT NOT NULL DEFAULT '0x05c4d59529807316d6fa09cdaa509addfe85b474';
ALTER TABLE plots ADD COLUMN contract_address TEXT NOT NULL DEFAULT '0x05c4d59529807316d6fa09cdaa509addfe85b474';
ALTER TABLE donations ADD COLUMN contract_address TEXT NOT NULL DEFAULT '0x05c4d59529807316d6fa09cdaa509addfe85b474';
ALTER TABLE ratings ADD COLUMN contract_address TEXT NOT NULL DEFAULT '0x05c4d59529807316d6fa09cdaa509addfe85b474';
ALTER TABLE comments ADD COLUMN contract_address TEXT NOT NULL DEFAULT '0x05c4d59529807316d6fa09cdaa509addfe85b474';
ALTER TABLE page_views ADD COLUMN contract_address TEXT NOT NULL DEFAULT '0x05c4d59529807316d6fa09cdaa509addfe85b474';

Important: Use lowercase addresses everywhere.

After migration, update the defaults to the current contract so new inserts are tagged correctly:

ALTER TABLE storylines ALTER COLUMN contract_address SET DEFAULT '0x6b8d38af1773dd162ebc6f4a8eb923f3c669605d';
ALTER TABLE plots ALTER COLUMN contract_address SET DEFAULT '0x6b8d38af1773dd162ebc6f4a8eb923f3c669605d';
ALTER TABLE donations ALTER COLUMN contract_address SET DEFAULT '0x6b8d38af1773dd162ebc6f4a8eb923f3c669605d';
ALTER TABLE ratings ALTER COLUMN contract_address SET DEFAULT '0x6b8d38af1773dd162ebc6f4a8eb923f3c669605d';
ALTER TABLE comments ALTER COLUMN contract_address SET DEFAULT '0x6b8d38af1773dd162ebc6f4a8eb923f3c669605d';
ALTER TABLE page_views ALTER COLUMN contract_address SET DEFAULT '0x6b8d38af1773dd162ebc6f4a8eb923f3c669605d';

Better approach: Don't hardcode the default in the migration. Instead, have the indexers and API routes explicitly pass STORY_FACTORY.toLowerCase() on every insert, and set the DB default to '' (empty). This way the migration doesn't need updating on future redeploys.

Frontend: filter all queries by STORY_FACTORY

Every Supabase query that reads storylines, plots, donations, ratings, comments, or page_views must add:

.eq("contract_address", STORY_FACTORY.toLowerCase())

Files to update (all queries):

  • src/app/page.tsx — home page storyline list
  • src/app/story/[storylineId]/page.tsx — story page
  • src/app/story/[storylineId]/[plotIndex]/page.tsx — plot detail page
  • src/app/dashboard/writer/page.tsx — writer dashboard
  • src/app/dashboard/reader/page.tsx — reader dashboard
  • src/components/WriterTradingStats.tsx — donation totals
  • src/components/DonateWidget.tsx — (no reads, but verify)
  • src/components/ViewCount.tsx — view count reads
  • lib/ranking.ts — trending/rising queries
  • Any other file querying Supabase

Indexers: stamp contract_address on insert

All indexer routes must include contract_address: STORY_FACTORY.toLowerCase() in inserted rows:

  • src/app/api/index/storyline/route.ts
  • src/app/api/index/plot/route.ts
  • src/app/api/index/donation/route.ts
  • src/app/api/views/route.ts
  • src/app/api/comments/route.ts
  • src/app/api/ratings/route.ts (if exists)

TypeScript types

Update lib/supabase.ts — add contract_address: string to all table Row/Insert/Update types.

Acceptance Criteria

  • All 6 tables have contract_address column
  • Existing rows tagged with old contract address
  • All frontend queries filter by STORY_FACTORY.toLowerCase()
  • All indexer inserts include contract_address
  • Supabase types updated
  • Switching NEXT_PUBLIC_CONTRACT_ADDRESS env var instantly shows only new contract data
  • Old contract data preserved and queryable
  • npm run typecheck passes

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions