Skip to content

feat: add zksync EraVM source verification and Abstract chain#2788

Open
coffeexcoin wants to merge 26 commits into
argotorg:stagingfrom
coffeexcoin:feat/zksync
Open

feat: add zksync EraVM source verification and Abstract chain#2788
coffeexcoin wants to merge 26 commits into
argotorg:stagingfrom
coffeexcoin:feat/zksync

Conversation

@coffeexcoin

Copy link
Copy Markdown

Summary

Adds ZKsync EraVM/zksolc source verification support to Sourcify, extending the standard JSON verification API surface.

Zksolc verification is selected by passing zksolcVersion to the normal /v2/verify/{chainId}/{address} JSON-input flow The underlying compilerVersion remains the solc or era-solc version requested by the user, and Sourcify records the final era-solc/solc version used for the verified compilation.

Changes by module

packages/compilers

  • Adds a local zksolc compiler adapter exported from @ethereum-sourcify/compilers.
  • Downloads and caches zksolc binaries from the current Matter Labs release repository, with legacy pre-1.5 binary support from zksolc-bin.
  • Downloads and caches era-solc binaries from the Matter Labs era-solidity releases.
  • Invokes zksolc through standard JSON using --standard-json and --solc.
  • Handles pre-1.5 zksolc CLI differences/nuances:
    • maps settings.enableEraVMExtensions / settings.isSystem to --system-mode
    • maps settings.forceEVMLA / settings.forceEvmla to --force-evmla
    • avoids requesting unsupported evm output for legacy zksolc versions
  • Supports exact upstream Solidity compiler versions like v0.8.26+commit.8a97fa7a by resolving Sourcify's normal native solc binaries and passing them to zksolc.
  • Adds zksolc compiler tests with broader parity coverage against solc compiler, including version normalization, binary URL patterns, legacy repositories, pre/post-1.5 argument differences, and upstream solc selection.

packages/lib-sourcify

  • Adds ZkSolcCompilation for EraVM Solidity compilations.
  • Adds IZkSolcCompiler and includes zksolc in the shared compilation type surface.
  • Keeps the language as Solidity but marks the compilation target VM as eravm.
  • Normalizes and expands compiler-version candidates for zksolc:
    • exact era-solc versions like 0.8.26-1.0.1
    • solc release versions like 0.8.26
    • commit-bearing solc versions like v0.8.26+commit.8a97fa7a
  • For commit-bearing solc versions, tries the exact upstream solc first, then falls back through compatible era-solc editions.
  • Enforces known valid era-solc edition combinations:
    • supports editions 1.0.2, 1.0.1, and 1.0.0
    • excludes 1.0.2 for zksolc versions before 1.5
    • excludes unavailable Solidity/era-solc combinations
  • Retries zksolc verification inside the normal verification flow when a candidate compiles but does not match bytecode.
  • Adjusts output selection so required zksolc outputs are requested without dropping user-provided output selections.
  • Treats zksolc byte-for-byte matches as perfect matches even though EraVM auxdata does not follow normal EVM CBOR metadata assumptions.
  • Disables strict CBOR validation for zksolc bytecode matching, while keeping existing Solidity/Vyper/Fe behavior intact.
  • Exports zksolc compilation metadata in VerificationExport:
    • compiler: "zksolc"
    • compilerVersion: zksolc version
    • zksolc.solcCompilerVersion: final underlying solc/era-solc version used
  • Adds a zksolc compilation test matrix covering:
    • zksolc 1.5.x with newer solc/era-solc behavior
    • pre-1.5 zksolc combinations such as 1.4.1 + 0.8.4-1.0.1
    • older zksolc combinations such as 1.3.17 + 0.7.6-1.0.1
    • invalid era-solc/zksolc combinations
    • compile retry and match retry behavior
    • metadata object parsing
    • perfect vs partial match behavior

