Skip to content

Normalize address casing in simulate and contractRegister - #1374

Merged
DZakh merged 5 commits into
mainfrom
claude/case-sensitive-address-matching-0owkn8
Jul 2, 2026
Merged

Normalize address casing in simulate and contractRegister#1374
DZakh merged 5 commits into
mainfrom
claude/case-sensitive-address-matching-0owkn8

Conversation

@DZakh

@DZakh DZakh commented Jul 2, 2026

Copy link
Copy Markdown
Member

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 casing
    • normalizeSimulateAddress: Relaxed validation for simulate srcAddress, only requires "0x" prefix to support test placeholders like "0xfoo", applies configured casing to valid addresses
  • Address.res: Updated fromStringLowercaseOrThrow to use strict: false when calling viem's isAddress, allowing valid 20-byte hex addresses regardless of casing (since they're about to be lowercased anyway)

  • SimulateItems.res: Modified deriveSrcAddress to normalize provided srcAddress through normalizeSimulateAddress before returning, ensuring it matches the configured address format

  • ContractRegisterContext.res: Refactored address validation to use Config.normalizeUserAddress instead of inline logic, centralizing the normalization behavior

  • Tests: Added comprehensive test coverage:

    • Config parsing tests for address format handling with various casings
    • Simulate routing tests for mixed-case and invalid-checksum addresses
    • Wildcard simulate test for non-address placeholders

Implementation Details

The normalization approach differs between the two use cases:

  • normalizeUserAddress is strict: rejects invalid addresses since they come from config.yaml or contractRegister which should be validated
  • normalizeSimulateAddress is relaxed: accepts any "0x"-prefixed string to support test placeholders, but still applies checksum/lowercase normalization to valid addresses

This allows simulate to be flexible for testing while maintaining strict validation where it matters.

https://claude.ai/code/session_01U3UcHPYjx9eE145k9voQYh

claude added 5 commits July 1, 2026 16:07
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
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a96c4b11-4d27-49b1-9361-7eb544157e42

📥 Commits

Reviewing files that changed from the base of the PR and between 957e500 and d07b4a5.

📒 Files selected for processing (7)
  • packages/envio/src/Address.res
  • packages/envio/src/Config.res
  • packages/envio/src/ContractRegisterContext.res
  • packages/envio/src/SimulateItems.res
  • scenarios/test_codegen/test/Config_test.res
  • scenarios/test_codegen/test/SimulateDynamicAddress_test.res
  • scenarios/test_codegen/test/WildcardSimulate_test.res

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

@DZakh
DZakh merged commit 44d7349 into main Jul 2, 2026
8 checks passed
@DZakh
DZakh deleted the claude/case-sensitive-address-matching-0owkn8 branch July 2, 2026 14:00
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.

2 participants