Is this address forwarding my calls somewhere else?
The Proxy Contract Detector reads any contract address and tells you whether it’s a proxy, which pattern it uses, and what it resolves to. It is built for developers and integrators who need a fast answer to one question before they interact with an address: is this contract forwarding my calls somewhere else, and if so, where?
Key facts
- Detects and classifies proxy patterns server-side via the
resolutioninclude onPOST /query/describe, covering EIP-1967 transparent, EIP-1967 beacon, EIP-1822 (UUPS), EIP-1167 minimal, legacy OpenZeppelin (zeppelinos), Gnosis Safe, EIP-2535 diamond, and EIP-7702 delegation. - An empty
resolution.routemeans the address is not a proxy at all: calls execute directly on it. - EIP-2535 diamond contracts render as a branching tree of resolved facets with a facet count, distinct from a single-hop proxy chain.
- Covers Ethereum, Base, BNB Smart Chain, and Polygon, with more EVM chains being added.
How to use it
- Paste the contract address into the form above.
- Pick the chain the contract is deployed to.
- Run the query and read the status and resolved chain in the result panel.
The status line gives you the verdict directly: proxy detected, diamond proxy, or not a proxy. Below it, the chain panel shows the address you pasted, followed by each resolved hop and the pattern it matched, ending at the implementation the calls actually execute on. Diamond contracts render as a branching tree of facets instead of a single chain, with a facet count alongside. If the address is not a proxy, the panel says so directly and calls execute on the address you pasted. A raw JSON toggle at the bottom shows the full response for debugging.
Classifying a proxy from its bytecode conventions
Most non-trivial contracts on Ethereum are proxies: a thin contract that forwards every call to an implementation address it can upgrade independently. Detecting this from bytecode alone means recognizing a handful of storage-slot and delegatecall conventions, not reading arbitrary logic. evmquery does this resolution server-side with the resolution include on POST /query/describe.
POST /query/describe{ "chain": "evm_ethereum", "schema": { "contracts": { "target": { "address": "0x..." } } }, "include": ["resolution"]}- The response is a schema object listing every contract you asked about, each annotated with a
resolutionextension. - A contract’s
resolution.routeis an ordered list of hops. Each hop has akind(eip1967andeip1967-beaconfor the two most common transparent/beacon proxy standards, pluseip1822(UUPS),eip1167(minimal proxies),zeppelinos(OpenZeppelin’s legacy proxy),gnosis-safe,eip2535-diamond(multi-facet diamonds),eip7702-delegation, and genericgetter/beacon-implementationhops) and atoaddress, the contract the call is forwarded to at that hop. An empty route means the address is not a proxy: calls execute directly on it. - Diamond contracts (EIP-2535) route different function selectors to different facet contracts. The tool detects the diamond hop and renders every resolved facet underneath it as a branch, not a chain, so you can see the fan-out at a glance.
- EIP-7702 delegation is resolved as its own hop kind. It is a distinct mechanism from a deployed proxy contract: an EOA temporarily executing delegated code via a signed authorization, not a storage-slot-routed forward.
Diamond proxies (EIP-2535)
Diamond contracts don’t have a single implementation to swap out. Instead they keep a mapping from function selector to facet address, and different selectors can point at entirely different facet contracts. Three pieces make this work:
diamondCut: the function used to add, replace, or remove facets and the selectors they own. This is how a diamond gets upgraded, one facet at a time.- Loupe functions: a small standard interface (
facets,facetFunctionSelectors,facetAddresses,facetAddress) that lets any caller, including this tool, enumerate exactly which facets are wired up and what each one handles. - Shared storage: facets typically read and write a common storage layout on the diamond itself, so splitting logic across contracts doesn’t mean splitting state.
Two properties explain why teams reach for the pattern. First, upgrade granularity: a bug or feature change in one facet can ship by cutting in a replacement for just that facet, without touching or redeploying the rest of the system. Second, contract size: Ethereum caps deployed contract bytecode at 24KB (EIP-170). A protocol with more logic than that limit allows can split it across facets behind one diamond address that callers never need to think about. Beanstalk Farms is a well-known production example: its Diamond address routes calls across dozens of facets covering its different protocol modules, and a facet count resolved today can change if facets are cut in or out later.
Beacon proxies
Most proxy standards store an implementation address directly in the proxy’s own storage. A Beacon proxy does something different: its EIP-1967 beacon slot holds the address of a separate beacon contract, and the proxy asks that beacon for the implementation address on every call rather than keeping its own copy. The beacon contract itself is usually a small, purpose-built piece of code exposing an implementation() getter, nothing more.
Resolving a beacon proxy takes two hops: the first, kind eip1967-beacon, points at the beacon contract, not the implementation. The second, kind beacon-implementation, points at the address whose code actually runs when you call the original proxy.
This indirection exists for fan-out. A protocol that deploys many near-identical contracts, one per user, one per NFT collection, one per avatar, can point every instance at the same beacon. Upgrading the beacon’s stored implementation instantly changes the logic every proxy runs on its next call, without touching a single byte of any individual proxy’s storage. A beacon can be referenced by anywhere from one proxy to tens of thousands, which makes it a single point of upgrade control for a whole fleet of contracts, worth knowing before you trust any one instance in isolation. The Sandbox uses exactly this pattern for its avatar contracts: each user’s avatar is a thin proxy pointed at a shared beacon, so The Sandbox can ship a logic upgrade once and have it apply across every avatar in circulation. Beacon fan-out shows up just as often on the token side: many large ERC-721 and ERC-1155 collections deploy behind a shared beacon so the team can patch minting or metadata logic across every token without redeploying, which the ERC-721 Inspector and ERC-1155 Inspector both resolve transparently when you read a specific token.
Lido’s stETH is a useful example on the token-standard side of the same coin: it’s an ERC-20 contract behind an upgradeable proxy, so the balance and supply figures the ERC-20 Token Inspector reads for it come from whatever implementation this tool resolves the proxy to at the time of the read.
Detect a proxy from your own code
Each of the snippets below makes the same POST call to the evmquery REST API. The example targets USDC’s proxy contract on Ethereum, a legacy OpenZeppelin (zeppelinos) proxy, not an EIP-1967 one.
REST (curl)
curl -X POST https://api.evmquery.com/api/v1/query/describe \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "chain": "evm_ethereum", "schema": { "contracts": { "target": { "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" } } }, "include": ["resolution"] }'Python
import requests
resp = requests.post( "https://api.evmquery.com/api/v1/query/describe", headers={"x-api-key": "YOUR_API_KEY"}, json={ "chain": "evm_ethereum", "schema": { "contracts": {"target": {"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"}}, }, "include": ["resolution"], }, timeout=10,)print(resp.json())TypeScript
const resp = await fetch("https://api.evmquery.com/api/v1/query/describe", { method: "POST", headers: { "x-api-key": process.env.EVMQUERY_API_KEY!, "Content-Type": "application/json", }, body: JSON.stringify({ chain: "evm_ethereum", schema: { contracts: { target: { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" } }, }, include: ["resolution"], }),});const schema = await resp.json();The free tier has no monthly cap. Get a free API key to drop these snippets into your project.
Where a fast proxy verdict actually helps
- Before interacting with an unfamiliar address. Confirm whether a contract is forwarding calls elsewhere before you trust its surface-level behavior.
- Sorting out proxy patterns. Tell Transparent, UUPS, Beacon, and Diamond proxies apart at a glance instead of tracing storage slots by hand.
- Checking a diamond’s facet spread. EIP-2535 diamonds split logic across many facets. The branching view shows how many are wired up without listing every method.
- Ruling proxies out. A clean “not a proxy” answer is a common, useful result on its own: it confirms calls execute directly on the address, no further resolution needed.
- Decoding a call against the right implementation. Once you know where a proxy’s calls actually land, the Calldata Decoder runs the same resolution automatically and decodes a specific transaction’s calldata against that implementation’s ABI, not the proxy’s own near-empty one.
FAQ
How can I tell if a contract is a proxy?
Reading the bytecode by hand rarely tells you: most proxies forward every call to an implementation address via a small set of well-known storage-slot or delegatecall conventions. This tool resolves those conventions server-side and reports the result directly, without asking you to trace opcodes or inspect storage slots yourself.
What is EIP-1967?
EIP-1967 is the most common proxy standard: it defines fixed storage slots for the implementation address (and, for beacon proxies, a beacon address), so tooling can locate them without relying on contract-specific getter functions. Transparent and beacon proxies both use this standard.
What’s the difference between Transparent, UUPS, and Beacon proxies?
A Transparent (EIP-1967) proxy stores the implementation address directly and includes upgrade logic in the proxy itself. A UUPS (EIP-1822) proxy also stores the implementation directly but keeps upgrade logic in the implementation contract instead, making the proxy thinner. A Beacon proxy stores the address of a separate beacon contract, which itself points to the implementation, letting many proxies share one upgrade point. This tool resolves all three and shows which one matched.
Does this detect Diamond (EIP-2535) proxies and their facets?
Yes. Diamond contracts route different function selectors to different facet contracts instead of a single implementation. When a diamond is detected, the tool renders every resolved facet as a branch rather than a single chain, with a facet count alongside it.
Is ‘proxy’ the same thing as ‘upgradeable’?
Not quite. A proxy is the mechanism, a thin contract that forwards calls to another address, and it’s what this tool detects. ‘Upgradeable’ describes whether that forwarding target can change: most proxies are upgradeable by design, but a proxy can also point at a fixed, non-upgradeable implementation. This tool reports the proxy pattern and current target; it does not determine whether upgrade rights are still active or who controls them.
Is EIP-7702 delegation the same as a proxy?
No. EIP-7702 lets an externally owned account (EOA) temporarily execute code from a contract address via a signed authorization, without deploying a proxy contract at all. It’s a distinct, Pectra-era mechanism. This tool flags EIP-7702 delegation as its own resolved pattern, separate from deployed proxy contracts like EIP-1967 or UUPS.
Which chains are supported?
Ethereum, Base, BNB Smart Chain, and Polygon today. More EVM chains are being added on the evmquery backend.
Can I try example proxy contracts?
Yes. The address field includes a picker of real, well-known contracts (Aave V3, Lido, Beanstalk’s Diamond, and more), mostly proxies covering different patterns plus one non-proxy for contrast, so you can see the tool working immediately, without needing your own contract address.
Limits and accuracy
- Detection covers the documented patterns: EIP-1967 transparent and beacon, EIP-1822 UUPS, EIP-1167 minimal proxies, the legacy OpenZeppelin layout, Safe, EIP-2535 diamonds, and EIP-7702 delegation. A contract that forwards calls through a bespoke mechanism outside those conventions returns an empty route, so an empty result means no known pattern matched rather than a guarantee that nothing is being forwarded.
- Facet enumeration on a diamond depends on the loupe functions the standard asks for. A diamond that omits them can be identified as a diamond without having its facets listed.
- Proxies can be upgraded after the read, so a route resolved today may point somewhere else tomorrow.
- The demo is rate limited per browser. If you hit the limit, grab a free API key and the limit goes away.
This is pattern and routing detection, not storage-slot decoding or a security audit. A resolved proxy pattern does not imply the contract, or its implementation, is safe to interact with.
Related
- Contract Inspector: the same resolved proxy chain plus the full method schema, for when you need to know what’s callable, not just what’s a proxy
- Gnosis Safe Inspector: inspect any Gnosis Safe multisig’s owners, threshold, and version, resolved through its proxy automatically
- Calldata Decoder: decode a call against the implementation this tool resolves
- ERC-20 Token Inspector: read a proxied token’s name, symbol, decimals, and supply
- ERC-721 Inspector and ERC-1155 Inspector: read a proxied NFT or multi-token collection
- evmquery for developers: the full integration story