Skip to content

Fix audit findings in EVM Cadence contract#8518

Merged
joshuahannan merged 6 commits into
masterfrom
josh/evm-contract-security-audit
Apr 8, 2026
Merged

Fix audit findings in EVM Cadence contract#8518
joshuahannan merged 6 commits into
masterfrom
josh/evm-contract-security-audit

Conversation

@joshuahannan
Copy link
Copy Markdown
Contributor

@joshuahannan joshuahannan commented Apr 7, 2026

Summary

Security and quality audit fixes for fvm/evm/stdlib/contract.cdc:

  • Fix dead variable and redundant borrow in validateCOAOwnershipProof: replaced nil-check + force-unwrap pattern with if let, and captured coaRef.address().bytes into a single binding rather than calling address() twice.
  • Fix Balance.isZero() missing view annotation: allows use in view contexts and pre-conditions.
  • Remove redundant isPaused() pre-condition from CadenceOwnedAccount.deposit: the delegated call to EVMAddress.deposit already enforces the pause guard.
  • Simplify CadenceOwnedAccount.balance(): call InternalEVM.balance directly instead of constructing an intermediate EVMAddress struct.
  • Remove incorrect @return docs on initAddress and CadenceOwnedAccount.deposit (both said "returns the token decimals of the ERC20").
  • Add docs to encodeABI and decodeABI.
  • Document setupHeartbeat access(all) rationale: explains why public access is safe (idempotency enforced by storage path collision).
  • Document dry-run pause exemption on dryRun: clarifies that dry-run functions intentionally bypass the pause guard since they perform no state mutations.
  • Fix typo: "Etherem" → "Ethereum" in file-level comment.
  • Fix stale docs link: update /evm/about/build/evm/how-it-works.

Summary by CodeRabbit

  • Bug Fixes

    • Optimized EVM balance computation; made EVM.Balance.isZero() a read-only view; removed pause guard from CadenceOwnedAccount.deposit().
  • Documentation

    • Added EVM contract security audit report; expanded docs for dry-run, ABI encode/decode, ownership proof validation, and heartbeat behavior.
  • Tests

    • Updated bootstrap test fixtures and genesis state-commitment constants.

@joshuahannan joshuahannan requested a review from a team as a code owner April 7, 2026 18:47
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 43afd43.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Updated the EVM Cadence stdlib contract (docs, ABI helpers, COA validation, pause guards, and a small API purity change) and replaced hardcoded genesis/state-commitment hexes used by bootstrap tests and unit-test utilities. (26 words)

Changes

Cohort / File(s) Summary
EVM Cadence contract
fvm/evm/stdlib/contract.cdc
Documentation fixes and expansions (typo, external link, encode/decode ABI docs, dryRun, setupHeartbeat). Changed EVM.Balance.isZero() to view fun; CadenceOwnedAccount.balance() now uses InternalEVM.balance(address: self.addressBytes); removed pre { !EVM.isPaused() } from CadenceOwnedAccount.deposit; refactored validateCOAOwnershipProof borrow/return logic and simplified error messages.
Bootstrap tests
engine/execution/state/bootstrap/bootstrap_test.go
Updated expected state commitment hex literals in two bootstrap tests to new precomputed values.
Unit test utils
utils/unittest/execution_state.go
Replaced GenesisStateCommitmentHex and updated genesisCommitHexByChainID return values for flow.Testnet and default cases.
New audit report
fvm/evm/stdlib/REPORT.md
Added security audit report for fvm/evm/stdlib/contract.cdc listing findings (two open low-severity items) and remediation suggestions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Improvement, Flow EVM

Suggested reviewers

  • janezpodhostnik
  • zhangchiqing
  • fxamacker

Poem

