Skip to content

docs(torn): DAO anatomy, tech funnel & remaining-gap roadmap#2004

Closed
Zeugh-eth wants to merge 5 commits into
feat/tornado-cash-integrationfrom
docs/torn-research
Closed

docs(torn): DAO anatomy, tech funnel & remaining-gap roadmap#2004
Zeugh-eth wants to merge 5 commits into
feat/tornado-cash-integrationfrom
docs/torn-research

Conversation

@Zeugh-eth

Copy link
Copy Markdown
Member

What

Adds apps/indexer/src/indexer/torn/RESEARCH.md — a companion to the existing INTEGRATION.md in the same directory. Independent research on the Tornado Cash governance model with a concrete roadmap for the remaining gaps.

Targets feat/tornado-cash-integration so the documentation lands together with #2002.

Contents

  • Anatomy of the DAO — contract topology, why it is not a Governor Bravo, and the live on-chain parameters (verified against tornadocash/tornado-governance, impl GovernanceProposalStateUpgrade / 5.proposal-state-patch).
  • Tech funnel — the indexer → Postgres → API → anticapture-client → dashboard pipeline, with the white-label path.
  • What feat: integrate tornado cash #2002 already delivers (so nothing is redone).
  • Open gaps (reconciled with INTEGRATION.md) and a prioritized next-steps roadmap:
    • P1 – proposal-target verification (unverified / look-alike address detection) — directly addresses the 2026-06-25 Proposal 67 attack; currently alreadySupportCalldataReview() = false.
    • P2 – per-account voting power via RewardUpdateSuccessfulreadContract lockedBalance.
    • P3 – correctness fixes (the items raised as inline comments on feat: integrate tornado cash #2002).

Related

Docs-only PR. No code changes.

…p roadmap

Companion to INTEGRATION.md. Documents the Tornado Cash custom-governor
anatomy, the indexer->API->dashboard data funnel, on-chain parameter
verification, and a prioritized next-steps roadmap (proposal-target
verification for the 2026-06-25 Proposal 67 attack, per-account voting
power, and correctness fixes raised in review).
@railway-app

railway-app Bot commented Jun 29, 2026

Copy link
Copy Markdown

This PR was not deployed automatically as @Zeugh-eth does not have access to the Railway project.

In order to get automatic PR deploys, please add @Zeugh-eth to your workspace on Railway.

@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
anticapture-storybook Ready Ready Preview, Comment Jun 29, 2026 8:03pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
anticapture Ignored Ignored Jun 29, 2026 8:03pm

Request Review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 753d61673d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +94 to +95
**White-label:** already supported — `middleware.ts` resolves hostname→daoId; a custom domain only needs
`hostnames: [...]` in `torn.ts`. No platform work.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add whitelabel config to custom-domain setup

For TORN, adding only hostnames would make middleware.ts rewrite the custom hostname, but the rewritten /whitelabel/[daoId] route immediately returns notFound() unless isWhitelabelDao(daoConfig) is true; that helper requires daoConfig.whitelabel to be present (apps/dashboard/shared/utils/whitelabel.ts:37-41, apps/dashboard/app/whitelabel/[daoId]/layout.tsx:87-90). Since torn.ts currently has no whitelabel field, following this note would produce a 404 until the config also includes whitelabel: {} or equivalent.

Useful? React with 👍 / 👎.

Comment on lines +87 to +88
▼ OpenAPI
[4] anticapture-client (kubb → TanStack Query hooks) → gateful (gateway)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Put Gateful before client codegen

In the contract-change workflow, this reverses the actual pipeline: Gateful aggregates the DAO APIs and the Kubb SDK reads the live Gateful OpenAPI URL (packages/anticapture-client/kubb.config.ts:29-55), not an API spec that is then sent through the client to Gateful. Following this diagram for TORN API changes would skip the gateway/spec step and can leave generated client hooks stale or missing gateway-level DAO paths; the funnel should be API → Gateful → @anticapture/client → Dashboard.

Useful? React with 👍 / 👎.

- Tech funnel: correct to API -> Gateful -> @anticapture/client -> Dashboard
  (Kubb reads Gateful's live OpenAPI; kubb.config.ts:29-55).
- White-label: require whitelabel:{} in addition to hostnames, or the
  /whitelabel/[daoId] route 404s via isWhitelabelDao (whitelabel.ts:37-41).
@Zeugh-eth

Copy link
Copy Markdown
Member Author

Thanks @codex — both addressed in 53cd4e89:

  • Tech funnel corrected to API → Gateful → @anticapture/client → Dashboard (Kubb reads Gateful's live OpenAPI).
  • White-label note now requires whitelabel: {} in addition to hostnames (flagged that torn.ts has no whitelabel field today, so hostnames-only would 404 via isWhitelabelDao).
    Analysis/roadmap content is unchanged — only the build-mechanics were corrected to match the codebase.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@Zeugh-eth

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 53cd4e8919

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

▼ ▼▼
[1] INDEXER apps/indexer/src/indexer/torn/{governor,erc20}.ts
• bool→0/1 support · single target→targets:[t] · timestamp→synthetic block
• delegatedSupply from Transfers in/out of governor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use Vault transfers for delegated-supply tracking

For post-v2 TORN locks, tokens are transferred to/from TornadoVault, not just the governor; this document already lists the vault as holding locked TORN since v2 and later says to backfill from governor+Vault. If a follow-up uses this funnel literally, the indexer will keep watching only the governor address and miss new lock/unlock events, leaving delegatedSupply/account power stale. Please describe the signal as legacy governor plus TornadoVault transfers.

Useful? React with 👍 / 👎.

- [x] Proposals (custom timestamp handler), votes (binary), execution
- [x] `TORNClient` with live `QUORUM_VOTES` + timestamp-based status
- [x] Dashboard config: attack-profitability, governance-implementation risk matrix, attack-exposure, holders/delegates, token distribution
- [x] Backfill verified (65 proposals, 49 executed, 1,089 votes — matches chain)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Date-scope the backfill verification counts

This file is dated 2026-06-29 and opens by citing Proposal 67, so saying #2002's backfill “65 proposals ... matches chain” reads as current even though at least proposals 66/67 now exist. If maintainers rely on this to decide the integration is complete, they may skip re-running a backfill that should cover the newer proposals; mark these numbers as March 2026 verification or update them.

Useful? React with 👍 / 👎.

…kfill

- delegatedSupply: note locks route to TornadoVault post-v2; indexer
  watches governor only (erc20.ts:150-159) -> add gap #6 (watch Vault too).
- Date-scope the March 2026 backfill counts; flag proposals 66-67 (incl.
  the June 2026 attack) need a re-run.
@Zeugh-eth

Copy link
Copy Markdown
Member Author

Round 2 addressed in 36667937 (analysis kept, mechanics corrected):

  • Vault lock custody — funnel now says governor + TornadoVault, and added gap feat: map events in the governor contract #6: the shipped indexer watches governor-only (erc20.ts:150-159) while post-v2 locks route to the Vault (GovernanceVaultUpgrade._transferTokens), so vault locks can be missed — flagged to watch both. (Worth a look on feat: integrate tornado cash #2002.)
  • Backfill counts — date-scoped to March 2026 and flagged proposals 66–67 (incl. the June 2026 attack) for a re-run.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36667937c8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +9 to +11
On **2026-06-25, Proposal 67** pointed execution at an **unverified look-alike contract**
(`0x5efda50f22d34f27…` vs the real governor `0x5efda50f22d34F26…` — differs only from the 16th
hex char) to swap governance control over a ~$23M TORN treasury. It drew **0 for / 27,163 against**

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Model proposal 67 as prefix spoofing

The reported proposal 67 attacker-controlled address is 0x5efda50f22d34f272c7077689d6abc42f15e285f, so it shares the first 15 hex characters with the governor and then diverges; it is not a one-character edit of the full governor address (see the Foresight/HTX report: https://www.htx.com/news/tornado-cash-suffers-another-governance-attack-a-fake-propos-w8ZWBHYX/). Because §5 turns this incident into the requirements for an address-similarity detector, describing it as “differs only from the 16th hex char” can lead implementers to look for single-character typos instead of vanity-prefix spoofing; please reword this to say it shares the first 15 chars and diverges at the 16th.

Useful? React with 👍 / 👎.

▼ ▼▼
[1] INDEXER apps/indexer/src/indexer/torn/{governor,erc20}.ts
• bool→0/1 support · single target→targets:[t] · timestamp→synthetic block
• delegatedSupply from TORN Transfers in/out of governor + TornadoVault (post-v2 locks route to the Vault — see §4)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Mark Vault transfer tracking as missing

This funnel describes the shipped TORN indexer as deriving delegatedSupply from both governor and TornadoVault transfers, but apps/indexer/src/indexer/torn/erc20.ts still only compares from/to with governorAddress, and §4.6 says Vault support is still a gap. In the post-v2 Vault-lock case, this wording can make a follow-up skip the missing Vault indexing, so please mark the Vault leg here as TODO/currently missing or describe the current governor-only behavior.

Useful? React with 👍 / 👎.

- Frame Proposal 67 as a vanity-prefix spoof (shared first 15 hex chars,
  diverges at 16th); detector in §5 must match leading prefixes, not typos.
- Funnel: state governor-only delegatedSupply today (erc20.ts:150-159) and
  mark TornadoVault transfer tracking as TODO (consistent with §4 gap #6).
@Zeugh-eth

Copy link
Copy Markdown
Member Author

Round 3 addressed in 5ececaa3:

  • Prefix-spoof framing — Proposal 67 now described as a vanity-prefix collision (shared first 15 hex chars, diverges at the 16th); §5's detector is directed to match leading prefixes, not single-char typos. Thanks for the Foresight/HTX reference.
  • Vault leg — funnel now states the shipped indexer is governor-only (erc20.ts:150-159) and marks TornadoVault tracking as TODO, consistent with §4 gap feat: map events in the governor contract #6.

@codex review

@Zeugh-eth

Zeugh-eth commented Jun 29, 2026

Copy link
Copy Markdown
Member Author

🤖↔🤖 AI review loop — audit summary (read this first)

This PR was iterated with the @codex auto-reviewer. To keep the human audit surface small, the whole loop is logged here in one place — review this table instead of every thread. All changes are to RESEARCH.md only (documentation); the analysis, anatomy and roadmap content is unchanged — only build-mechanics/accuracy were corrected to match the codebase.

Round Commit Codex finding (severity) Resolution
753d6167 initial doc DAO anatomy + tech funnel + roadmap
1 53cd4e89 Funnel order reversed (P2); white-label needs whitelabel:{} not just hostnames (P2) Funnel → API→Gateful→client→Dashboard (Kubb reads Gateful's live OpenAPI); white-label note now requires whitelabel:{}
2 36667937 Post-v2 locks go to Vault not governor (P2); "65 proposals" reads as current (P3) Funnel/gap updated; backfill counts date-scoped to Mar 2026, flagged proposals 66–67
3 5ececaa3 Proposal 67 is a vanity-prefix spoof, not a typo (P2); funnel overstated Vault tracking as shipped (P3) §0 reworded (shares first 15 hex chars; detector matches prefixes); funnel marks Vault leg TODO
4 76058864 Voting-power snapshot must watch RewardUpdateFailed too, not only …Successful (P2) P2 roadmap now triggers on both reward-update outcomes (lock/unlock changes balance even on the failed path) + reconcile via transfers

Status of each Codex comment: addressed and 👍-reacted; superseded comments are on older commits.

⚠️ Items that are real code findings, not doc nits

  1. Vault lock custody (gap feat: map events in the governor contract #6). Shipped indexer detects locks via governor transfers only (apps/indexer/src/indexer/torn/erc20.ts:150-159); post-v2 lock routes TORN to the TornadoVault (GovernanceVaultUpgrade._transferTokens) → delegatedSupply/account power can drift. Decision needed: fix in feat: integrate tornado cash #2002 or track separately.
  2. RewardUpdateFailed coverage (round 4). Any future P2 voting-power indexer must watch both reward-update events; documented in §5 P2.

What a human still needs to check

  • The two code findings above.
  • This PR is docs-only, base feat/tornado-cash-integration.

Loop status

Rounds 1–4 each surfaced a substantive (P2) item, all applied. The loop has been productive (not just nitpicking), so it has not been frozen — author can run one more @codex review to confirm convergence, or freeze here.

This comment is the canonical log; per-round replies are kept for traceability.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: 5ececaa336

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@Zeugh-eth

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ececaa336

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +155 to +156
- Use `RewardUpdateSuccessful(account)` (fires on every lock/unlock) as a "balance-changed" trigger to
`readContract lockedBalance(account)` and write `votingPowerHistory` + `accountPower.votingPower`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle failed reward-update events

For TORN lock/unlock tracking, using only RewardUpdateSuccessful(account) is not sufficient: the current governance ABI/source also emits RewardUpdateFailed(account, errorData) from the non-reverting reward-sync path, so the lock or unlock can still change lockedBalance while producing the failed event instead of the successful one. If the P2 implementation follows this roadmap literally, votingPowerHistory and accountPower.votingPower will miss those balance changes; the guidance should require watching both reward-update outcomes or otherwise reconciling with vault/governor transfers before reading lockedBalance.

Useful? React with 👍 / 👎.

P2 voting-power roadmap: trigger lockedBalance snapshot on BOTH
RewardUpdateSuccessful and RewardUpdateFailed (updateRewards modifier
emits one or the other via try/catch, but lock/unlock changes balance
either way); reconcile with governor+Vault transfers for pre-v3 history.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 76058864b6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +147 to +150
- On `ProposalCreated`, enrich `target` via the existing **`apps/address-enrichment`** app:
- Etherscan **source-verified?** (unverified ⇒ HIGH risk)
- **address-similarity** vs a known-entity allowlist (governor/vault/token/multisig) — catches `…f27…` vs `…F26…`
- optional: bytecode decompile diff (declared vs actual)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require scanning embedded proposal addresses

For Proposal 67, the spoofed 0x5ef...f27 address was the admin address hidden inside the unverified proposal bytecode, while ProposalCreated.target is only the delegatecalled proposal contract that the indexer stores as targets: [target] in governor.ts. If P1 only enriches that top-level target and compares it to the allowlist, a future implementation can miss the exact vanity-address replacement this section is meant to catch once the proposal contract itself is otherwise verified; make bytecode/source constant extraction part of the required similarity check rather than leaving it optional.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants