Skip to content

Refactor Protocol Parsing and Introduce Word Domains Support#140

Merged
RogerPodacter merged 2 commits into
evm-backend-demofrom
fix_collections
Nov 10, 2025
Merged

Refactor Protocol Parsing and Introduce Word Domains Support#140
RogerPodacter merged 2 commits into
evm-backend-demofrom
fix_collections

Conversation

@RogerPodacter

@RogerPodacter RogerPodacter commented Nov 7, 2025

Copy link
Copy Markdown
Member
  • Updated ProtocolParser to streamline extraction of parameters for various protocols, including ERC-20 and ERC-721.
  • Introduced WordDomainsParser for handling legacy word-domain registrations, supporting operations like register and set_primary.
  • Enhanced the Erc20FixedDenominationParser and Erc721EthscriptionsCollectionParser to utilize the new unified parsing approach.
  • Adjusted ABI encoding for operations to accommodate changes in parameter structures.
  • Improved test coverage for new parsing functionalities and ensured compatibility across different protocols.

Note

Unifies protocol parsing, adds word-domain support, enforces content hashes for collections, and links fixed-denomination ERC-20 mints to ERC-721 collections with updated contracts, events, and tests.

  • Protocol Parsing:
    • Unified ProtocolParser: Parses JSON body/headers and plain content; routes to parsers; for_calldata returns [protocol, op, encoded]; import fallback by ethscription_id for collections.
    • New WordDomainsParser: Supports register (plain text or JSON) and set_primary operations.
    • ERC-20 Parser: Returns [protocol, op, encoded] via strict JSON regex; removed legacy structured outputs.
    • ERC-721 Collections Parser: Adds content_hash to item, computes/injects hash, strict key order, updated ABI encodings and import fallback.
  • Smart Contracts:
    • ERC20FixedDenominationManager: Deploys an ERC-721 collection per token; mints/transfers note NFTs (tokenId = mintId) alongside ERC-20; new mappings and views; new ERC721CollectionDeployed event; updated mint/transfer events to include mintId.
    • ERC721EthscriptionsCollection: Stores collectionId, handles mint-to-zero, uses manager for metadata/URIs.
    • ERC721EthscriptionsCollectionManager: ItemData includes contentHash; validates against ethscription content; Merkle leaf updated; create/init updated to pass collectionId.
    • Ethscriptions: Reduced pagination limits.
    • NameRegistry: New protocol handler for word-domains mirroring ownership as ERC-721; exposes primary name helpers.
    • L2Genesis/Predeploys: Deploy/register NameRegistry; added predeploy address; register word-domains protocol.
  • Tooling/Tests:
    • Event Reader: Parses new/changed events and fields.
    • Specs: Migrate to ProtocolParser.for_calldata; update ABI expectations (including content_hash); add gas test for tokenURI; extensive new tests for collections and name registry.

Written by Cursor Bugbot for commit 1aa1898. This will update automatically on new commits. Configure here.

- Updated `ProtocolParser` to streamline extraction of parameters for various protocols, including ERC-20 and ERC-721.
- Introduced `WordDomainsParser` for handling legacy word-domain registrations, supporting operations like `register` and `set_primary`.
- Enhanced the `Erc20FixedDenominationParser` and `Erc721EthscriptionsCollectionParser` to utilize the new unified parsing approach.
- Adjusted ABI encoding for operations to accommodate changes in parameter structures.
- Improved test coverage for new parsing functionalities and ensured compatibility across different protocols.
@RogerPodacter RogerPodacter requested a review from Copilot November 7, 2025 22:39

Copilot AI left a comment

Copy link
Copy Markdown

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 introduces a unified protocol parsing system with support for word-domains (legacy word-only domain inscriptions), refactors the existing ERC-20 and ERC-721 parsers to use a common interface, and adds ERC-721 collection support for fixed denomination tokens. The changes also include updates to contract event signatures, pagination limits, and test coverage.

Key Changes:

  • Unified protocol parser interface with consistent validate_and_encode API across all parsers
  • New word-domains protocol parser for dotless domain registrations
  • ERC-20 fixed denomination tokens now have associated ERC-721 collections for individual notes
  • Updated event signatures to include mintId parameter for better tracking
  • Added contentHash field to collection items for content verification

