What this tool does
The Diamond Proxy Checker reads a contract address and confirms whether it’s an EIP-2535 diamond: a proxy that splits its logic across multiple facet contracts instead of pointing at one implementation. It’s built for developers who already suspect they’re looking at a diamond (Beanstalk, Aavegotchi, and a growing list of DeFi and gaming protocols use the pattern) and want the facet count and routing confirmed in one read, not traced by hand through diamondCut events.
Key facts
- Confirms whether a contract is an EIP-2535 diamond, a proxy that routes different function selectors to separate facet contracts instead of one implementation, and reports the resolved facet count.
- Diamonds get upgraded facet-by-facet via
diamondCut, and standard loupe functions (facets,facetAddresses,facetFunctionSelectors) let any caller enumerate exactly what is wired up. - The pattern partly works around Ethereum’s 24KB deployed-bytecode cap (EIP-170): splitting logic across facets lets a protocol ship far more functionality than one implementation contract could hold.
- Covers Ethereum, Base, and BNB Smart Chain; a facet count resolved today can change if facets are cut in or out later.
How to use it
- Paste the contract address into the form above, or leave the prefilled Beanstalk Farms Diamond in place to see a real example immediately.
- Pick the chain the contract is deployed to.
- Run the query and read the status line: it reports “Diamond proxy (EIP-2535)” directly when a match is found, along with a facet count.
When the address is a diamond, the result panel renders every resolved facet as a branch rather than a single chain, so the fan-out is visible at a glance. If the address routes through another proxy pattern before reaching the diamond (uncommon, but possible), that outer hop is shown too. A raw JSON toggle at the bottom shows the full response for debugging.
What is EIP-2535, specifically
Most proxies (EIP-1967, UUPS, Beacon) forward every call to a single implementation address, and upgrading means swapping that one address for another. EIP-2535 takes a different approach: a diamond contract keeps 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 fall out of this design that explain why teams reach for it. First, upgrade granularity: a bug or feature change in one facet can be shipped 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 has two choices, split it across multiple independently-called contracts, or split it across facets behind one diamond address that callers never need to think about. Beanstalk Farms is a well-known production example of the pattern: its Diamond address routes calls across dozens of facets covering its different protocol modules.
Build this yourself
Each of the snippets below makes the same POST call to the evmquery REST API. The example targets Beanstalk Farms’ Diamond on Ethereum.
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": "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" } } }, "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": "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5"}}, }, "include": ["resolution"], }, timeout=10,)data = resp.json()contract = next(c for c in data["contracts"] if c["name"] == "target")route = contract.get("_extension", {}).get("resolution", {}).get("route", [])diamond_index = next((i for i, hop in enumerate(route) if hop["kind"] == "eip2535-diamond"), -1)if diamond_index != -1: facets = route[diamond_index:] print(f"Diamond detected: {len(facets)} facets resolved")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: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" }, }, }, include: ["resolution"], }),});const schema = await resp.json();const contract = schema.contracts.find( (c: { name: string }) => c.name === "target",);const route = contract?._extension?.resolution?.route ?? [];const diamondIndex = route.findIndex( (hop: { kind: string }) => hop.kind === "eip2535-diamond",);if (diamondIndex !== -1) { const facets = route.slice(diamondIndex); console.log(`Diamond detected: ${facets.length} facets resolved`);}The free tier has no monthly cap. Get a free API key to drop these snippets into your project.
When you would use this
- Confirming a diamond before integrating. Some contracts look like ordinary addresses from the outside but route calls across many facets underneath. Confirm the pattern before you assume a single ABI covers everything callable.
- Tracking a diamond’s facet count over time. Diamonds change shape as teams cut in new facets. A quick recheck tells you whether the facet count moved since you last looked.
- Ruling out the diamond pattern. If the address resolves to an empty route or a different proxy kind, you know immediately that facet-based tooling and assumptions don’t apply here.
FAQ
What is a Diamond (EIP-2535) proxy?
A Diamond is a proxy contract that routes different function selectors to different facet contracts, instead of forwarding every call to one implementation. Facets are added, replaced, or removed through the diamondCut function, and two loupe functions (facets and facetAddress) let anyone enumerate what’s wired up. This tool resolves that routing and reports it as a facet count.
How is a diamond different from a single-implementation proxy?
An EIP-1967, UUPS, or Beacon proxy points at exactly one implementation address at a time. A diamond splits its logic across many facet contracts, each responsible for a subset of function selectors, and can upgrade one facet without touching the rest. This tool’s result panel makes the difference visible directly: single-implementation proxies render as a chain, diamonds render as a branching tree of facets.
Why do teams use the diamond pattern instead of a normal proxy?
Two reasons come up most often. First, granular upgrades: a team can replace one facet’s logic without redeploying or migrating state for the rest of the contract. Second, the 24KB contract size limit on Ethereum: splitting logic across facets lets a project ship far more functionality than a single implementation contract could hold, since each facet is deployed and sized independently.
How does this tool derive the facet count?
evmquery resolves the address server-side with the resolution include on POST /query/describe. The response’s resolution.route is an ordered list of hops; when a hop’s kind is eip2535-diamond, every hop after it in the route is a resolved facet. The facet count is simply the number of hops remaining once the diamond hop is found.
Which chains are supported?
Ethereum, Base, and BNB Smart Chain today. More EVM chains are being added on the evmquery backend.
Can I try a real diamond contract right now?
Yes. This page loads with Beanstalk Farms’ Diamond prefilled, a well-known EIP-2535 deployment on Ethereum, so you can see a real facet fan-out without pasting your own address first.
What if I need to check a proxy that might not be a diamond?
Use the general Proxy Contract Detector instead: it covers EIP-1967, UUPS, Beacon, and Diamond patterns in one form, so you can check any address without knowing its pattern up front. If you need the full callable method schema rather than just proxy status, the Contract Inspector resolves the same proxy chain and lists every method.
Limits and accuracy
- The result reflects the contract’s current on-chain state at the time of the read. A diamond’s facets can be cut in or out after the fact, so a facet count resolved today may differ 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 diamond and its facet count do not imply the underlying logic is safe to interact with.
Related
- Proxy Contract Detector: check any proxy pattern, not just diamonds, including EIP-1967, UUPS, and Beacon
- Contract Inspector: need the full method schema instead of just proxy status? This resolves the same proxy chain plus every callable method
- evmquery for developers: the full integration story