bat-cli 0.31.0

Blockchain Auditor Toolkit (BAT)
# Address-typed wiring, and what it would buy — a note, not a plan (2026-10-13)

Written down because it came up while drawing `bat-cli map` and was deliberately NOT implemented.
Nothing here is a commitment; it is the state of the question so the next person does not have to
rediscover it.

## What the map cannot see

`relations()` works from TYPES. A state variable typed as a contract or an interface gives the
scan a name to resolve; one typed `address` gives it nothing. In ascend that is not an edge case,
it is a pattern:

| Contract | Variable | How it is used |
| --- | --- | --- |
| `AscendCtoReceiver` | `registry` | `if (msg.sender != registry) revert Unauthorized();` (:59, :64) |
| `AscendYieldVault` | `depositor` | `if (depositor != address(0) && msg.sender != depositor) revert UnauthorizedDepositor();` (:436) |
| `YieldRouter` | `guardian`, `executor` | `if (msg.sender != guardian) revert Unauthorized();` (:298, :329) |
| `CoreListingVerifier` | `deployer` | `if (t.deployer != deployer) revert WrongDeployer();` (:170) |
| `AscendFeeController` | `launchpad` | `if (hooks == launchpad) return 0;` (:79) — the exemption; any other hook reverts |
| `AscendFeeController` | `feeSink` | only the `recipient` argument of `collectProtocolFees` (:100) |
| `YieldRouter` | `burnSink` | only the token receiver in `_purchase` (:452) |

Two different facts hide in that list:

1. **Authorisation.** The address is compared — against `msg.sender`, or against a field of a key
   — and the call reverts if it does not match. The arrow this implies runs the OTHER WAY from a
   call: `registry` in `AscendCtoReceiver` does not mean "Receiver uses Registry", it means
   "Registry is the only one who may enter Receiver".
2. **Value.** The address is a recipient (`transfer`, `safeTransfer`, `call{value:}`, or a
   `recipient` parameter that ends in one). `feeSink` and `burnSink` are where funds end up.

## Why it is not in the map

The auditor's call, and it is the right one: **the map is a map of relations**. A gate is a
property of a FUNCTION's access control, not a line between two contracts, and routing it through
the map would be a long way round for what is really "this address may change state over there".
What might eventually belong on the map is the VALUE kind — "this contract sends funds to that
one" — and even that is not pressing.

## Where it would belong instead

A separate capability: **access control per function**. Which functions are gated, by what, and
which are not gated at all. Today nothing in bat-cli answers that; the code often makes it
obvious at a glance, which is exactly why it has stayed a secondary task.

If it is ever built, the material is already here:

- The comparison sites above are mechanical to find: an `address` state variable in a `==`/`!=`
  whose failure branch is a `revert`. `storage_writes` and the parser already walk the bodies.
- The static type is `address`, so the TARGET cannot be derived — it has to be declared, the way
  interfaces are today: `bat-cli resolve <Contract>.<var> <Target>` alongside the existing
  `bat-cli resolve <Interface> <Contract>`.
- Many of these addresses are EOAs (`guardian`, `executor`, `deployer`). A capability that
  assumes every gate is a contract will draw contracts that do not exist.

## One more blind spot, same investigation

A call that starts in `lib/` and lands in `src/` is invisible: `relations()` only walks contracts
that are not vendored, so `CLPoolManager.initialize` → (assembly `staticcall` through
`IProtocolFeeController`) → `AscendFeeController.protocolFeeForPool` has no edge, even though
that controller is the gate deciding which pools may exist. `ICLPoolManager` itself is fine — it
is called from `AscendLaunchpad`, `YieldRouter` and `AscendFeeController`, and the map already
draws it as a grey leaf of each.