Reviewed Changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
spec/models/word_domains_parser_spec.rb New test suite for word-domains protocol parser
spec/models/ethscription_transaction_builder_spec.rb Updated to use unified ProtocolParser interface
spec/models/erc721_ethscriptions_collection_parser_spec.rb Updated tests for new collection item structure with contentHash
spec/models/erc721_collections_import_fallback_spec.rb Updated tests for import fallback with contentHash support
spec/models/erc20_fixed_denomination_parser_spec.rb Refactored tests to use ProtocolParser interface
spec/integration/tokens_protocol_spec.rb Updated integration test for new parser interface
lib/protocol_event_reader.rb Added ERC721CollectionDeployed event and updated event signatures with mintId
contracts/test/TokenURIGas.t.sol New gas benchmark test for tokenURI scaling
contracts/test/NameRegistry.t.sol New test suite for name registry functionality
contracts/test/EthscriptionsToken.t.sol Added tests for ERC-721 collection support in fixed denomination tokens
contracts/test/CollectionsManager.t.sol Updated tests to include contentHash in item data
contracts/src/libraries/Predeploys.sol Added NAME_REGISTRY predeploy address
contracts/src/NameRegistry.sol New contract implementing word-domains protocol
contracts/src/Ethscriptions.sol Reduced pagination limits for performance
contracts/src/ERC721EthscriptionsCollectionManager.sol Added contentHash field to ItemData and verification logic
contracts/src/ERC721EthscriptionsCollection.sol Updated initialization to store collectionId locally and handle zero-address minting
contracts/src/ERC20FixedDenominationManager.sol Added ERC-721 collection deployment and management for fixed denomination tokens
contracts/script/L2Genesis.s.sol Added NameRegistry deployment and protocol registration
app/models/word_domains_parser.rb New Ruby parser for word-domains protocol
app/models/protocol_parser.rb Refactored to unified protocol parsing system with consistent interface
app/models/erc721_ethscriptions_collection_parser.rb Updated to new interface with contentHash support
app/models/erc20_fixed_denomination_parser.rb Refactored to simplified interface with tuple encoding

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

REGISTER_OP = 'register'.b
SET_PRIMARY_OP = 'set_primary'.b

NAME_REGEX = /\A[a-z0-9_]{1,30}\z/.freeze

Copilot AI Nov 7, 2025

Copy link

Choose a reason for hiding this comment

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

The comment on line 21 in contracts/src/NameRegistry.sol defines MAX_LENGTH = 31, but the Ruby parser regex allows only 1-30 characters ({1,30}). This inconsistency between the contract constant (31) and the parser validation (30) could lead to rejected valid inscriptions. Either the contract constant should be 30, or the regex should be {1,31}.

Suggested change
NAME_REGEX = /\A[a-z0-9_]{1,30}\z/.freeze
NAME_REGEX = /\A[a-z0-9_]{1,31}\z/.freeze

Copilot uses AI. Check for mistakes.
CollectionRecord storage collection = collectionStore[collectionId];
Ethscriptions.Ethscription memory ethscription = ethscriptions.getEthscription(ethscriptionId, false);

require(ethscription.contentHash == item.contentHash, "Content hash mismatch");

Copilot AI Nov 7, 2025

Copy link

Choose a reason for hiding this comment

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

The error message 'Content hash mismatch' is unclear about what failed. Consider providing more context such as 'Item content hash does not match ethscription content' to help users understand that the hash they provided in the item data must match the actual ethscription content hash.

Suggested change
require(ethscription.contentHash == item.contentHash, "Content hash mismatch");
require(ethscription.contentHash == item.contentHash, "Item content hash does not match ethscription content");

