Skip to content

Add 3Jane governance context to LLM reports - #343

Merged
spalen0 merged 4 commits into
mainfrom
feat/3jane-governance-context
Aug 25, 2026
Merged

Add 3Jane governance context to LLM reports#343
spalen0 merged 4 commits into
mainfrom
feat/3jane-governance-context

Conversation

@spalen0

@spalen0 spalen0 commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add a 3Jane-specific LLM context adapter for mainnet timelock calls
  • reverse hashed bytes32 arguments to ProtocolConfig keys and Jane / EmergencyController roles, with the value stored on-chain for config keys
  • read RewardsDistributor distribution mode, MINTER_ROLE authority, JANE supply and transferability, claim accounting, and recent epoch emissions
  • fan protocol adapters out from one registry (utils/llm/protocol_context.py) instead of wiring each into the explainer

Motivation

This 3Jane 24h-timelock report hedged four times over facts that are public getters — "the current useMint value and available token balance are not provided", "the execution path cannot be determined", "whether the distributor is adequately funded", "whether claims can expand JANE supply" — and called 5.56M JANE "substantial in absolute terms" with nothing to compare it against.

ProtocolConfig is owned by the 24h timelock and its only setter is setConfig(bytes32,uint256), so every config change currently reaches the LLM as a bare hash — the same failure mode as an unresolved role hash in a grantRole alert.

What the LLM now sees for that alert

RewardsDistributor: 0xaC6985D4dBcd89CCAD71DB9bf0309eaF57F064e8
Distribution mode: useMint = true — claims MINT new JANE. The distributor holds
  MINTER_ROLE on the token, so its own balance (0 JANE) is not the funding source.
Reward token: 0x3333...3404 (JANE, 18 decimals), current totalSupply 38,919,583 JANE
Token transfers globally enabled: false (when false, only TRANSFER_ROLE holders can move the token)
Claim accounting: maxClaimable 84,649,011 JANE, totalClaimed 38,919,583 JANE,
  outstanding claimable 45,729,427 JANE
Current merkleRoot: 0x9a8f7a94...abff2d1
Current epoch: 45
Epoch emissions currently stored on-chain — epoch 42: 5,304,782 JANE,
  epoch 43: 5,369,214 JANE, epoch 44: 5,499,673 JANE, epoch 45: 0 JANE

Two facts the original report could not reach: JANE totalSupply equals totalClaimed exactly, so claims are the only source of supply and epoch 45 would add ~14% to it; and transferable = false, so minted rewards are not yet liquid.

Notes

  • Hashed labels are keyed by keccak256(name) derived at import, so the table cannot drift from the names. Hashes outside the table stay unresolved rather than guessed at, and role hashes get no value line since there is nothing to read.
  • The adapter follows EIP-1967 to the implementation ABI — ProtocolConfig and MorphoCredit are transparent proxies, and without this the config read came back "not readable".
  • Token amounts are truncated to whole tokens, matching the call-flow amount hints from fix(llm): truncate token hints to whole amounts #341.
  • The Merkle tree's recipients remain unresolvable: the root is a commitment and 3Jane publishes no rewards endpoint I could find. The report will still say individual claimants can't be verified — correctly, rather than as one hedge among four.
  • The Protocol Context prompt header was Infinifi-specific ("farm, asset, whitelist") and rendered above 3Jane content; it is now generic. The Infinifi escrow guidance stays in SYSTEM_INSTRUCTIONS.

Testing

  • uv run pytest -q tests/ (844 passed, 4 skipped, 5 subtests)
  • 26 new network-free tests in tests/test_threejane_context.py and tests/test_protocol_context.py
  • uv run ruff check . and uv run ruff format --check .
  • uv run mypy --follow-imports=skip utils/llm/threejane_context.py utils/llm/protocol_context.py
  • live mainnet replay of the linked alert through explain_transaction (LLM call stubbed), plus setConfig on the proxied ProtocolConfig, grantRole on JANE, and the wrong-protocol / wrong-chain / unknown-hash guards

🤖 Generated with Claude Code

spalen0 and others added 4 commits August 25, 2026 22:07
3Jane timelock calls reach the LLM as opaque data: setConfig names its
parameter only by keccak256 hash, and RewardsDistributor allocates JANE
without revealing whether a claim mints new supply or moves a balance. A
recent 24h-timelock report hedged four separate times over facts that are
public getters.

- reverse bytes32 arguments against a checked-in table of ProtocolConfig
  keys and Jane/EmergencyController roles, with the value stored on-chain
  for config keys
- read RewardsDistributor distribution mode, MINTER_ROLE authority, token
  supply and transferability, claim accounting, root, and the emissions of
  the preceding epochs so an allocation is judged against recent ones
- follow EIP-1967 to the implementation ABI, since ProtocolConfig and
  MorphoCredit sit behind transparent proxies
- fan protocol adapters out from one registry instead of wiring each into
  the explainer, and make the Protocol Context prompt header generic

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The three inline ABI literals were 75 of the module's lines and pushed the
resolvers off screen. Move them next to the protocol's existing ABIs and
reuse ProtocolConfig.json rather than restating `config(bytes32)`.

Loading is lazy and cached: this module sits in the explainer's import
chain, so an unreadable file degrades one protocol's context instead of
breaking every AI alert.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One alert probes the same target for both shapes it can take, and each
probe followed EIP-1967 independently — two identical eth_getStorageAt
calls for a setConfig alert. Split the ABI lookup into own-ABI and
implementation-ABI halves and memoize both, so the slot is read only when
the proxy ABI comes up short and only once per address.

A target that is not a proxy still never reads the slot: adapter cost for
a setConfig alert drops from 3 round trips to 2, and the distributor path
is unchanged at 2 batched requests. Context output is byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three consecutive weeks of the identical setEpochEmissions + updateRoot
pair scored LOW, MEDIUM, MEDIUM. Emissions move in a ±3% weekly band, so
each allocation is unremarkable in series and "sizable" in isolation —
the reports had no series to read.

- derive how the proposed allocation compares to the epoch before it, and
  whether the funded epoch is past, current, or future; no extra reads,
  the history was already fetched
- render a capping key beside what it caps, batched into the existing
  config request: USD3_SUPPLY_CAP next to USD3 totalAssets, which is what
  separates a routine ceiling raise from one that unblocks deposits

DEBT_CAP is deliberately not registered — its denomination against the
market's borrow accounting is unconfirmed, and a wrong unit is worse than
no comparison.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@spalen0
spalen0 marked this pull request as ready for review August 25, 2026 21:00
@spalen0
spalen0 merged commit bca5d50 into main Aug 25, 2026
3 checks passed
@spalen0
spalen0 deleted the feat/3jane-governance-context branch August 25, 2026 21:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant