Skip to content

Implement collections and more generic protocol framework#113

Merged
RogerPodacter merged 9 commits into
evm-backend-demofrom
collection_protocol
Oct 13, 2025
Merged

Implement collections and more generic protocol framework#113
RogerPodacter merged 9 commits into
evm-backend-demofrom
collection_protocol

Conversation

@RogerPodacter

@RogerPodacter RogerPodacter commented Sep 24, 2025

Copy link
Copy Markdown
Member

Note

Introduce a generic protocol framework with Collections support, protocol handler registry, and ERC-721 enumerable upgrades across contracts, extractors, libs, and tests.

  • Protocols/Extraction:
    • Add GenericProtocolExtractor, CollectionsParamsExtractor, and unified ProtocolExtractor with calldata encoding; introduce DataUri parser.
    • New Ruby readers/helpers: ProtocolEventReader, CollectionsReader, TokenReader.
  • Contracts:
    • Implement CollectionsManager, EthscriptionERC721 (non-transferable ERC-721 with enumerable), and IProtocolHandler.
    • Update Ethscriptions: protocol-aware creation/transfer hooks, media URI generation, batched proving interactions; add getEthscriptionWithContent.
    • Extend TokenManager to protocol handler pattern; add deterministic clone/predict helpers.
    • Update genesis (L2Genesis.s.sol): deploy/register protocol handlers; add predeploys for ERC721 template and CollectionsManager; adjust genesis flow.
    • Upgrade ERC721EthscriptionsUpgradeable to store name/symbol and support IERC721Enumerable.
  • Infrastructure:
    • Add/modify predeploy addresses (Predeploys.sol) incl. depositor account and new templates.
    • Improve L1RpcPrefetcher (throttling, caching latest, stats); enhance GenesisGenerator (quiet logging, env flags).
  • Tests:
    • Comprehensive Solidity and Ruby integration tests for Collections, Tokens, Prover, ERC721 enumerable, protocol registration, and E2E flows.
  • Rails:
    • Remove facet_rails_common usage from ApplicationController.
  • Deps:
    • Dependency updates and cleanup in Gemfile/lockfile.

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

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 implements a comprehensive protocol framework for handling collections and other generic protocols in the Ethscriptions system, building upon the existing token protocol infrastructure.

  • Introduces ProtocolExtractor as a unified entry point that intelligently routes between strict token protocols and flexible generic protocols
  • Adds GenericProtocolExtractor with automatic type inference for ABI encoding of arbitrary JSON protocol data
  • Implements the collections protocol with CollectionsManager and EthscriptionERC721 contracts for managing NFT collections

Reviewed Changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
spec/models/protocol_extractor_spec.rb Tests unified protocol extraction routing between token and generic extractors
spec/models/token_params_extractor_via_protocol_spec.rb Verifies token protocol continues working through unified interface
spec/models/generic_protocol_*_spec.rb Comprehensive testing of generic protocol extraction and type inference
contracts/src/protocols/ New protocol handler interfaces and ERC721 collection contract implementation
contracts/src/Ethscriptions.sol Updated to use generic protocol framework with handler registration
contracts/src/TokenManager.sol Refactored to implement IProtocolHandler interface
app/models/protocol_extractor.rb Ruby implementation of unified protocol extraction
app/models/generic_protocol_extractor.rb Generic protocol extractor with automatic type inference

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +102 to +119
context 'marketplace protocol' do
# it 'extracts create_listing with string numbers' do
# content_uri = 'data:,{"p":"marketplace","op":"create_listing","tokenId":"0x4567","price":"1000000000000000000","duration":"604800"}'

# protocol, operation, encoded_data = GenericProtocolExtractor.extract(content_uri)

# expect(protocol).to eq('marketplace'.b)
# expect(operation).to eq('create_listing'.b)

# types = ['uint256', 'uint256', 'string']
# decoded = Eth::Abi.decode(types, encoded_data)

# expect(decoded[0]).to eq(604800)
# expect(decoded[1]).to eq(1000000000000000000) # 1 ETH in wei
# expect(decoded[2]).to eq('0x4567')
# end
end

Copilot AI Sep 24, 2025

Copy link

Choose a reason for hiding this comment

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

This entire marketplace protocol test context contains only commented-out code. Either implement the test or remove this dead code to keep the test suite clean.

Suggested change
context 'marketplace protocol' do
# it 'extracts create_listing with string numbers' do
# content_uri = 'data:,{"p":"marketplace","op":"create_listing","tokenId":"0x4567","price":"1000000000000000000","duration":"604800"}'
# protocol, operation, encoded_data = GenericProtocolExtractor.extract(content_uri)
# expect(protocol).to eq('marketplace'.b)
# expect(operation).to eq('create_listing'.b)
# types = ['uint256', 'uint256', 'string']
# decoded = Eth::Abi.decode(types, encoded_data)
# expect(decoded[0]).to eq(604800)
# expect(decoded[1]).to eq(1000000000000000000) # 1 ETH in wei
# expect(decoded[2]).to eq('0x4567')
# end
end

