Skip to content

H-L06: fix(contracts/ChannelHub): remove payable from methods, clarify in create#666

Merged
nksazonov merged 1 commit into
fix/audit-findingsfrom
fix/audit-h-l06
Apr 9, 2026
Merged

H-L06: fix(contracts/ChannelHub): remove payable from methods, clarify in create#666
nksazonov merged 1 commit into
fix/audit-findingsfrom
fix/audit-h-l06

Conversation

@nksazonov
Copy link
Copy Markdown
Contributor

@nksazonov nksazonov commented Apr 9, 2026

Description

The functions withdrawFromChannel(...), checkpointChannel(...), and createChannel(...) are marked payable but fail to validate msg.value for operations that should never accept ETH.

Specifically:

  1. withdrawFromChannel(...) only accepts WITHDRAW intent, which always has userFundsDelta < 0 (funds flowing OUT to user), yet is marked payable;
  2. checkpointChannel(...) only accepts OPERATE intent, which always has userFundsDelta = 0 (no user fund movement), yet is marked payable;
  3. createChannel(...) accepts multiple intents (DEPOSIT, WITHDRAW, OPERATE) but only validates msg.value inside _pullFunds(...), which is only called when userFundsDelta > 0. For WITHDRAW (userFundsDelta < 0) and OPERATE (userFundsDelta = 0) intents, msg.value is never validated.

Any ETH sent with these non-deposit operations is added to the contract balance, but not tracked in any accounting variable (_nodeBalances, lockedFunds, or _reclaims), making it permanently stuck with no recovery mechanism.

Impact

This is a fail-unsafe design where the protocol silently accepts invalid input instead of rejecting it. The payable modifier on withdrawFromChannel(..) and checkpointChannel(..) creates a misleading interface that suggests these functions can accept ETH when they logically never should. Users who send ETH with these operations (whether through direct calls, contract integration bugs, SDK errors, or UI mistakes) will permanently lose those funds. While this requires user error to trigger and provides no direct benefit to attackers, it represents a protocol design flaw where the declared interface (payable) doesn't match the actual behavior (never processes ETH).

Summary by CodeRabbit

  • Bug Fixes

    • Tightened ETH handling for channel operations to prevent unintended native asset transfers. Channel withdrawals, checkpoints, and closures now reject ETH. Channel creation stricter validates ETH acceptance based on operation intent.
  • Tests

    • Added comprehensive test coverage for ETH rejection behavior across channel operations.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 205859ee-57f1-4db5-a9a0-7b809904c70c

📥 Commits

Reviewing files that changed from the base of the PR and between 38d2ad1 and be1d89d.

📒 Files selected for processing (5)
  • contracts/src/ChannelHub.sol
  • contracts/test/ChannelHub_units/ChannelHub_checkpointChannel.t.sol
  • contracts/test/ChannelHub_units/ChannelHub_closeChannel.t.sol
  • contracts/test/ChannelHub_units/ChannelHub_createChannel.t.sol
  • contracts/test/ChannelHub_units/ChannelHub_withdrawFromChannel.t.sol

📝 Walkthrough

Walkthrough

This PR strengthens ETH value handling in the ChannelHub contract by enforcing stricter validation rules. It adds a check in createChannel() rejecting ETH when intent is not DEPOSIT, and removes payable modifiers from withdrawFromChannel, checkpointChannel, and closeChannel to prevent accidental ETH transfers. Corresponding unit tests validate the new restrictions.

Changes

Cohort / File(s) Summary
ChannelHub ETH Validation
contracts/src/ChannelHub.sol
Added msg.value == 0 enforcement in createChannel() for non-DEPOSIT intents, reverting with IncorrectValue(). Removed payable modifier from withdrawFromChannel, checkpointChannel, and closeChannel.
Unit Tests for ETH Rejection
contracts/test/ChannelHub_units/ChannelHub_createChannel.t.sol, ChannelHub_withdrawFromChannel.t.sol, ChannelHub_checkpointChannel.t.sol, ChannelHub_closeChannel.t.sol
Added four new test contracts with test_revert_ifETHSent() test functions verifying ETH is rejected when sent to channel operations, each using low-level calls with value: 1 and asserting failure with empty revert data.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested reviewers

  • ihsraham
  • philanton
  • dimast-x

Poem

🐰 A rabbit hops through contracts with care,
No stray ETH floating in the air!
Payable stripped, validation tight,
Channel operations now done right! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly reflects the main change: removing payable modifiers from three ChannelHub methods and clarifying ETH handling in createChannel, which matches the core objective of preventing untracked ETH acceptance.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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 fix/audit-h-l06

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.

@nksazonov
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nksazonov nksazonov merged commit c2310fe into fix/audit-findings Apr 9, 2026
3 checks passed
@nksazonov nksazonov deleted the fix/audit-h-l06 branch April 9, 2026 12:12
nksazonov added a commit that referenced this pull request Apr 16, 2026
H-H01: fix(contracts): disallow challenge with CLOSE or FIN_MIG intents (#631)

H-M01: fix(contracts/ChannelHub): remove transferred amount check (#636)

fix(contracts/ChannelHub): allow unblocking escrow ops after migration (#637)

M-C01: feat(contracts): add token check between states (#639)

H-L03: fix(contracts/ChannelHub): skip disputed escrow during purge (#640)

M-H04: feat(contracts/ChannelHub): purge during escrow challenge finalization, count both skip and purge (#641)

M-M02: fix(contracts): require zero allocs on close (#643)

M-C02: fix(rpc/core): apply finalize escrow deposit correctly (#644)

H-L02: fix(contracts/ChannelHub): revert on withdrawFromVault failure (#651)

M-H03: fix(clearnode): log correct fields on failure (#647)

M-C03: fix(pkg/core): disallow negative amount transitions (#645)

H-L01: fix(contracts/ChannelHub): add CH address to prevent val addition replay (#650)

M-H06(clearnode): restrict max channel challenge duration (#654)

M-M03(clearnode): revert EscrowLock on insufficient home user balance (#655)

M-M04(clearnode): support default signer even if it is not approved (#656)

M-M05(clearnode): require correct intent on FinalizeEscrowWithdrawal (#657)

M-M09: reject asset decimals exceeding its token's decimals (#659)

M-L01: return correct errors during state advancement validation (#660)

M-L03: limit number of related ids per session key state (#662)

M-I02: normalize input hex addresses (#663)

M-H01: feat(contracts/ChannelHub): restrict to one node (#649)

M-H07: fix(contracts/ChannelHub): emit stored, not arbitrary candidate state (#664)

M-I01: feat(contracts/ChannelHub): remove updateLastState flag (#665)

H-L06: fix(contracts/ChannelHub): remove payable from methods, clarify in create (#666)

H-L09: feat(contracts/ChannelEngine): add non-home migration version check (#669)

YNU-839: fix blockchain listener lifecycle (#658)

M-H09: use channel signer for all channel state node sigs (#667)

H-L07: fix(contracts/ChannelHub): restrict createChannel to non-existing channels (#668)

H-I02: docs(contracts): add a note that rebasing tokens are not supported (#670)

H-I03: fix(contracts/ChannelHub): use CIE pattern in depositToHub (#671)

M-H11: revert empty signatures on quorum verification (#672)

M-H11: reject issuance of receiver state during escrow ops (#674)

H-I01: docs(contracts): note that fee-on-transfer tokens are not supported (#675)

M-L04: docs: mention liquidity monitoring (#677)

fix(contracts/ChannelHub): fix initialize escrow deposit dos (#679)

M-H08: enforce strict transition ordering after MutualLock and EscrowLock (#680)

fix: run forge fmt (#681)

M-L05: docs(contracts): document native token deposits (#685)

fix(clearnode): resolve a set of audit findings (#686)

M-H13: feat(contract): add validateChallengeSignature, revert in SK validator (#688)
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.

3 participants