Normalize address casing in simulate and contractRegister - #1374
Conversation
Simulate compared a user-provided srcAddress as a raw string against the configured contract addresses, which are canonicalized to the address_format (lowercase or checksum) at config load. A valid but differently-cased address (e.g. a checksummed address under address_format: lowercase) therefore failed to route, even though the whole point of address_format is to not care about case. Canonicalize the provided srcAddress via a shared Config.normalizeUserAddress helper, mirroring how contractRegister already validates and checksums/lowercases user-land addresses. Both entry points now go through the same helper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U3UcHPYjx9eE145k9voQYh
The previous fix normalized every simulate srcAddress through the same strict validation used for config.yaml addresses, which rejects anything that isn't a valid 20-byte hex string. That breaks a common test pattern: using a short placeholder like "0xfoo" as a stand-in srcAddress for events (typically wildcard) where the actual address doesn't matter. Add Config.normalizeSimulateAddress: real addresses are still canonicalized to the configured casing (so mixed-case checksum input keeps routing correctly), but a non-address placeholder is only required to start with "0x" and passes through otherwise unchanged (case-folded under address_format: lowercase). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U3UcHPYjx9eE145k9voQYh
Under address_format: checksum, normalizeSimulateAddress was gated on viem's strict isAddress check, which rejects a syntactically valid 20-byte address whose casing doesn't match its EIP-55 checksum (e.g. all-uppercase hex). Such an address was left unchanged and silently failed to route against the correctly-checksummed config address. getAddress recomputes the checksum regardless of input casing and only throws on a genuinely malformed address, so attempt it directly and fall back to the original string only on failure. This also drops the now-unneeded Address.Evm.isValid binding. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U3UcHPYjx9eE145k9voQYh
Regression test guarding the scope of the relaxed simulate srcAddress normalization: a malformed address in a contract's config addresses must still throw at config load, unaffected by the "0x"-prefix-only relaxation that applies to simulate. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U3UcHPYjx9eE145k9voQYh
…at: lowercase
fromStringLowercaseOrThrow gated on viem's strict isAddress, which rejects a
syntactically valid 20-byte address whose casing isn't already all-lowercase
or a correct EIP-55 checksum (e.g. an all-uppercase address). Under
address_format: lowercase this meant config load could throw on a perfectly
valid address just because of its input casing — defeating the setting's
purpose. address_format: checksum already tolerated this via getAddress,
which recomputes the checksum regardless of input casing.
Switch the shape check to isAddress(str, {strict: false}): valid hex shape in
any casing passes and gets lowercased; non-hex input (e.g. "0xfoo") still
throws.
Added a checksum x lowercase x {all-uppercase, all-lowercase} matrix in
Config_test.res, plus an invalid-address throw case for each format. Verified
the new lowercase/all-uppercase case actually catches the bug: reverting the
Address.res change reproduces the exact failure this fixes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U3UcHPYjx9eE145k9voQYh
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 16 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
Comment |
Summary
Adds address normalization for user-provided addresses in simulate events and contractRegister calls, ensuring they match the configured address format (checksum or lowercase) before routing and comparison. This allows simulate srcAddress and contractRegister add to work correctly regardless of input casing.
Key Changes
Config.res: Added two new address normalization functions:
normalizeUserAddress: Strict validation for contractRegister, requires valid 20-byte hex and applies configured casingnormalizeSimulateAddress: Relaxed validation for simulate srcAddress, only requires "0x" prefix to support test placeholders like "0xfoo", applies configured casing to valid addressesAddress.res: Updated
fromStringLowercaseOrThrowto usestrict: falsewhen calling viem'sisAddress, allowing valid 20-byte hex addresses regardless of casing (since they're about to be lowercased anyway)SimulateItems.res: Modified
deriveSrcAddressto normalize provided srcAddress throughnormalizeSimulateAddressbefore returning, ensuring it matches the configured address formatContractRegisterContext.res: Refactored address validation to use
Config.normalizeUserAddressinstead of inline logic, centralizing the normalization behaviorTests: Added comprehensive test coverage:
Implementation Details
The normalization approach differs between the two use cases:
normalizeUserAddressis strict: rejects invalid addresses since they come from config.yaml or contractRegister which should be validatednormalizeSimulateAddressis relaxed: accepts any "0x"-prefixed string to support test placeholders, but still applies checksum/lowercase normalization to valid addressesThis allows simulate to be flexible for testing while maintaining strict validation where it matters.
https://claude.ai/code/session_01U3UcHPYjx9eE145k9voQYh