Skip to content

Conversation

@katspaugh
Copy link
Member

Summary

Add comprehensive support for EIP-3770 chain-prefixed addresses across all address input fields in the CLI. Users can now use either plain addresses (0x...) or chain-prefixed addresses (eth:0x..., matic:0x..., etc.). When a chain prefix is provided, the system validates that it matches the current Safe's chain and errors out if there's a mismatch.

Changes

Core Functionality

  • ValidationService: Added validateAddressWithChain() and assertAddressWithChain() methods for EIP-3770 support
    • Validates addresses in both plain (0x...) and EIP-3770 (shortName:0x...) formats
    • Verifies chain prefix matches the expected chain ID
    • Provides clear, user-friendly error messages on chain mismatch
    • Strips EIP-3770 prefix and returns checksummed address

Updated Commands

  • Transaction Creation (tx create): To address field now accepts EIP-3770 format
  • Transaction Builder: All address-type function parameters support chain-prefixed addresses
  • Account Management:
    • account open: Safe address input
    • account create: Owner addresses
    • account add-owner: New owner address

User Experience

All address inputs now:

  • Show helpful placeholders: 0x... or eth:0x...
  • Provide clear error messages on chain mismatch:
    Chain mismatch: address is for Polygon (matic:) but current Safe is on Ethereum
    
  • Maintain backward compatibility: plain addresses work exactly as before

Testing

  • ✅ Added 17 comprehensive tests for EIP-3770 validation
  • ✅ All 783 tests passing across 19 test files
  • ✅ TypeScript compilation successful
  • ✅ ESLint checks passed
  • ✅ Full backward compatibility maintained

Examples

Plain addresses (works as before):

To address: 0x742d35Cc6634C0532925a3b844Bc454e4438f44e

EIP-3770 addresses with validation:

# Valid - matches current Safe's chain
To address: eth:0x742d35Cc6634C0532925a3b844Bc454e4438f44e

# Error - chain mismatch detected
To address: matic:0x742d35Cc6634C0532925a3b844Bc454e4438f44e
Error: Chain mismatch: address is for Polygon (matic:) but current Safe is on Ethereum

🤖 Generated with Claude Code

Add comprehensive support for EIP-3770 chain-prefixed addresses across
all address input fields. Users can now use either plain addresses
(0x...) or chain-prefixed addresses (eth:0x..., matic:0x..., etc.).
When a chain prefix is provided, the system validates that it matches
the current Safe's chain and errors out if there's a mismatch.

Changes:
- Add validateAddressWithChain() and assertAddressWithChain() methods
  to ValidationService for EIP-3770 support and chain validation
- Update transaction creation 'To' field to support EIP-3770 format
- Update TransactionBuilder to validate address-type parameters with
  chain prefix support
- Update account management commands (open, create, add-owner) to
  accept chain-prefixed addresses
- Add 17 comprehensive tests for EIP-3770 validation
- Maintain backward compatibility: plain addresses work as before

