Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ HATS_CONTRACT_ADDRESS=
ANGRY_DWARF_HAT_ID=

# External APIs
# Optional; public CoinGecko price endpoint is used when unset.
COINGECKO_API_KEY=
75 changes: 44 additions & 31 deletions PROJECT_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,20 @@ The homepage should always be visible to members who pass wallet access control,

Homepage content:

- Current total treasury balance in USD.
- Asset breakdown.
- Account breakdown.
- Current total treasury balance in USD, starting with onchain Gnosis accounts and later including the latest imported bank balance snapshot.
- Asset breakdown across included balance sources.
- Account breakdown by Treasury, side-vault, and eventually bank balance source.
- Published quarter cards.
- Export buttons for published quarters.

The homepage should not include a recent activity feed in V1.

Balance refresh behavior:

- The homepage serves the latest cached treasury balance immediately.
- Any authenticated member may trigger a background refresh when cached balance data is missing or older than one hour.
- Stale data should remain readable and dated, with clear sync timing and subtle refreshing/updated UI states.

## 8. Data Sources

### Main Safe
Expand All @@ -166,9 +172,10 @@ The first automated onchain source is the main RaidGuild Safe on Gnosis.

The main Safe address is environment-configured.

The app should fetch:
The app should fetch current balances for USDC, xDAI, wxDAI, and wETH through the configured Gnosis RPC. Stable assets are valued 1:1 with USD. wETH uses CoinGecko pricing when available, with the API key optional.

Later main Safe ingestion should fetch:

- Current balances.
- Native transfers.
- ERC-20 transfers.
- Safe transaction metadata where useful.
Expand All @@ -178,24 +185,27 @@ The app should fetch:

Side-vaults come after main Safe support.

Side-vault records are admin-managed address book entries.
Side-vault records are Angry Dwarf admin-managed Gnosis Safe/multisig entries.

Fields:

- Name.
- Address.
- Network/type.
- Gnosis chain.
- Notes.
- Whether DAO-controlled or independent multisig.
- Active/archived status.

Side-vault behavior:

- Queried alongside the main treasury.
- Included in balances and reports.
- Included in homepage balances and later reports.
- Labeled by source account.
- Some side-vaults are controlled by DAO proposals.
- Some are separate multisigs and will not have DAO proposal links.

Onchain balance syncing is Gnosis-only for V1. Future bank imports should add the latest imported bank balance snapshot to the homepage total without changing the onchain account model.

### DAO Proposals

The app should fetch DAO proposals that moved money during execution.
Expand Down Expand Up @@ -544,58 +554,61 @@ Each step should be its own PR.
4. Member homepage
- Current treasury balance UI with mocked/configured data shape, asset/account breakdown.

5. Main Safe ingestion
- Gnosis Safe/RPC integration, import balances/transactions from env-configured main Safe.
5. Main Safe balance ingestion
- Gnosis RPC integration, cached balances from env-configured Treasury.

6. Side-vault admin
- Admin-managed Gnosis side-vault CRUD using treasury account records, including active/archived state and DAO-controlled labeling.

6. Quarter workspace model
7. Multi-account balance sync
- Sync Treasury plus active side-vault balances, include them in the homepage total, account breakdown, and aggregated asset breakdown.

8. Quarter workspace model
- Q1 workspace, statuses, draft/review/published/reopened flow, quarter audit history.

7. Core entity management
9. Core entity management
- Clients, raids, providers, subcontractors, address book CRUD with admin-only writes.

8. Transaction classification
10. Main Safe and side-vault transaction ingestion
- Import Gnosis native/ERC-20 transfers, Safe metadata where useful, tx hashes, and timestamps from tracked onchain accounts.

11. Transaction classification
- Classify imported transactions, link to entities, categories, notes, verification/source metadata.

9. Raid accounting views
12. Raid accounting views
- Raid revenue, 10% spoils calculation, subcontractor payout summaries, unpaid/remaining views.

10. DAO proposal linking
13. DAO proposal linking
- Fetch money-moving proposals, link by execution tx hash, proposal expense page.