Copilot uses AI. Check for mistakes.
Comment on lines +396 to +397
function _getContractSalt(string memory tick, string memory contractType) private pure returns (bytes32) {
return keccak256(abi.encode(tick, contractType));

Copilot AI Nov 7, 2025

Copy link

Choose a reason for hiding this comment

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

The salt computation uses only tick and contractType without including any protocol-specific identifier. This could potentially allow collisions if other protocols or contracts use the same tick names with CREATE2. Consider including the protocol name or contract address in the salt computation: keccak256(abi.encode(protocolName, tick, contractType)).

Suggested change
function _getContractSalt(string memory tick, string memory contractType) private pure returns (bytes32) {
return keccak256(abi.encode(tick, contractType));
function _getContractSalt(string memory tick, string memory contractType) private view returns (bytes32) {
return keccak256(abi.encode(address(this), tick, contractType));

Copilot uses AI. Check for mistakes.
Comment on lines +156 to +157
hash = Eth::Util.keccak256(decoded_content).unpack1('H*')
content_hash = '0x' + hash

Copilot AI Nov 7, 2025

Copy link

Choose a reason for hiding this comment

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

The content hash computation is duplicated in three places: lines 156-157, lines 219-220, and used again in the build_item_object method. Consider extracting this into a private helper method like compute_content_hash(decoded_content) to reduce duplication and ensure consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +67 to +68
mapping(string => TokenInfo) internal tokensByTick;
mapping(bytes32 => string) internal deployToTick; // deployEthscriptionId => tick

Copilot AI Nov 7, 2025

Copy link

Choose a reason for hiding this comment

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

Using string as a mapping key in tokensByTick can be gas-inefficient for lookups. Consider using bytes32 (via keccak256(bytes(tick))) as the key instead, similar to how the old implementation used _getTickKey. This would make lookups more gas-efficient while maintaining uniqueness.

Copilot uses AI. Check for mistakes.
@@ -115,8 +115,8 @@ contract Ethscriptions is ERC721EthscriptionsSequentialEnumerableUpgradeable {
// =============================================================

/// @dev Maximum page sizes for pagination helpers

Copilot AI Nov 7, 2025

Copy link

Choose a reason for hiding this comment

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

The pagination limits were reduced from 50/1000 to 20/50 without any explanatory comment. Consider adding a comment explaining why these limits were reduced (e.g., 'Reduced to prevent excessive gas consumption in view functions' or 'Optimized based on gas benchmarks').

Suggested change
/// @dev Maximum page sizes for pagination helpers
/// @dev Maximum page sizes for pagination helpers
// Reduced from 50/1000 to 20/50 to prevent excessive gas consumption in view functions and optimize for gas usage based on benchmarks.

Copilot uses AI. Check for mistakes.

# For plain word-domains: must have blank mimetype and no parameters
# Also reject empty content (like 'data:,')
return nil if decoded_content.strip.empty?

Copilot AI Nov 7, 2025

Copy link

Choose a reason for hiding this comment

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

Using strip.empty? for validation could incorrectly reject valid word domains that consist only of whitespace characters if they passed through the regex validation. However, the NAME_REGEX in WordDomainsParser only allows [a-z0-9_], so whitespace-only names would fail anyway. Consider using empty? directly instead of strip.empty? for clarity, or add a comment explaining the intent.

Suggested change
return nil if decoded_content.strip.empty?
return nil if decoded_content.empty?

Copilot uses AI. Check for mistakes.
Comment thread app/models/word_domains_parser.rb
@RogerPodacter

Copy link
Copy Markdown
Member Author

bugbot run

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.


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

item.description,
item.attributes
));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Merkle Proofs: Backwards Incompatibility

The Merkle proof verification now includes contentHash as the first field in the leaf computation, but this breaks compatibility with existing Merkle trees. Any Merkle proofs generated before this change (which didn't include contentHash) will fail validation even if they were valid before. This creates a backwards incompatibility where previously valid proofs become invalid after deployment.

Fix in Cursor Fix in Web

function _getTickKey(string memory tick) private pure returns (bytes32) {
return keccak256(abi.encode(protocolName, tick));
function _getContractSalt(string memory tick, string memory contractType) private pure returns (bytes32) {
return keccak256(abi.encode(tick, contractType));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: CREATE2 Addresses: Predictability Compromised

The CREATE2 salt generation changed from keccak256(abi.encode(protocolName, tick)) to keccak256(abi.encode(tick, contractType)), breaking deterministic address generation. Any tokens deployed before this change will have different addresses than what predictTokenAddressByTick now computes, and attempting to deploy the same tick will create a contract at a different address, potentially allowing duplicate ticks or breaking address predictability guarantees.

Fix in Cursor Fix in Web

@RogerPodacter RogerPodacter merged commit 6d02714 into evm-backend-demo Nov 10, 2025
8 checks passed
@RogerPodacter RogerPodacter deleted the fix_collections branch November 10, 2025 15:17
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