services/server

  • Add zksolc verification on the existing JSON-input verification endpoint.
  • Adds optional zksolcVersion to the request body.
  • Adds ZkSolcJsonInput as a Solidity standard JSON superset for zksolc-specific settings.
  • Detects zksolc verification from zksolcVersion and zksolc-specific settings.
  • Rejects zksolc requests unless:
    • the input language is Solidity
    • zksolcVersion is present when zksolc-specific settings are used
    • the target chain has zksolc support enabled
  • Adds ZkSolcLocal and passes zksolc/era-solc/solc repository paths through CLI and worker initialization.
  • Adds config entries for zksolcRepo and eraSolcRepo.
  • Updates API docs to describe zksolc verification via Solidity standard JSON and to show an EraVM example request.
  • Updates persisted verification output mapping so zksolc verified contracts store:
    • compiler = "zksolc"
    • version = <zksolcVersion>
    • additional_input.era_solc_version = <final underlying solc/era-solc version>
  • Adds tests for service validation, worker threading, API request behavior, and database column mapping.

Chain configuration

  • Adds chain-level zksolc capability metadata:
    • zksolc.supported: true | false
  • Threads this field through SourcifyChain and generated chain config loading.
  • Enables Abstract Mainnet in the default local chain config:
    • chain id 2741
    • RPC https://api.mainnet.abs.xyz
    • zksolc.supported: true
  • Non-zksolc chains reject zksolc verification attempts before worker compilation.

Database

  • Adds a Sourcify-owned migration allowing additional_input.era_solc_version on compiled_contracts.
  • Updates the Sourcify database schema snapshot accordingly.
  • Does not update the upstream services/database/database-specs submodule.

Verification behavior

For zksolc requests, supports both explicit era-solc compiler versions and normal Solidity compiler versions:

  • If compilerVersion is an era-solc version, Sourcify uses that exact era-solc binary.
  • If compilerVersion is a commit-bearing Solidity release, for example v0.8.26+commit.8a97fa7a, Sourcify first tries the exact upstream solc binary through zksolc.
  • If the exact upstream solc candidate fails to compile or fails bytecode matching, Sourcify falls back through compatible era-solc candidates until one matches or all candidates are exhausted.
  • If compilerVersion is a plain Solidity release, Sourcify expands it directly to compatible era-solc candidates.

This is intended to support explorer-style submissions where users only know the Solidity compiler version while still allowing exact era-solc requests when that is known; this supports existing tooling and legacy verifications/compile artifacts that may not bear the exact solc fork edition (v1.0.x)

Testing

Automated coverage added or expanded:

  • packages/compilers/test/zksolcCompiler.spec.ts
  • packages/lib-sourcify/test/Compilation/ZkSolcCompilation.spec.ts
  • packages/lib-sourcify/test/SourcifyChain.spec.ts
  • services/server/test/integration/apiv2/verification/verify.json.spec.ts
  • services/server/test/unit/VerificationService.spec.ts
  • services/server/test/unit/utils/database-util.spec.ts
  • services/server/test/unit/verificationWorker.spec.ts

Manual verification performed against Abstract Mainnet contracts from abscan/Etherscan-compatible source metadata, including exact upstream solc fallback behavior for commit-bearing compiler versions.

Candidate contracts tested include:
https://abscan.org/address/0xbc176ac2373614f9858a118917d83b139bcb3f8c#code - zksolc 1.5.7, solc v0.8.26+commit.8a97fa7a (resolves to v0.8.26-1.0.1)

https://abscan.org/address/0x4f7589c619d59443db52489dd375de63e03e671d#code - zksolc v1.3.19, solc v0.6.12+commit.27d51765 (resolves to direct v0.6.12+commit.27d51765)

https://abscan.org/address/0x0929d81a73a83b73e5de2ba63a15ce2a18addbe2#code - zksolc v1.5.15, solc v0.8.26+commit.8a97fa7a (resolves to v0.8.26-1.0.2)

@coffeexcoin coffeexcoin marked this pull request as ready for review May 13, 2026 13:09
@coffeexcoin

Copy link
Copy Markdown
Author

Happy to break this up into smaller PRs by area (compiler then lib then api etc) if that is easier for review/merging

@kuzdogan kuzdogan self-assigned this May 19, 2026
kuzdogan and others added 2 commits May 19, 2026 19:16
The function always returns false for `vm-1.5.0-a167aa3` regardless of
the `target` argument. That only happens to produce correct behavior
because every caller compares against 1.5.0; for any other target
(e.g. 1.0.0), the helper would mis-classify that pre-release as
below the threshold.

Hardcoding 1.5.0 in the name and the body makes the contract honest:
this is specifically an "is zksolc ≥ 1.5?" check, and the vm- guard
is the correct answer to that specific question.

Updates the inline explanatory comment to match.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Explain that the -gnu suffix on the Windows platform string is the
upstream MinGW filename (not a libc choice), and that the Linux
candidate list carries both glibc and musl filenames for fallback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
kuzdogan and others added 3 commits May 20, 2026 09:45
Document the zksolc / era-solc / upstream-solc compiler model: their
naming and roles, the zksolc 1.5.0 CLI/output-selection split, the
modern vs legacy release repos, and the Linux gnu/musl libc handling.
Add a pointer comment at the top of zksolcCompiler.ts.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Covers logic that previously ran only behind network-dependent
integration tests or not at all: the pre/post-1.5 standard-JSON CLI
argument mapping, the isZkSolcVersionAtLeastV15 edge cases (including
the unparseable-version fail-open), the primary/legacy download
fallback in getZkSolcExecutable, and the era-solc vs upstream-solc
routing in getZkSolcBaseSolcExecutable.

Network seams (fetchWithBackoff, getSolcExecutable) are stubbed via
module-namespace reassignment so the tests stay offline and fast.
getZkSolcStandardJsonArgs and getZkSolcBaseSolcExecutable are exported
to make them testable, consistent with the file's existing pattern of
exporting internal helpers.

zksolcCompiler.ts coverage: lines 76% -> 87%, functions 90% -> 100%.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a ZKSOLC.md section describing how Sourcify spawns zksolc and how
zksolc in turn spawns a solc backend via --solc, plus the zkSync
compiler-toolchain diagram. Because zksolc spawns the backend as a
native child process, the Emscripten soljson build can never be used
for EraVM verification.