Copilot uses AI. Check for mistakes.
Comment on lines +143 to +155
# it 'recognizes various fixed-length bytes types' do
# content_uri = 'data:,{"p":"test","op":"bytes_test","byte1":"0x12","byte4":"0x12345678","byte16":"0x123456789abcdef0123456789abcdef0"}'

# protocol, operation, encoded_data = GenericProtocolExtractor.extract(content_uri)

# types = ['bytes1', 'bytes16', 'bytes4']
# decoded = Eth::Abi.decode(types, encoded_data)

# expect(decoded[0]).to eq('0x12')
# expect(decoded[1]).to eq('0x123456789abcdef0123456789abcdef0')
# expect(decoded[2]).to eq('0x12345678')
# end

Copilot AI Sep 24, 2025

Copy link

Choose a reason for hiding this comment

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

This commented-out test for various fixed-length bytes types should either be implemented or removed. The current implementation in the extractor raises 'Not supported' for non-address/bytes32 types, so this test would fail if uncommented.

Suggested change
# it 'recognizes various fixed-length bytes types' do
# content_uri = 'data:,{"p":"test","op":"bytes_test","byte1":"0x12","byte4":"0x12345678","byte16":"0x123456789abcdef0123456789abcdef0"}'
# protocol, operation, encoded_data = GenericProtocolExtractor.extract(content_uri)
# types = ['bytes1', 'bytes16', 'bytes4']
# decoded = Eth::Abi.decode(types, encoded_data)
# expect(decoded[0]).to eq('0x12')
# expect(decoded[1]).to eq('0x123456789abcdef0123456789abcdef0')
# expect(decoded[2]).to eq('0x12345678')
# end

Copilot uses AI. Check for mistakes.
Comment thread app/models/generic_protocol_extractor.rb Outdated
Comment on lines +259 to +268
// CollectionItem memory item = collectionItems[txHash];

// // If this ethscription is part of a collection, sync ownership
// if (item.collectionId != bytes32(0)) {
// CollectionInfo memory collection = collections[item.collectionId];
// if (collection.collectionContract != address(0)) {
// // Sync the ownership in the ERC721 contract
// EthscriptionERC721(collection.collectionContract).syncOwnership(item.tokenId);
// }
// }

Copilot AI Sep 24, 2025

Copy link

Choose a reason for hiding this comment

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

The onTransfer function contains only commented-out code. Either implement the transfer synchronization logic or remove this dead code. The function currently does nothing when ethscriptions are transferred.

Suggested change
// CollectionItem memory item = collectionItems[txHash];
// // If this ethscription is part of a collection, sync ownership
// if (item.collectionId != bytes32(0)) {
// CollectionInfo memory collection = collections[item.collectionId];
// if (collection.collectionContract != address(0)) {
// // Sync the ownership in the ERC721 contract
// EthscriptionERC721(collection.collectionContract).syncOwnership(item.tokenId);
// }
// }
CollectionItem memory item = collectionItems[txHash];
// If this ethscription is part of a collection, sync ownership
if (item.collectionId != bytes32(0)) {
CollectionInfo memory collection = collections[item.collectionId];
if (collection.collectionContract != address(0)) {
// Sync the ownership in the ERC721 contract
EthscriptionERC721(collection.collectionContract).syncOwnership(item.tokenId);
}
}

Copilot uses AI. Check for mistakes.
Comment thread contracts/src/protocols/EthscriptionERC721.sol Outdated
cursor[bot]

This comment was marked as outdated.

Comment thread contracts/test/CollectionsManager.t.sol Outdated
// Create an ethscription to add to the collection
vm.prank(alice);

string memory itemContent = 'data:,{"p":"collections","op":"add","collection":"0x1234","item":"artwork1"}';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

what's item here? is it the item's data uri?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The tests are completely messed up rn!

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

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 34 out of 34 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

spec/models/token_params_extractor_spec.rb:1

  • Remove the commented-out debugging statement.
require 'rails_helper'

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +569 to +571
# if receipt['status'] != '0x1'
# ap EthRpcClient.l2.trace_transaction(l2_tx['hash'])
# end

Copilot AI Sep 29, 2025

Copy link

Choose a reason for hiding this comment

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

Remove the commented-out debug code or move it to a proper debug logging mechanism if it's still needed.

Copilot uses AI. Check for mistakes.
// If they differ, update our state
if (actualOwner != recordedOwner) {
// Use internal _transfer to update state and emit event
_transfer(recordedOwner, actualOwner, tokenId);

Copilot AI Sep 29, 2025

Copy link

Choose a reason for hiding this comment

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

The _transfer function should validate that the token exists and that the recorded owner actually owns it before performing the transfer. Consider adding these checks to prevent potential state corruption.

Copilot uses AI. Check for mistakes.

def parse_json_safely(json_str)
# Size check
raise ExtractionError, "JSON too large" if json_str.bytesize > 10_000

Copilot AI Sep 29, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Consider making the JSON size limit configurable or define it as a constant rather than hardcoding the value.

Copilot uses AI. Check for mistakes.
@RogerPodacter RogerPodacter requested a review from Copilot October 10, 2025 21:33

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 43 out of 43 changed files in this pull request and generated 5 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +574 to +576
# if receipt['status'] != '0x1'
# ap EthRpcClient.l2.trace_transaction(l2_tx['hash'])
# end

Copilot AI Oct 10, 2025

Copy link

Choose a reason for hiding this comment

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

Remove commented debugging code before merging to production. This appears to be leftover debugging logic that should be cleaned up.

Suggested change
# if receipt['status'] != '0x1'
# ap EthRpcClient.l2.trace_transaction(l2_tx['hash'])
# end

Copilot uses AI. Check for mistakes.
"max" => "1000000",
"lim" => "100"
}
# binding.irb