11. Membership activity reports
14. Membership activity reports
- Member dues/joins and ragequit classification/report pages.

12. Side-vault address book
- Admin-managed Gnosis side-vaults, source account labeling.

13. Side-vault ingestion
- Balance/transaction scan for side-vaults, included in homepage and quarter workspaces.

14. Manual transaction lookup
15. Manual transaction lookup
- Paste tx hash, support Gnosis/Arbitrum/Optimism/Ethereum/Base, fetch native/ERC-20 transfers.

15. Manual raid revenue flow
16. Manual raid revenue flow
- Cleric/admin flow to select transfer(s), allocate revenue to client/raid, support split revenue and flagged unverified entries.

16. Manual raid payout flow
17. Manual raid payout flow
- Cleric/admin flow to select transfer(s), allocate payout lines to raid/subcontractors, support split payouts and flagged unverified entries.

17. Spoils linking flow
18. Spoils linking flow
- Cleric/admin flow to link spoils inflows to raids and compare actual spoils against expected 10%.

18. RIP tracking
19. RIP tracking
- RIP CRUD, external links, linked tx/proposals/payees, most/least expensive views.

19. Bank CSV import
20. Bank CSV import
- Upload, parse, normalize rows, discard original file, encrypted sensitive fields, duplicate detection.

20. Bank transaction classification
21. Bank transaction classification
- Classify imported bank rows, link to quarter/entities/reports.

21. XLSX export
22. XLSX export
- Q1 workbook with all required tabs, source hashes, verification flags, last published/updated date.

22. Publish/export polish
23. Publish/export polish
- Member-facing published quarter view, export buttons, read-only locked state, final UI/security pass.

## 14. Open Questions
Expand Down
40 changes: 40 additions & 0 deletions drizzle/0002_lovely_colossus.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
CREATE TYPE "public"."treasury_snapshot_status" AS ENUM('pending_live_sync', 'synced', 'stale_syncing', 'partial', 'failed');--> statement-breakpoint
CREATE TABLE "treasury_balance_assets" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"snapshot_id" uuid NOT NULL,
"symbol" text NOT NULL,
"name" text NOT NULL,
"decimals" integer NOT NULL,
"raw_amount" numeric(78, 0) NOT NULL,
"balance" numeric(36, 18) NOT NULL,
"usd_price" numeric(18, 8) NOT NULL,
"usd_value" numeric(18, 2) NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "treasury_balance_snapshots" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"account_address" text NOT NULL,
"chain_id" integer NOT NULL,
"status" "treasury_snapshot_status" NOT NULL,
"total_usd" numeric(18, 2) NOT NULL,
"synced_at" timestamp with time zone NOT NULL,
"error_message" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "treasury_balance_assets" ADD CONSTRAINT "treasury_balance_assets_snapshot_id_treasury_balance_snapshots_id_fk" FOREIGN KEY ("snapshot_id") REFERENCES "public"."treasury_balance_snapshots"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "treasury_balance_assets_snapshot_id_idx" ON "treasury_balance_assets" USING btree ("snapshot_id");--> statement-breakpoint
CREATE UNIQUE INDEX "treasury_balance_assets_snapshot_symbol_unique" ON "treasury_balance_assets" USING btree ("snapshot_id","symbol");--> statement-breakpoint
CREATE INDEX "treasury_balance_snapshots_chain_account_synced_idx" ON "treasury_balance_snapshots" USING btree ("chain_id","account_address","synced_at" DESC);--> statement-breakpoint
CREATE INDEX "treasury_balance_snapshots_synced_at_idx" ON "treasury_balance_snapshots" USING btree ("synced_at");--> statement-breakpoint
CREATE TRIGGER "treasury_balance_assets_set_updated_at"
BEFORE UPDATE ON "treasury_balance_assets"
FOR EACH ROW
EXECUTE FUNCTION "public"."set_updated_at"();--> statement-breakpoint
CREATE TRIGGER "treasury_balance_snapshots_set_updated_at"
BEFORE UPDATE ON "treasury_balance_snapshots"
FOR EACH ROW
EXECUTE FUNCTION "public"."set_updated_at"();
Loading
Loading