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
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_addresscolumn to all data tables. The frontend filters byNEXT_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 NULLto all 6 tables, defaulting existing rows to the old contract:Important: Use lowercase addresses everywhere.
After migration, update the defaults to the current contract so new inserts are tagged correctly:
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:
Files to update (all queries):
src/app/page.tsx— home page storyline listsrc/app/story/[storylineId]/page.tsx— story pagesrc/app/story/[storylineId]/[plotIndex]/page.tsx— plot detail pagesrc/app/dashboard/writer/page.tsx— writer dashboardsrc/app/dashboard/reader/page.tsx— reader dashboardsrc/components/WriterTradingStats.tsx— donation totalssrc/components/DonateWidget.tsx— (no reads, but verify)src/components/ViewCount.tsx— view count readslib/ranking.ts— trending/rising queriesIndexers: stamp contract_address on insert
All indexer routes must include
contract_address: STORY_FACTORY.toLowerCase()in inserted rows:src/app/api/index/storyline/route.tssrc/app/api/index/plot/route.tssrc/app/api/index/donation/route.tssrc/app/api/views/route.tssrc/app/api/comments/route.tssrc/app/api/ratings/route.ts(if exists)TypeScript types
Update
lib/supabase.ts— addcontract_address: stringto all table Row/Insert/Update types.Acceptance Criteria
contract_addresscolumnSTORY_FACTORY.toLowerCase()contract_addressNEXT_PUBLIC_CONTRACT_ADDRESSenv var instantly shows only new contract datanpm run typecheckpasses