🐰 I hopped through bytes and docs with glee,
Tuned guards, fixed links, and balanced a tree,
I nudged a view here, smoothed a borrow there,
Updated hashes, tests, and left a tiny hare-care,
Carrots poured in CI — a gentle, joyful spree.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: applying audit findings fixes to the EVM Cadence contract, which is the primary objective evident from the raw summary.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch josh/evm-contract-security-audit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@joshuahannan joshuahannan requested a review from m-Peter April 7, 2026 18:49
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 7, 2026

Codecov Report

❌ Patch coverage is 0% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
utils/unittest/execution_state.go 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@joshuahannan joshuahannan force-pushed the josh/evm-contract-security-audit branch from bb521c5 to e29a7fd Compare April 7, 2026 18:50
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@fvm/evm/stdlib/contract.cdc`:
- Around line 1088-1110: The error message concatenation in the COA ownership
mismatch branch concatenates "EVM.validateCOAOwnershipProof(): Cannot validate
COA ownership." with "The provided evm address..." resulting in "ownership.The"
without a space; update the string used in the first ValidationResult (inside
the if let coaRef borrow branch) so there is a trailing space (or include a
leading space in the concatenated string) before concatenation—locate the
ValidationResult construction that returns isValid: false in the COA address
bytes loop (using coaRef, coaAddressBytes, evmAddress) and add the missing
whitespace to the concatenated message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: abff165d-e6d8-4024-99f2-db1f22ed1124

📥 Commits

Reviewing files that changed from the base of the PR and between 9596707 and bb521c5.

📒 Files selected for processing (1)
  • fvm/evm/stdlib/contract.cdc

Comment thread fvm/evm/stdlib/contract.cdc
@blacksmith-sh

This comment has been minimized.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
fvm/evm/stdlib/contract.cdc (1)

1095-1096: ⚠️ Potential issue | 🟡 Minor

Add the missing space in the COA mismatch message.

These literals still concatenate as ownership.The, so the returned problem string reads awkwardly.

✏️ Suggested fix
-                        problem: "EVM.validateCOAOwnershipProof(): Cannot validate COA ownership."
-                                 .concat("The provided evm address does not match the account's COA address.")
+                        problem: "EVM.validateCOAOwnershipProof(): Cannot validate COA ownership. "
+                                 .concat("The provided evm address does not match the account's COA address.")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fvm/evm/stdlib/contract.cdc` around lines 1095 - 1096, The problem string
concatenation in EVM.validateCOAOwnershipProof() produces "ownership.The"
because there's no space between the two literals; update the concatenation
around the variable/problem construction (the `problem` assignment in
EVM.validateCOAOwnershipProof) to include a space either at the end of the first
literal ("EVM.validateCOAOwnershipProof(): Cannot validate COA ownership. ") or
at the start of the second literal (" The provided evm address...") so the final
message reads correctly.
🧹 Nitpick comments (1)
fvm/evm/stdlib/contract.cdc (1)

928-930: Clarify the bytes mapping example.

This contract already defines EVMBytes/EVMBytes4/EVMBytes32 as the Cadence-side byte wrappers. Showing [UInt8] → bytes here is confusing unless raw [UInt8] is intentionally supported too.

