BotRadix proxies and router do not use EIP-1967. The router also does not expose the EIP-2535 loupe. An explorer failing to detect a proxy, or an empty standard slot, does not prove that logic is fixed. Follow each family's own read functions.
Address, state and logic#
The host or proxy retains its address and state. When it delegates a function, code from another address executes against that state. An implementation can change without moving funds or changing the address the user sees.
To reconstruct a route, start from the official addresses, identify the function selector and query the contracts at the same block. Record the block number and hash, addresses, revisions and code hashes: mixing reads before and after an upgrade can produce a route that never existed.
An implementation ABI helps interpret delegated functions, but state reads and user transactions belong at the host address. Calling an implementation directly can revert or read different state. Identity reads explicitly directed to the implementation below are an exception.
Router: facets, dispatch kernel and modules#
The router combines a host table, extensions and a dispatch kernel that can prioritize the module registry. A facet is a piece of logic to which a function is routed.
| Read | What it checks |
|---|---|
routerLifecycle() on the router | Readiness, configuration seal, epoch, manifest and pending proposal |
getFacetManifest() on the router | The 17 manifest facets and their code hashes |
facetForSelector(bytes4) on the router | The host route for that selector; alone it does not reflect a registry override |
dispatchLifecycle() on the router | Address, code hash, revision and proposal of the dispatch kernel |
moduleRegistryInfo() on the router | Lookup mode, epoch, manifest, frozen state, delay and registry size |
moduleRoute(bytes4) on the router | Reported effective target for that selector, identity, revision and route source |
TRADE_FACET(), then dexModule(0..12) on that facet | Internal DEX components delegated to by the trading facet |
With a kernel that enables the registry, a module route can take priority over the host route. In moduleRoute(), source 1 means a fixed or extension route, 2 the registry, and 3 a registry override of a fixed route; 0 means no effective route. A new selector may point to the hub in the host table: that hub reverts unless the required module route exists. A kernel that ignores the registry changes this interpretation, which is why dispatchLifecycle() and lookup mode also matter.
DexExtensionInstalled events help reconstruct extension changes, but a historical event does not prove the current route. Compare it with reads at the chosen block. Checking only the 17 facets omits extensions and module overrides.
CrossMargin: custom slot and modules#
On the CrossMargin proxy, implementation() returns the logic and lifecycle() exposes its seal, proposal, revisions and hashes. Its lifecycle storage root is:
bytes32(uint256(keccak256("botradix.cross.proxy.v1")) - 1)This uses Ethereum Keccak-256, not SHA3-256. It is not the EIP-1967 slot. The first field is the implementation address; it shares a word with the sealing boolean, so do not interpret the entire word as an address. Prefer the read functions.
Next, call implementationModules() on the returned implementation address to identify its eight internal pieces. Account queries still belong at the proxy. Also call moduleRegistryInfo() and moduleRoute(selector) on the proxy to distinguish those pieces from module registry routes. Sealing the proxy and freezing every route or parameter are different questions.
Business proxies: separate control#
The oracle hub (PairForge), Registry, Automation and the Uniswap adapter use a separate control contract. Other business components, such as CrossVerifier, use the same family.
- Read
BUSINESS_CONTROL()on the host. - On the returned control, read
current(),revision()andisSealed()for the logic, its revision and that path's seal. - Read
openPolicyLifecycle()on the host for the corresponding upgrade policy. A policy and business logic are different components. - Where the host exposes the module registry, also read
moduleRegistryInfo()andmoduleRoute(selector)there. The main logic alone does not describe every function.
Reference example#
Documentation snapshot: 11 October 2026. These relationships provide orientation, not a substitute for a current read:
| Starting point | Read | Reference result |
|---|---|---|
Router 0x740E207d90ec9D82fce040F8DA0268dB761F364E | dispatchLifecycle() | BotradixDispatchKernelV2 0x95DC9EFbff26Acb8B9509d68dd73E712c4541093 |
CrossMargin 0x79ED1002F842DE2A9E25d353F2627206483b2b3B | implementation() | BotradixCrossMargin 0xc1030c90C644Df556c5fe311601B55B84387f6d7 |
PairForge 0x55Ec5A8DFEb6ab71BCAE98D833b4E1d96a6c871C | BUSINESS_CONTROL() | Control 0x255DdF4313FC5D547c3925748f30A8008676551f |
| That PairForge control | current() | BotradixPairForgeLogic 0x0744e9a3f4ad391414FA2d1443de2259bB1620e6 |
What verification establishes#
Verifying a host's source does not automatically verify its implementations, modules, policies or controls. A combined ABI also does not prove which route was active at a block. A complete check relates each component's address, bytecode, reproducible source, routes, authorities and seals.
Publication status at the reference date: source will be published jointly on BaseScan near closure. Read or write buttons are not assumed to exist already for every function. The mechanism above can also be inspected with tools holding the correct ABIs, without sending transactions.
See Security and contracts to interpret OPEN, the limits of AvisoPrevio and staged sealing, and Reproducible compilation to relate source and bytecode.