Copilot AI Oct 10, 2025

Copy link

Choose a reason for hiding this comment

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

Remove debugging breakpoint before merging to production.

Suggested change
# binding.irb

Copilot uses AI. Check for mistakes.
Comment thread lib/l1_rpc_prefetcher.rb
@@ -2,6 +2,7 @@
require 'retriable'

Copilot AI Oct 10, 2025

Copy link

Choose a reason for hiding this comment

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

The Memery include is added but there's no corresponding require statement for the gem. Ensure Memery is properly required or imported.

Suggested change
require 'retriable'
require 'retriable'
require 'memery'

Copilot uses AI. Check for mistakes.
// import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "../ERC721EthscriptionsUpgradeable.sol";
// import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
// import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

Copilot AI Oct 10, 2025

Copy link

Choose a reason for hiding this comment

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

Remove commented import statements. These appear to be replaced by the actual import but should be cleaned up for code clarity.

Suggested change
// import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

Copilot uses AI. Check for mistakes.
Comment on lines +60 to 64
// // protocolParams: Ethscriptions.ProtocolParams({
// // protocol: "",
// // tick: "",
// // max: 0,
// // lim: 0,
// // amt: 0
// // operation: "",
// // data: ""
// // })

Copilot AI Oct 10, 2025

Copy link

Choose a reason for hiding this comment

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

Remove large blocks of commented test code or convert to proper documentation if the code examples are meant to be preserved for reference.

Copilot uses AI. Check for mistakes.
cursor[bot]

This comment was marked as outdated.

@RogerPodacter RogerPodacter requested a review from Copilot October 13, 2025 16:32

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 46 out of 47 changed files in this pull request and generated 2 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +114 to +128
# it 'extracts create_listing with string numbers' do
# content_uri = 'data:,{"p":"marketplace","op":"create_listing","tokenId":"0x4567","price":"1000000000000000000","duration":"604800"}'

# protocol, operation, encoded_data = GenericProtocolExtractor.extract(content_uri)

# expect(protocol).to eq('marketplace'.b)
# expect(operation).to eq('create_listing'.b)

# types = ['uint256', 'uint256', 'string']
# decoded = Eth::Abi.decode(types, encoded_data)

# expect(decoded[0]).to eq(604800)
# expect(decoded[1]).to eq(1000000000000000000) # 1 ETH in wei
# expect(decoded[2]).to eq('0x4567')
# end

Copilot AI Oct 13, 2025

Copy link

Choose a reason for hiding this comment

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

Remove large blocks of commented-out test code. If these tests are planned for future implementation, track them in issues or TODO comments instead.

Suggested change
# it 'extracts create_listing with string numbers' do
# content_uri = 'data:,{"p":"marketplace","op":"create_listing","tokenId":"0x4567","price":"1000000000000000000","duration":"604800"}'
# protocol, operation, encoded_data = GenericProtocolExtractor.extract(content_uri)
# expect(protocol).to eq('marketplace'.b)
# expect(operation).to eq('create_listing'.b)
# types = ['uint256', 'uint256', 'string']
# decoded = Eth::Abi.decode(types, encoded_data)
# expect(decoded[0]).to eq(604800)
# expect(decoded[1]).to eq(1000000000000000000) # 1 ETH in wei
# expect(decoded[2]).to eq('0x4567')
# end
# TODO: Implement test for 'create_listing' operation (see removed commented-out test)

Copilot uses AI. Check for mistakes.
Comment on lines 98 to 103
// mimeSubtype: "png",
// esip6: false,
// isCompressed: false,
// tokenParams: Ethscriptions.TokenParams({
// op: "", protocol: "", tick: "", max: 0, lim: 0, amt: 0
// protocolParams: Ethscriptions.ProtocolParams({
// protocol: "", operation: "", data: ""
// })

Copilot AI Oct 13, 2025

Copy link

Choose a reason for hiding this comment

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

Remove large blocks of commented-out test code. If compression testing is planned for future implementation, create proper test stubs or track in issues.

Copilot uses AI. Check for mistakes.
cursor[bot]

This comment was marked as outdated.

@RogerPodacter RogerPodacter merged commit ae9d933 into evm-backend-demo Oct 13, 2025
2 checks passed
@RogerPodacter RogerPodacter deleted the collection_protocol branch October 13, 2025 16:55
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.

3 participants