Skip to content

feat: add Fe language support for smart contract verification#2692

Open
kuzdogan wants to merge 25 commits intostagingfrom
feat/fe-language-support
Open

feat: add Fe language support for smart contract verification#2692
kuzdogan wants to merge 25 commits intostagingfrom
feat/fe-language-support

Conversation

@kuzdogan
Copy link
Member

@kuzdogan kuzdogan commented Mar 17, 2026

Summary

  • New language: Fe (argotorg/fe) — Rust-inspired EVM smart contract language with CLI-only compiler
  • Compiler wrapper (feCompiler.ts): downloads Fe binary per-version, scaffolds ingot project structure (fe.toml + src/), runs fe build, reads out/*.bin/out/*.runtime.bin
  • FeCompilation class: extends AbstractCompilation; AuxdataStyle.FE (no CBOR metadata → always partial match); no ABI/immutables/libraries
  • Server pipeline: FeLocal compiler, Piscina worker support, compilation.ts and database-util.ts Fe cases
  • API middleware (validateAndNormalizeFeInput): normalizes source keys to src/ prefix, validates contractIdentifier format (src/path.fe:Name required)
  • OpenAPI spec updated with Fe language, examples, and contractIdentifier format docs
  • Tests: FeCompilation.spec.ts (6 unit tests), FeVerification.spec.ts (3 verification tests on local hardhat), verify.fe.spec.ts (2 server e2e tests)
  • Pre-commit hook (husky + lint-staged): runs ESLint and Prettier on all staged files before every commit. Added to ensure AI agents write correctly styled code — a pre-commit hook was chosen over a Claude-specific instruction so the enforcement applies globally to all contributors, not just Claude.

API usage

Single-file:

POST /v2/verify/{chainId}/{address}
{
  "compilerVersion": "26.0.0-alpha.10",
  "contractIdentifier": "src/lib.fe:Counter",
  "stdJsonInput": {
    "language": "Fe",
    "sources": {
      "src/lib.fe": { "content": "pub contract Counter { ... }" }
    }
  }
}

Multi-file ingot (contract in non-lib.fe file):

{
  "contractIdentifier": "src/counter.fe:Counter",
  "stdJsonInput": {
    "language": "Fe",
    "sources": {
      "src/lib.fe": { "content": "use ingot::counter::Counter" },
      "src/counter.fe": { "content": "pub contract Counter { ... }" }
    }
  }
}

Sources without src/ prefix are automatically normalized. Match is always partial (Fe emits no CBOR metadata hash).

Test plan

  • cd packages/lib-sourcify && npm test — 150 tests passing (includes 6 FeCompilation + 3 FeVerification)
  • cd services/server && npm run test-local — full integration suite
  • Manual: POST to /v2/verify with a deployed Fe contract

TODOs

🤖 Generated with Claude Code

kuzdogan and others added 7 commits March 16, 2026 10:22
Adds compilation and verification support for the Fe programming language
(argotorg/fe). Fe compiles via a CLI binary with no standard-JSON interface,
outputs plain hex bytecode files, and embeds no CBOR metadata — so
verification is always partial (bytecode match only).

Key changes:
- AuxdataStyle.FE in bytecode-utils (no-op splitAuxdata)
- FeTypes (FeJsonInput, FeOutput, FeOutputContract) in compilers-types
- useFeCompiler in compilers: downloads binary from GitHub releases,
  scaffolds a unique temp ingot dir, runs `fe build`, reads .bin outputs
- FeCompilation class extending AbstractCompilation
- CompilationLanguage union extended with 'Fe', IFeCompiler interface added
- PreRunCompilation, AbstractCompilation, Verification updated for Fe

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tests cover: bytecode correctness, empty CBOR auxdata positions,
empty link/immutable references, version stripping, and compiler errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add FeLocal compiler wrapper
- Wire Fe compiler through CLI, Server, VerificationService, and worker
- Add 'Fe' case to createCompilationFromJsonInput
- Update type unions (workerTypes, handlers, middlewares) to include FeJsonInput
- Update getCompilerNameFromLanguage and createPreRunCompilationFromStoredCandidate for Fe
- Add FeJsonInput to AnyJsonInput in compilers-types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update language enum to include 'Fe'
- Update endpoint description to mention Fe
- Add Fe contract example to StdJSONVerificationRequest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…zation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rification tests

- Remove FeSettings (no configurable fields in Fe alpha); add adapted-interface
  doc comment to FeTypes.ts explaining Fe has no official std-JSON I/O
- Fix FeCompilation bytecodes to include 0x prefix (required for on-chain comparison)
- validateAndNormalizeFeInput: reject bare contract names without path
- Skip storeSignatures for Fe contracts (Fe emits no ABI)
- New FeVerification.spec.ts: deploy + verify single-file and multi-file ingots on local hardhat
- New verify.fe.spec.ts: e2e server API tests for Fe verification (single-file + multi-file)
- Add deployFeContract helper to test utils
- Add ServerFixture Fe compiler support + test/sources/fe fixtures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
kuzdogan and others added 5 commits March 17, 2026 10:06
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Automatically runs ESLint --fix and Prettier on staged files before
every commit. Applies to all developers and Claude instances after
npm install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix prettier formatting on feCompiler.ts, AbstractCompilation.ts,
  FeVerification.spec.ts, middlewares.ts, FeLocal.ts, ServerFixture.ts
- Add feRepoPath to VerificationService unit test options (TS error)
- Cast jsonInput.settings to VyperSettings in EtherscanVerifyApiService
  test to fix TS error introduced by settings union type change
- Add Fe fields lookup + direct DB query test in verify.fe.spec.ts:
  asserts abi/storageLayout/devdoc/userdoc null, language/compiler/
  compilerSettings/fullyQualifiedName correct after Fe verification

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… fields

Add creationBytecode to API fields, assert runtimeMatch/creationMatch,
and check all creation_code_artifacts/runtime_code_artifacts columns
(sourceMap, linkReferences, immutableReferences, cborAuxdata) plus
compilation_artifacts.transientStorageLayout and .sources in the
direct DB query, matching the full compiled_contracts table breakdown.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kuzdogan
Copy link
Member Author

How a Fe contract is stored in compiled_contracts

Fe is a new compilation type with some workarounds in how it plugs into Sourcify's existing framework (no official std-JSON interface, no CBOR metadata, no ABI). Since this is the first language we add that lacks most of the fields Solidity/Vyper produce, I wanted to highlight exactly how these values get saved in compiled_contracts.

compiled_contracts row:

Column Value Description
language "fe" Lowercased from "Fe"
compiler "fe" Fe CLI compiler
version "26.0.0-alpha.10" Compiler version as passed in the request
name "Counter" Contract name from contractIdentifier
fully_qualified_name "src/lib.fe:Counter" Full path:name from contractIdentifier
compiler_settings {} Fe alpha has no configurable settings
compilation_artifacts {"abi": null, "userdoc": null, "devdoc": null, "storageLayout": null, "transientStorageLayout": null, "sources": null} Fe does not emit ABI, NatSpec, storage layouts, or source IDs
creation_code_artifacts {"sourceMap": null, "linkReferences": null, "cborAuxdata": {}} Fe emits no source maps, has no library linking, and embeds no CBOR metadata
runtime_code_artifacts {"sourceMap": null, "linkReferences": null, "immutableReferences": null, "cborAuxdata": {}} Same as creation, plus no immutables

Match type: always "match" (partial) for both creation and runtime. Fe embeds no CBOR metadata in its bytecode, so there is no metadata hash to achieve an exact/perfect match.

All of the above is verified by "should store and return correct Fe-specific fields after verification" in services/server/test/integration/apiv2/verification/verify.fe.spec.ts, which checks every field in this table through two layers:

  1. API responseGET /v2/contract/:chainId/:address?fields=compilation,abi,metadata,storageLayout,sources,creationBytecode,runtimeBytecode
  2. Direct DB query — raw compiled_contracts row via serverFixture.sourcifyDatabase

@kuzdogan kuzdogan requested a review from manuelwedler March 17, 2026 10:26
@kuzdogan kuzdogan moved this from Triage to Sprint - Needs Review in Sourcify Public Mar 17, 2026
@cburgdorf
Copy link

Very nice! I'll prioritize a few things so that we can close some gaps (Most importantly the missing JSON ABI)

feRepo was missing from default, master, staging, and migration configs.
config.get("feRepo") throws at startup if the key is absent, so the
server would crash on launch. Matches the same pattern as vyperRepo.

Also scope lint-staged ESLint to *.ts only, matching the existing
check:eslint script (--ext .ts), so CommonJS .js config files are
not incorrectly linted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Member

@manuelwedler manuelwedler left a comment

Choose a reason for hiding this comment

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

Nice! Much more straight forward than I thought.

No major issues with the PR. Just some improvements that could be done.

Also, if we can have the ABI support by next week, I would wait for it to merge this PR.

summary: Verify Contract (Standard JSON)
description: |-
Submit a contract for verification via the [Solidity standard JSON input](https://docs.soliditylang.org/en/latest/using-the-compiler.html#input-description) or [Vyper JSON input](https://docs.vyperlang.org/en/stable/compiling-a-contract.html#input-json-description).
Submit a contract for verification via the [Solidity standard JSON input](https://docs.soliditylang.org/en/latest/using-the-compiler.html#input-description), [Vyper JSON input](https://docs.vyperlang.org/en/stable/compiling-a-contract.html#input-json-description), or Fe JSON input (a Sourcify-defined format — Fe has no official compiler JSON interface).
Copy link
Member

Choose a reason for hiding this comment

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

Not part of this PR, but we should add a docs page for Fe support and especially describe how the json input should be created for a Fe contract.

Copy link
Member Author

Choose a reason for hiding this comment

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

We don't have docs pages for other languages. Isn't the example here enough?

Copy link
Member

Choose a reason for hiding this comment

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

yeah, probably it's enough. Some note somewhere that we support Fe would still be good. We list languages on the intro page, maybe just add it there.

kuzdogan and others added 2 commits March 23, 2026 10:41
Co-authored-by: Manuel Wedler <manuel@wedler.dev>
Co-authored-by: Manuel Wedler <manuel@wedler.dev>
@kuzdogan kuzdogan force-pushed the feat/fe-language-support branch from 6f67f1b to b8936e8 Compare March 23, 2026 09:51
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kuzdogan kuzdogan force-pushed the feat/fe-language-support branch from b8936e8 to 4208d38 Compare March 23, 2026 09:57
kuzdogan and others added 4 commits March 23, 2026 13:12
The feRepo key is defined in default.js, so config.has() checks are redundant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tests platform detection, executable fetching, single/multi-file
compilation, and error handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the dedicated verify.fe.spec.ts with JSON test case files
in the standardized verification-cases infrastructure. This gives Fe
the same thorough DB and API assertions as Solidity and Vyper tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kuzdogan kuzdogan requested a review from manuelwedler March 23, 2026 10:46
@kuzdogan
Copy link
Member Author

The Blockscout scraping test is failing which will be removed in #2672

@manuelwedler
Copy link
Member

@kuzdogan lib-sourcify tests also fail:

@ethereum-sourcify/lib-sourcify:  Exception during run: test/Compilation/FeCompilation.spec.ts(22,9): error TS2345: Argument of type '{ language: "Fe"; sources: { "src/lib.fe": { content: string; }; }; }' is not assignable to parameter of type 'FeJsonInput'.
@ethereum-sourcify/lib-sourcify:   Property 'settings' is missing in type '{ language: "Fe"; sources: { "src/lib.fe": { content: string; }; }; }' but required in type 'FeJsonInput'.
@ethereum-sourcify/lib-sourcify: test/Compilation/FeCompilation.spec.ts(74,9): error TS2345: Argument of type '{ language: "Fe"; sources: { "src/lib.fe": { content: string; }; }; }' is not assignable to parameter of type 'FeJsonInput'.
@ethereum-sourcify/lib-sourcify:   Property 'settings' is missing in type '{ language: "Fe"; sources: { "src/lib.fe": { content: string; }; }; }' but required in type 'FeJsonInput'.
@ethereum-sourcify/lib-sourcify: test/Compilation/FeCompilation.spec.ts(109,9): error TS2345: Argument of type '{ language: "Fe"; sources: { 'src/lib.fe': { content: string; }; 'src/counter.fe': { content: string; }; }; }' is not assignable to parameter of type 'FeJsonInput'.
@ethereum-sourcify/lib-sourcify:   Property 'settings' is missing in type '{ language: "Fe"; sources: { 'src/lib.fe': { content: string; }; 'src/counter.fe': { content: string; }; }; }' but required in type 'FeJsonInput'.

FeJsonInput requires a `settings` property (Record<string, never>).
The tests were missing it, causing TS2345 compilation errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kuzdogan
Copy link
Member Author

@manuelwedler fixed

Copy link
Member

@manuelwedler manuelwedler left a comment

Choose a reason for hiding this comment

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

Looks good to me! thanks for the changes.

@cburgdorf What's the status on the ABI output? Would still prefer to have it before a Sourcify release of this.

@cburgdorf
Copy link

What's the status on the ABI output?

It was merged and will be included in the next release. I'm currently off but I can try to coordinate with the team to cut a new alpha release so you can test out the integration.

@cburgdorf
Copy link

You can run feup to get the latest version which has json abi support.

$ fe new foobar
Created ingot at /somepath/foobar
cburgdorf at hackbot in /somepath
$ cd foobar/
cburgdorf at hackbot in /somepath/foobar
$ fe build
Wrote /somepath/foobar/out/Counter.bin
Wrote /somepath/foobar/out/Counter.runtime.bin
Wrote /somepath/foobar/out/Counter.abi.json
cburgdorf at hackbot in /somepath/foobar
$ cat /somepath/foobar/out/Counter.abi.json
[
  {
    "type": "constructor",
    "inputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "increment",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "get",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view"
  }
]

@manuelwedler
Copy link
Member

You can run feup to get the latest version which has json abi support.

Awesome! Thanks for this!

@kuzdogan let's update this PR and make the new release the minimum supported Fe version. We can also remove the assumption that abi can be null from the code.

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