-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Add EIP-3770 chain-prefixed address support with validation #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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>
There was a problem hiding this 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()andassertAddressWithChain()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.
src/services/transaction-builder.ts
Outdated
| // 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') | ||
| } | ||
| } |
Copilot
AI
Oct 28, 2025
There was a problem hiding this comment.
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.
src/commands/account/create.ts
Outdated
| const checksummed = validator.assertAddressWithChain( | ||
| value as string, | ||
| chainId as string, | ||
| chainsConfig, | ||
| 'Owner address' | ||
| ) | ||
| if (owners.includes(checksummed)) return 'Owner already added' |
Copilot
AI
Oct 28, 2025
There was a problem hiding this comment.
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.
| 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 | |
| } |
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>
Code Review FixesI've addressed the feedback from the code review: 1. Fixed duplicate assertion in validation functionsFiles: The
2. Eliminated code duplication in TransactionBuilderFile: Replaced ~40 lines of duplicated EIP-3770 validation logic with calls to ValidationService methods:
Benefits:
All tests (783) continue to pass. ✅ |
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
validateAddressWithChain()andassertAddressWithChain()methods for EIP-3770 support0x...) and EIP-3770 (shortName:0x...) formatsUpdated Commands
tx create): To address field now accepts EIP-3770 formataccount open: Safe address inputaccount create: Owner addressesaccount add-owner: New owner addressUser Experience
All address inputs now:
0x... or eth:0x...Testing
Examples
Plain addresses (works as before):
EIP-3770 addresses with validation:
🤖 Generated with Claude Code