✏️ One possible doc fix
-    /// corresponding Solidity types (e.g. UInt256 → uint256, [UInt8] → bytes).
+    /// corresponding Solidity types (e.g. UInt256 → uint256, EVMBytes → bytes).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fvm/evm/stdlib/contract.cdc` around lines 928 - 930, The documentation
comment that maps Cadence types to Solidity types is ambiguous about bytes;
update the example to reference the Cadence EVM byte wrapper types (EVMBytes,
EVMBytes4, EVMBytes32) instead of showing [UInt8] → bytes, or explicitly state
if raw [UInt8] arrays are also supported; edit the comment near the ABI encoding
routine so it reads something like: Cadence EVMBytes / EVMBytes4 / EVMBytes32
map to Solidity bytes/bytes4/bytes32 (or, if raw [UInt8] is supported, add a
sentence clarifying that [UInt8] will be accepted and converted to EVMBytes).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@fvm/evm/stdlib/contract.cdc`:
- Around line 1095-1096: The problem string concatenation in
EVM.validateCOAOwnershipProof() produces "ownership.The" because there's no
space between the two literals; update the concatenation around the
variable/problem construction (the `problem` assignment in
EVM.validateCOAOwnershipProof) to include a space either at the end of the first
literal ("EVM.validateCOAOwnershipProof(): Cannot validate COA ownership. ") or
at the start of the second literal (" The provided evm address...") so the final
message reads correctly.

---

Nitpick comments:
In `@fvm/evm/stdlib/contract.cdc`:
- Around line 928-930: The documentation comment that maps Cadence types to
Solidity types is ambiguous about bytes; update the example to reference the
Cadence EVM byte wrapper types (EVMBytes, EVMBytes4, EVMBytes32) instead of
showing [UInt8] → bytes, or explicitly state if raw [UInt8] arrays are also
supported; edit the comment near the ABI encoding routine so it reads something
like: Cadence EVMBytes / EVMBytes4 / EVMBytes32 map to Solidity
bytes/bytes4/bytes32 (or, if raw [UInt8] is supported, add a sentence clarifying
that [UInt8] will be accepted and converted to EVMBytes).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 517fb23f-5b51-4710-9d6c-935fd32362ad

📥 Commits

Reviewing files that changed from the base of the PR and between bb521c5 and e29a7fd.

📒 Files selected for processing (1)
  • fvm/evm/stdlib/contract.cdc

Copy link
Copy Markdown
Member

@turbolent turbolent left a comment

Choose a reason for hiding this comment

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

Nice!

/// @return the token decimals of the ERC20
access(all)
fun deposit(from: @FlowToken.Vault) {
pre {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

How come the pre check here is removed? This will allow EVM state-change, allowing funds to enter Flow EVM, but the withdraw function below still has the pre check. Note that COA.deposit & COA.withdraw do create EVM.TransactionExecuted events, which means that an EVM state-change occurred.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

My bad, just missed the comment in the PR description:

Remove redundant isPaused() pre-condition from CadenceOwnedAccount.deposit: the delegated call to EVMAddress.deposit already enforces the pause guard.

/// the from address as the signer.
/// The transaction state changes are not persisted.
/// This is useful for gas estimation or calling view contract functions.
/// Note: dry-run functions are intentionally exempt from the isPaused() guard —
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Good addition 👍

Comment thread fvm/evm/stdlib/contract.cdc Outdated
return ValidationResult(
isValid: false,
problem: "EVM.validateCOAOwnershipProof(): Cannot validate COA ownership."
.concat("The provided evm address does not match the account's COA address.")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we still need the String.concat here? Not related to the PR, but it's a good opportunity to update it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch! I'll change that

Comment thread fvm/evm/stdlib/contract.cdc Outdated
problem: nil
isValid: false,
problem: "EVM.validateCOAOwnershipProof(): Cannot validate COA ownership. "
.concat("Could not borrow the COA resource for account \(address).")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we still need the String.concat here? Not related to the PR, but it's a good opportunity to update it.

Copy link
Copy Markdown
Collaborator

@m-Peter m-Peter left a comment

Choose a reason for hiding this comment

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

LGTM!

@joshuahannan joshuahannan force-pushed the josh/evm-contract-security-audit branch from ef5c8f1 to 24877dc Compare April 8, 2026 16:02
@joshuahannan joshuahannan force-pushed the josh/evm-contract-security-audit branch from 24877dc to d62ef3e Compare April 8, 2026 16:12
@joshuahannan joshuahannan enabled auto-merge April 8, 2026 16:16
@joshuahannan joshuahannan disabled auto-merge April 8, 2026 16:17
@joshuahannan joshuahannan enabled auto-merge April 8, 2026 16:22
@joshuahannan joshuahannan added this pull request to the merge queue Apr 8, 2026
Merged via the queue into master with commit eb3fcc3 Apr 8, 2026
61 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants