Skip to content

fix(ConfidentialETH): use safe staticcall in _cappedDecimals to avoid revert on non-standard WETH#52

Open
amathxbt wants to merge 1 commit intoFhenixProtocol:masterfrom
amathxbt:fix/confidential-eth-safe-decimals-call
Open

fix(ConfidentialETH): use safe staticcall in _cappedDecimals to avoid revert on non-standard WETH#52
amathxbt wants to merge 1 commit intoFhenixProtocol:masterfrom
amathxbt:fix/confidential-eth-safe-decimals-call

Conversation

@amathxbt
Copy link
Copy Markdown

@amathxbt amathxbt commented May 3, 2026

Summary

ConfidentialETH._cappedDecimals calls IERC20Metadata(token).decimals() directly. If the WETH contract does not implement decimals() or reverts (e.g., a non-standard or incorrectly deployed WETH), the entire ConfidentialETH.initialize call reverts with no meaningful error.

Inconsistency

The sibling contract ConfidentialERC20 already handles this correctly with a safe staticcall + fallback:

// ConfidentialERC20._cappedDecimals (safe ✓)
function _cappedDecimals(address token) private view returns (uint8) {
    (bool ok, bytes memory data) = token.staticcall(abi.encodeCall(IERC20Metadata.decimals, ()));
    uint8 d = (ok && data.length == 32) ? abi.decode(data, (uint8)) : 18;
    return d > 6 ? 6 : d;
}

// ConfidentialETH._cappedDecimals (unsafe ✗)
function _cappedDecimals(address token) private view returns (uint8) {
    uint8 d = IERC20Metadata(token).decimals();  // reverts on failure
    return d > 6 ? 6 : d;
}

Impact

Since ConfidentialETH is deployed behind a UUPS proxy via initialize, if initialization reverts the proxy is permanently bricked — _disableInitializers() in the constructor means the implementation cannot be re-initialized, and a new proxy would need to be deployed. Deploying with a non-standard WETH or a network where WETH hasn't been fully set up yet would silently fail in a non-recoverable way.

Fix

Apply the same safe staticcall pattern as ConfidentialERC20._cappedDecimals, falling back to 18 decimals on failure.

ConfidentialETH._cappedDecimals called IERC20Metadata(token).decimals()
directly. If the WETH contract does not implement decimals() or reverts
(e.g., non-standard or mis-deployed WETH), the entire ConfidentialETH
initialise call reverts with no meaningful error, permanently bricking
the proxy since UUPS initializers cannot be retried.

ConfidentialERC20._cappedDecimals already uses a safe staticcall with
an 18-decimal fallback. Apply the same pattern here for consistency
and robustness.
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 3, 2026

@amathxbt is attempting to deploy a commit to the Fhenix Team on Vercel.

A member of the Team first needs to authorize it.

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.

1 participant