Proxies and implementations
How Forensia finds the code an upgradeable contract actually runs, and how sure it is.
#Plain languageIn plain terms
Many contracts are built in two parts. The address people use is a thin shell called a proxy. The real logic lives at a second address called the implementation. The shell forwards every call to the logic. The owner can point the shell at new logic later, which is how contracts get upgraded.
This matters in an investigation because the address you searched may tell you almost nothing. What it does today is decided by where it points. Forensia finds that destination and says how it found it.
#How it worksHow it works
There is a standard, ERC-1967, that fixes the storage locations where a proxy keeps its pointers. The lookup reads those locations directly. If they are empty it falls back to what the verification registry reports.
- Read three standard storage slots: implementation, admin and beacon.
- If the implementation slot holds an address, use it.
- Otherwise, if the beacon slot holds an address, ask that beacon contract which implementation it serves.
- Otherwise, if the verification registry marks the contract as a proxy, use the first implementation it lists.
- Fetch the implementation's code and its own verification record.
#Technical detailThe exact rules
| Slot | Storage key |
|---|---|
| Implementation | 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc |
| Admin | 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103 |
| Beacon | 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50 |
A slot value is ignored when it is empty or all zeros. Otherwise the last 20 bytes are taken as the address. The beacon is queried with an eth_call using selector 0x5c60da1b, which is implementation(), with a 6 second timeout.
| How it was resolved | Evidence class | Confidence |
|---|---|---|
| Implementation slot | Lead | High |
| Beacon call | Lead | High |
| Verification registry | Attribution | Medium |
The first two are read from the contract's own storage, so they are strong evidence of current routing. The third is a third party's statement and is graded lower. When both the implementation and beacon slots are filled, the implementation slot wins, as the standard requires, and a warning is added. Warnings are also added when the beacon slot is filled but the beacon call returns no usable address, and when the resolved implementation returns no code.
The result is the finding finding:proxy-resolution and a proxy record holding the implementation address, its code size, its count of selector-like constants, any admin address from the admin slot, any beacon address from the beacon slot, and the evidence each came from.
#LimitsWhat it does not tell you
- Resolution is one level deep. If the implementation is itself a proxy, that second hop is not followed.
- Only the latest state is read. Earlier implementations are not reconstructed from storage.
- Other proxy designs are not recognised from the chain: minimal clones (EIP-1167), the older UUPS slot, diamonds (EIP-2535) and Safe wallets. One of these resolves only if the verification registry marks the contract as a proxy, at the lower grade shown above.
- The admin address is whatever the slot holds. Who controls that address is a separate question.