All address inputs now show helpful placeholders like "0x... or eth:0x..."
and provide clear error messages on chain mismatch (e.g., "Chain mismatch:
address is for Polygon (matic:) but current Safe is on Ethereum").

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings October 28, 2025 14:30
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements EIP-3770 chain-prefixed address support across the CLI, enabling users to specify addresses in both plain format (0x...) and chain-prefixed format (eth:0x...). The implementation validates that chain prefixes match the current Safe's chain and provides clear error messages on mismatches.

Key Changes:

  • Added validateAddressWithChain() and assertAddressWithChain() methods to ValidationService for EIP-3770 validation
  • Updated all address input commands to support and validate chain-prefixed addresses
  • Added comprehensive test coverage with 17 new tests for EIP-3770 functionality

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/services/validation-service.ts Core implementation of EIP-3770 validation methods with chain verification
src/services/transaction-builder.ts Updated to accept EIP-3770 addresses in function parameters and validate chain matches
src/commands/tx/create.ts Modified transaction creation to support EIP-3770 format in 'to' address field
src/commands/account/open.ts Added EIP-3770 support for Safe address input during account opening
src/commands/account/create.ts Updated owner address inputs to accept chain-prefixed addresses
src/commands/account/add-owner.ts Modified new owner address validation to support EIP-3770 format
src/tests/unit/services/validation-service.test.ts Comprehensive test suite for EIP-3770 validation scenarios
src/tests/unit/services/transaction-builder.test.ts Updated test initialization to include chain configuration parameters
src/tests/integration/integration-transaction-builder.test.ts Updated integration test to pass required chain parameters

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 138 to 165
// Check if EIP-3770 format
if (isEIP3770(value)) {
try {
const { shortName, address } = parseEIP3770(value)

// Resolve the chainId from the shortName
const chainId = getChainIdFromShortName(shortName, this.chains)

// Check if it matches the expected chain
if (chainId !== this.chainId) {
const expectedChain = this.chains[this.chainId]
const providedChain = this.chains[chainId]
const expectedName = expectedChain?.name || this.chainId
const providedName = providedChain?.name || chainId

throw new Error(
`Chain mismatch: address is for ${providedName} (${shortName}:) but current Safe is on ${expectedName}`
)
}

return address
} catch (error) {
if (error instanceof Error) {
throw error
}
throw new Error('Invalid EIP-3770 address format')
}
}
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

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

The EIP-3770 validation logic in parseParameter duplicates the exact same logic from validateAddressWithChain in ValidationService (lines 48-76 of validation-service.ts). This creates a maintenance burden as any changes to validation logic need to be made in both places. Consider refactoring to call validationService.validateAddressWithChain() and validationService.assertAddressWithChain() instead of duplicating the logic.

Copilot uses AI. Check for mistakes.
Comment on lines 99 to 105
const checksummed = validator.assertAddressWithChain(
value as string,
chainId as string,
chainsConfig,
'Owner address'
)
if (owners.includes(checksummed)) return 'Owner already added'
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

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

The assertAddressWithChain call is duplicated inside the validate function (lines 99-104) and after the prompt (lines 115-120). The validate function should return the error message from validateAddressWithChain, and the assertion should only happen once after the prompt succeeds.

Suggested change
const checksummed = validator.assertAddressWithChain(
value as string,
chainId as string,
chainsConfig,
'Owner address'
)
if (owners.includes(checksummed)) return 'Owner already added'
// Only check for duplicate after successful validation
// Use the normalized address for comparison
try {
const checksummed = validator.assertAddressWithChain(
value as string,
chainId as string,
chainsConfig,
'Owner address'
)
if (owners.includes(checksummed)) return 'Owner already added'
} catch (e) {
// If assert throws, validation will already have failed above
}

Copilot uses AI. Check for mistakes.
katspaugh and others added 2 commits October 28, 2025 15:33
Validation functions should only return error strings, not throw errors.
The assertAddressWithChain call was duplicated - once inside the validate
function and once after the prompt. This fix wraps the assertion in a
try-catch block inside the validate function to properly handle errors
as return values.

Changes:
- Wrap assertAddressWithChain in try-catch within validation functions
- Add comments to clarify the duplicate check logic
- Keep single assertion after prompt for final processing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…Builder

Replace duplicated EIP-3770 validation logic in TransactionBuilder with calls
to ValidationService methods. This eliminates code duplication and ensures
consistent validation behavior across the codebase.

Changes:
- Add ValidationService instance to TransactionBuilder
- Replace parseParameter address logic with assertAddressWithChain()
- Use validateAddressWithChain() directly in validateParameter()
- Remove unused Address import
- Add comments explaining the refactoring

Benefits:
- Single source of truth for address validation
- Reduced maintenance burden
- Consistent error messages
- Better code organization

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@katspaugh
Copy link
Member Author

Code Review Fixes

I've addressed the feedback from the code review:

1. Fixed duplicate assertion in validation functions

Files: src/commands/account/create.ts, src/commands/account/add-owner.ts

The assertAddressWithChain() call was being used inside the validate function, which should only return error strings, not throw. Fixed by:

  • Wrapping the assertion in a try-catch block within validation functions
  • Keeping the validation logic separate from the final assertion after the prompt
  • Added clarifying comments

2. Eliminated code duplication in TransactionBuilder

File: src/commands/transaction-builder.ts

Replaced ~40 lines of duplicated EIP-3770 validation logic with calls to ValidationService methods:

  • Added ValidationService instance to TransactionBuilder
  • Use validateAddressWithChain() in validateParameter() for direct validation
  • Use assertAddressWithChain() in parseParameter() to get checksummed address
  • Removed unused imports

Benefits:

  • Single source of truth for address validation
  • Reduced maintenance burden - changes to validation logic only need to be made in one place
  • Consistent error messages across all address inputs
  • Better code organization and separation of concerns

All tests (783) continue to pass. ✅

@katspaugh katspaugh merged commit 8dd1c00 into main Oct 28, 2025
4 checks passed
@katspaugh katspaugh deleted the feat/eip-3770-address-validation branch October 28, 2025 14:37
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