Skip to content

feat(torn): proposal-target verification — flag malicious/look-alike execution targets #2005

Description

@Zeugh-eth

Feature Request

User story

As an Anticapture user (delegate / security researcher) watching Tornado Cash, I want each on-chain proposal's execution target automatically checked for source-verification and look-alike-address risk, so a malicious proposal like Proposal 67 (2026-06-25) is surfaced in the UI/feed before it can pass — instead of relying on manual decompilation.

Context

  • Proposal 67 pointed execute()'s delegatecall at an unverified look-alike contract0x5efda50f22d34f27… vs the real governor 0x5efda50f22d34F26…, differing only from the 16th hex char — to seize governance over a ~$23M treasury. It was caught manually by L2BEAT / P. Caversaccio via missing code verification + decompilation.
  • Today (PR feat: integrate tornado cash #2002) we index the target but don't analyze it: TORNClient.alreadySupportCalldataReview() === false. Background + roadmap in apps/indexer/src/indexer/torn/RESEARCH.md (PR docs(torn): DAO anatomy, tech funnel & remaining-gap roadmap #2004, §5 P1, §7).
  • This is P1 of the documented next-steps roadmap and the single most attack-relevant feature for TORN.

Describe the feature request

Compute a proposal target risk signal from the already-indexed target and surface it on the proposal detail + activity feed.

Risk inputs:

  1. Source verified? Etherscan getsourcecode → empty SourceCode/ABI ⇒ unverified ⇒ HIGH.
  2. Look-alike address? similarity (prefix/suffix + Levenshtein) vs a known-entity allowlist (governor / vault / token / multisig) ⇒ catches …f27… vs …F26….
  3. (optional, later) behavior diff — decompile executeProposal() / scan for storage-slot writes to governance-critical slots.

Describe solution — adaptation across the 5 components (per the dao-integration skill)

1. address-enrichment (apps/address-enrichment) — new home for the analysis (indexer stays on-chain-only).

  • Job that, per TORN proposal target, calls Etherscan verification + runs the similarity check against the known-entity list, and writes a risk record. Reuses the app's existing Etherscan/RPC plumbing.

2. DB — add a side table proposal_target_risk (proposalId, daoId, target, verified bool, lookAlikeOf address|null, riskLevel, details jsonb) rather than migrating the shared proposals_onchain schema (keeps other DAOs untouched; consistent with the "no-migration" approach in RESEARCH.md §4).

3. API (apps/api) — layered controller→service→repository→mapper:

  • GET /proposals/{id}/target-risk (or fold into the proposal-detail mapper).
  • In clients/torn, expose target-risk via the client and flip alreadySupportCalldataReview() once the signal is populated.

4. gateway + client — regenerate packages/anticapture-client from the updated OpenAPI; apps/gateful passes through.

5. dashboard (apps/dashboard) — render a HIGH-risk banner on features/governance/.../ProposalSection.tsx; add a feedEvent flag for the activity feed; reflect support in shared/dao-config/torn.ts.

Enum sync (the 5th component the skill calls out): add a PROPOSAL_RISK feed/metric type consistently across indexer + api + dashboard enums.

Acceptance / validation

  • Proposal 67 (and the May 2023 attack proposal) render with a HIGH target-risk badge (unverified + look-alike of 0x5efda5…A1Ce).
  • Legitimate executed proposals with verified targets render LOW/none.
  • No schema migration affecting other DAOs.

Want a second opinion on the approach before implementation — see the @codex ask in the comments. Tagging @pikonha (owner of #2002) to validate this is the right next step / right home (address-enrichment vs indexer; side-table vs schema extension).

Metadata

Metadata

Assignees

Labels

enhancementImproving an already existing feature

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions