docs(torn): DAO anatomy, tech funnel & remaining-gap roadmap#2004
docs(torn): DAO anatomy, tech funnel & remaining-gap roadmap#2004Zeugh-eth wants to merge 5 commits into
Conversation
…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).
|
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. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
There was a problem hiding this comment.
💡 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".
| **White-label:** already supported — `middleware.ts` resolves hostname→daoId; a custom domain only needs | ||
| `hostnames: [...]` in `torn.ts`. No platform work. |
There was a problem hiding this comment.
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 👍 / 👎.
| ▼ OpenAPI | ||
| [4] anticapture-client (kubb → TanStack Query hooks) → gateful (gateway) |
There was a problem hiding this comment.
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).
|
Thanks @codex — both addressed in
|
|
To use Codex here, create a Codex account and connect to github. |
|
@codex review |
There was a problem hiding this comment.
💡 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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
|
Round 2 addressed in
@codex review |
There was a problem hiding this comment.
💡 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".
| 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** |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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).
|
Round 3 addressed in
@codex review |
🤖↔🤖 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
Status of each Codex comment: addressed and 👍-reacted; superseded comments are on older commits.
|
|
To use Codex here, create a Codex account and connect to github. |
|
Codex Review: Didn't find any major issues. Breezy! Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
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". |
|
@codex review |
There was a problem hiding this comment.
💡 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".
| - Use `RewardUpdateSuccessful(account)` (fires on every lock/unlock) as a "balance-changed" trigger to | ||
| `readContract lockedBalance(account)` and write `votingPowerHistory` + `accountPower.votingPower`. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
💡 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".
| - 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) |
There was a problem hiding this comment.
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 👍 / 👎.
What
Adds
apps/indexer/src/indexer/torn/RESEARCH.md— a companion to the existingINTEGRATION.mdin the same directory. Independent research on the Tornado Cash governance model with a concrete roadmap for the remaining gaps.Targets
feat/tornado-cash-integrationso the documentation lands together with #2002.Contents
tornadocash/tornado-governance, implGovernanceProposalStateUpgrade/5.proposal-state-patch).INTEGRATION.md) and a prioritized next-steps roadmap:alreadySupportCalldataReview() = false.RewardUpdateSuccessful→readContract lockedBalance.Related
calculateQuorum, vote-mutability / threshold / voting-delay copy).