That made solJsonRepoPath dead weight: it was threaded through
useZkSolcCompiler, getZkSolcBaseSolcExecutable and
getUpstreamSolcExecutable but never used -- yet still load-bearing as a
guard condition, so omitting it silently disabled upstream-solc
resolution. Remove it from those functions, from ZkSolcLocal, and from
both ZkSolcLocal call sites; the guard now checks only solcRepoPath.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
normalizedVersion: string,
): string[] {
const primary = getZkSolcFileName(platform, normalizedVersion);
if (platform.endsWith('-gnu')) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (platform.endsWith('-gnu')) {
if (platform.startsWith('linux-')) {

Nitpick. This looks like the correct check as windows does not have upstream -musl binary but the windows platform is windows-amd64-gnu so the windows case would also fall into the prev. statement.

*/
export class ZkSolcCompilation extends AbstractCompilation {
public language: CompilationLanguage = 'Solidity';
public readonly targetVM = 'eravm';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used anywhere?

readonly auxdataStyle: AuxdataStyle.ZKSYNC = AuxdataStyle.ZKSYNC;

public readonly zksolcVersion: string;
public readonly requestedSolcCompilerVersion: string;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also is this used anywhere?

@kuzdogan kuzdogan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've gathered some notes and questions:

Notes for the next reviewer

1. The new runCompiler wrapper — design sign-off

This PR adds a protected abstract runCompiler() to AbstractCompilation, and
each compilation subclass (Solidity, Vyper, Fe, PreRun, ZkSolc) now
supplies its own implementation. It replaces the single hardcoded
this.compiler.compile(version, jsonInput, forceEmscripten) call in the base
class, which no longer fits zksolc's two-version (zksolcVersion +
solcVersion) signature.

Are we OK with this as the long-term shape? It's a clean template-method split
and it also lets us drop the old as any cast on jsonInput. The tradeoff is
that every future compilation type must now implement runCompiler. No
objection from me — flagging it for an explicit decision.

Changes I've made

1. Removed the unused solJsonRepoPath parameter

I dropped solJsonRepoPath from the zksolc compiler chain — useZkSolcCompiler,
getZkSolcBaseSolcExecutable, getUpstreamSolcExecutable, and ZkSolcLocal
plus its two call sites.

Reason: it was misleading. zksolc resolves its solc backend as a native
binary
that it spawns via --solc — the Emscripten soljson JS build can
never be used here. The parameter was never read; worse, it was load-bearing as
a guard condition (&& solJsonRepoPath), so omitting it silently disabled
upstream-solc resolution. The guard now checks only solcRepoPath.

2. Added zksolc compiler unit tests

I added deterministic, offline unit tests to zksolcCompiler.spec.ts to raise
coverage of the new adapter: pre/post-1.5 CLI-arg mapping, isZkSolcVersionAtLeastV15
edge cases, download fallback, and era-solc vs. upstream-solc routing. Coverage
of zksolcCompiler.ts went from ~76%/60%/90% to ~87%/70%/100%
(lines/branches/functions).

Todos

1. Set the zksolc field in sourcifyeth/sourcify-chains

This PR adds a zksolc?: { supported: boolean } field to SourcifyChain and
SourcifyChainExtension. The per-chain extension data lives in the external
sourcifyeth/sourcify-chains repo — so this field still needs to be populated
there for the chains it applies to (the zkSync EraVM / zk-stack chains, e.g.
Abstract). Without it, those chains won't be flagged for zksolc verification.

TODO: open a follow-up in sourcifyeth/sourcify-chains to add zksolc to the
applicable chain entries.

2. Check the Etherscan import for EraVM contracts

Verify the "import from Etherscan" verification path works seamlessly for
zksync / EraVM contracts — i.e. that the Etherscan-API response for a
zksolc-compiled contract is parsed correctly (zksolc version, era-solc version,
settings) and the import produces a valid verification.

Questions to @coffeexcoin

1. era-solc forks vs. upstream solc — is era-solc actually required?

I don't fully follow when zksolc uses the era-solc forks (e.g. 0.8.30-1.0.2)
versus plain upstream solc binaries. From getZkSolcCompilerVersionCandidates,
the candidate list is built as:

  • submitted version is already era-solc (0.8.26-1.0.1) → that exact edition only
  • submitted version is commit-bearing (v0.8.26+commit.…) → upstream solc
    first, era-solc editions as fallback
  • submitted version is a plain release (0.8.26) → era-solc editions only

Since upstream solc works as a zksolc backend and is even tried first for
commit-bearing versions: what is the actual purpose of era-solc vs. just
using the native upstream solc binary?
What does the Matter Labs fork change
that plain solc can't provide?

2. We skip CBOR decoding for the EraVM trailer — but it looks CBOR-encoded

The PR treats the EraVM bytecode trailer as a non-CBOR, bare 32-byte hash:

  • bytecode-utils splitAuxdata returns [bytecode] for AuxdataStyle.ZKSYNC
    — i.e. "no auxdata to split out";
  • Verification passes validateCbor: false to extractAuxdataTransformation;
  • ZkSolcCompilation.generateEraVmBytecodeHashPosition marks only the final
    32 bytes as the auxdata region.

But the on-chain bytecode of a real Abstract contract says otherwise. The
deployed bytecode of
https://abscan.org/address/0x9cc639c9855cf9e959c47a7bdf1c3c14468a270f ends with:

a2 64 "ipfs" 5822 1220<32-byte multihash>
   64 "solc" 7824 "zksolc:1.5.15;solc:0.8.24;llvm:1.0.1"
0055

That is a standard Solidity-style CBOR auxdata map followed by a 2-byte
length (0x0055 = 85 → ~87-byte trailer) — exactly the EVM solc layout, just
with a combined zksolc;solc;llvm version string. It is not a bare 32-byte
hash.

Clarification needed:

  • Where is the EraVM "bytecode hash" appended, and where is it used? Is it
    distinct from the ipfs hash inside the CBOR map above?
  • Which zksolc versions emit a CBOR auxdata vs. a bare bytecode hash, and what
    does the metadata.bytecodeHash / hashType setting control across versions?

The CBOR above can be decoded with the Sourcify playground:
https://playground.sourcify.dev/?bytecode=0xa2646970667358221220c8a1d50ed58fe61338ad26340368b3b993ac100ede6d05bd18ade2d125d114bf64736f6c6378247a6b736f6c633a312e352e31353b736f6c633a302e382e32343b6c6c766d3a312e302e310055

3. Runtime vs. creation bytecode for EraVM — need more context

ZkSolcCompilation assigns creationBytecode and runtimeBytecode to the
same value (evm.bytecode.object), and generateCborAuxdataPositions
always sets creationBytecodeCborAuxdata = {} while runtime gets the hash
position. I'd like more context on the intent here.

The generic Verification flow still runs the creation path: when
creatorTxHash is set it fetches an "onchain creation bytecode" and compares it
to compilation.creationBytecode. But on zkSync that doesn't line up with the
EVM model. Example — this Abstract contract:
https://abscan.org/address/0x9cc639c9855cf9e959c47a7bdf1c3c14468a270f#code

Its "Contract Creation Code" is not EVM init code. It is calldata to the
ContractDeployer system contract: a create selector (9c4d535b), a salt,
the EraVM versioned bytecode hash (0100…), then ABI-encoded constructor args
(the trailing zero-padded address). The contract bytecode itself is not in the
tx — it is referenced by hash and shipped in the tx's factory_deps. That's
why the explorer's "creation code" looks like a block of ABI-encoded words.

Questions:

  • Is there a concept of creation-bytecode matching for EraVM at all? If so,
    should we take the runtime code as the creation code (as the PR does), or do
    we need the ContractDeployer calldata, as in the abscan example above?
  • Depending on that answer, what should creationBytecodeCborAuxdata be?

4. What artifacts should an EraVM verification store?

zksolc seems to emit only abi, metadata, and evm.bytecode.object, so most
compiled_contracts artifact fields end up null/{} (no deployedBytecode,
no source maps; creation cborAuxdata = {}, runtime = the 32-byte position;
immutableReferences = {}).

Is that the full set? Does zksolc ever emit linkReferences, immutable
references, or anything else? What are the complete outputs we actually get from
a zksolc compilation? We should have test cases that both document and assert
this expected artifact set — the way #2692 did for Fe.

Posted with Claude Code

@kuzdogan

Copy link
Copy Markdown
Member

Another point we should consider is if and how we'll handle VerA for this case.

@coffeexcoin

Copy link
Copy Markdown
Author

1. era-solc forks vs. upstream solc — is era-solc actually required?

For current zksolc versions, upstream solc is not a valid backend anymore. Since zksolc 1.5.8, using upstream solc is explicitly prohibited by zksolc

The reason upstream solc still appears in the candidate logic is historical/API compatibility. Older zksolc versions did allow upstream solc, and explorer/Etherscan-style APIs may submit the upstream long version, e.g. v0.8.26+commit..., even for ZKsync builds. Hardhat’s Etherscan verifier does this: if build info says zkVM-0.8.26-1.0.2, it maps that back to the upstream v0.8.26+commit... for the Etherscan-compatible compilerversion field.

So +commit does not necessarily mean the deployed contract was built with vanilla solc. For modern zksolc it is often just the upstream Solidity identity exposed by the verification API. We use it to recover the base solc version, then fall back to the matching era-solc editions. For old zksolc versions, trying upstream first can still be correct. For current zksolc >=1.5.8, the upstream candidate should fail and the era-solc candidate is the one expected to match.

@coffeexcoin

Copy link
Copy Markdown
Author
  1. We skip CBOR decoding for the EraVM trailer — but it looks CBOR-encoded

Good catch. EraVM runtime bytecode can have two different metadata trailer shapes:

Updated with 1ad9de5

  1. Modern zksolc output, e.g. zksolc 1.5.15, can end with a normal Solidity-style CBOR metadata map plus the final 2-byte CBOR length. The example you pointed out is one of these:

    • CBOR payload contains ipfs
    • CBOR payload contains a combined compiler string like zksolc:1.5.15;solc:0.8.24;llvm:1.0.1
    • the physical EraVM trailer also includes zero padding so the metadata region is 32-byte aligned
  2. Older EraVM bytecode, including the other Abstract examples I had tested with do not expose a decodable CBOR trailer at the end. Those cases still look like a bare final 32-byte metadata hash, sometimes with an immediately preceding zero word from EraVM padding.

Pushed a fix is to make ZKsync auxdata detection format-sensitive instead of always treating the final 32 bytes as the auxdata region.

The updated logic now:

  • first checks whether the final 2 bytes point to a valid CBOR payload;
  • if yes, marks the full physical EraVM metadata trailer as auxdata, including alignment padding and the CBOR length bytes;
  • if no CBOR is present, falls back to the older bare 32-byte hash behavior;
  • skips the bare-hash fallback when compiler metadata settings explicitly disable metadata;
  • keeps validateCbor: false during transformation validation because the stored ZKsync auxdata value may be either padded CBOR or a bare hash.

The ipfs value inside the CBOR map is therefore metadata inside the CBOR trailer. It is distinct from the older bare-final-32-byte case. For verification, both serve the same practical role: they are metadata/auxdata that should be ignored for partial matching when the executable bytecode matches.

I also added tests for:

  • modern CBOR EraVM metadata using the Abstract zksolc 1.5.15 example;
  • an IPFS-only CBOR trailer with EraVM alignment padding;
  • non-CBOR/bare-hash EraVM trailers from older Abstract examples;
  • partial matches where only the CBOR IPFS hash changes;
  • partial matches where only the bare metadata hash changes;
  • metadata-disabled settings producing no auxdata fallback.

@coffeexcoin

Copy link
Copy Markdown
Author
  1. Runtime vs. creation bytecode for EraVM

EraVM does not have EVM-style initcode matching in the normal sense. as ZKSync stores a single copy of the contract bytecode and the chain references that single copy of bytecode for all matching contracts using the stored bytecode hash.

For native EraVM contracts, zksolc’s emitted bytecode is the bytecode preimage that gets published as a factory dependency and then installed by hash. The deployment transaction does not carry creationCode + constructorArgs like Ethereum. It calls the ContractDeployer system contract with:

  • selector, e.g. create(bytes32,bytes32,bytes) = 0x9c4d535b
  • salt
  • the versioned EraVM bytecode hash, e.g. 0x0100...
  • ABI-encoded constructor calldata

The actual contract bytecode is provided separately through factoryDeps (a ZKSync/EraVM specific concept), and the deployer checks that the referenced bytecode hash is known before constructing the contract.

This is also how the Matter Labs tooling models it:

  • hardhat-zksync-solc writes the same bytecode into both bytecode and deployedBytecode, with the comment that zkEVM has no real distinction between them.
  • foundry-zksync similarly maps the same zksolc bytecode into both bin and bin_runtime.
  • zksync-era’s deploy transaction helper computes BytecodeHash::for_bytecode(contract_bytecode), puts that hash into ContractDeployer calldata, and puts the actual bytecode into factory_deps.

So in this PR, ZkSolcCompilation.creationBytecode === runtimeBytecode is best understood as an artifact/API compatibility choice: Sourcify’s generic compilation model expects both fields, but zksolc only gives us one native EraVM bytecode artifact. It is not intended to mean that the ContractDeployer calldata should byte-for-byte match creationBytecode.

For verification, the meaningful bytecode match for EraVM is the deployed/runtime bytecode, or equivalently the factory-dep bytecode identified by the versioned bytecode hash. The ContractDeployer calldata is useful for deployment metadata, especially constructor args and bytecode-hash correlation, but it is not “creation bytecode” in the EVM initcode sense.

Given the current PR scope, I think creationBytecodeCborAuxdata = {} is the right conservative behavior. The auxdata region we need for bytecode matching is on the EraVM bytecode itself, which is what we compare as runtime bytecode. Applying auxdata handling to the generic creation path would be misleading because the on-chain “creation code” fetched from the tx is ContractDeployer calldata, not the zksolc bytecode.

Longer term, for first-class EraVM deployment matching, it should be a ZKsync-specific path:

  1. decode ContractDeployer calldata;
  2. extract constructor args from the third parameter;
  3. compare the calldata bytecode hash to the hash of the compiled EraVM bytecode;
  4. ideally verify that the matching bytecode was present in the tx factory deps / published factory-dep table.

But that would be separate from EVM creation-bytecode matching. For this PR, runtime bytecode matching plus runtime auxdata handling is a minimal compatibility surface, and creation auxdata should stay empty.

@coffeexcoin

Copy link
Copy Markdown
Author
  1. What artifacts should an EraVM verification store?

For EraVM, zksolc does not emit the full EVM-style artifact set that regular solc does. In particular, native EraVM output does not have a meaningful evm.deployedBytecode vs evm.bytecode split, and we still should not expect standard EVM source maps or immutable refs for the observed outputs.

It is not fully accurate to say that the only possible outputs are abi, metadata, and evm.bytecode.object. zksolc supports additional generic outputs such as storageLayout, userdoc, and devdoc, depending on the zksolc version/output selection. It can also emit zksolc-specific fields such as hash, factoryDependencies, factoryDependenciesUnlinked, missingLibraries, and objectFormat, but those do not currently map into Sourcify’s generic compiled-contract artifact schema.

I updated this to make the generic artifact handling explicit:

  • zksolc output selection now forces abi and storageLayout;
  • for zksolc versions that support them, it also forces metadata, userdoc, and devdoc;
  • aggregate evm is still only requested for zksolc >= 1.5.0, since older versions reject that selector;
  • verification export now preserves EraVM bytecode link references via the ZK bytecode path when evm.deployedBytecode is absent.

So the expected stored artifact set is now:

  • abi, metadata, userdoc, devdoc, storageLayout when emitted;
  • native EraVM bytecode from evm.bytecode.object;
  • creation/runtime code both derived from that native bytecode for compatibility;
  • runtime auxdata from the detected EraVM metadata region;
  • creation auxdata {};
  • source maps, standard deployedBytecode, and standard immutable references absent/null unless zksolc starts emitting them in a compatible shape.

I also added tests covering the version-gated output selection, generic artifact export/storage, link-reference fallback, and the three Abstract EraVM sample contracts from the PR body.

@manuelwedler manuelwedler moved this from Triage to Sprint - Needs Review in Sourcify Public May 26, 2026
@manuelwedler manuelwedler self-requested a review May 26, 2026 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Sprint - Needs Review

Development

Successfully merging this pull request may close these issues.

3 participants