diff --git a/app/models/protocol_parser.rb b/app/models/protocol_parser.rb index 422ca91..c72a7ca 100644 --- a/app/models/protocol_parser.rb +++ b/app/models/protocol_parser.rb @@ -5,7 +5,6 @@ class ProtocolParser # Protocol name to parser class mapping PROTOCOL_PARSERS = { - 'word-domains' => WordDomainsParser, 'erc-20' => Erc20FixedDenominationParser, 'erc-20-fixed-denomination' => Erc20FixedDenominationParser, 'erc-721-ethscriptions-collection' => Erc721EthscriptionsCollectionParser @@ -15,7 +14,6 @@ def self.extract(content_uri, eth_transaction: nil, ethscription_id: nil) # Parse data URI and extract protocol info parsed = parse_data_uri_and_protocol(content_uri) - # Special case: plain word-domains registration (data:,word) has no protocol markers if parsed.nil? # If we have an ethscription_id, try import fallback for collections regardless of content if ethscription_id @@ -49,7 +47,7 @@ def self.extract(content_uri, eth_transaction: nil, ethscription_id: nil) end end - return try_plain_word_domains(content_uri) + return nil end # Direct routing - no "try" needed since we know the protocol @@ -215,40 +213,4 @@ def self.parse_parameters(parameters) map end - def self.try_plain_word_domains(content_uri) - # Try to parse as plain word-domains registration (data:,word) - return nil unless content_uri.is_a?(String) - return nil unless DataUri.valid?(content_uri) - - data_uri = DataUri.new(content_uri) - decoded_content = data_uri.decoded_data - return nil unless decoded_content.is_a?(String) - - # For plain word-domains: must have blank mimetype and no parameters - # Also reject empty content (like 'data:,') - return nil if decoded_content.strip.empty? - mt = data_uri.mimetype.to_s - return nil unless (mt.empty? || mt == 'text/plain') && data_uri.parameters.empty? - - # Special handling for plain word-domains - encoded = WordDomainsParser.validate_and_encode( - decoded_content: decoded_content, - operation: nil, # No operation for plain text - params: {}, - source: :plain, - ethscription_id: nil - ) - - return nil if encoded == DEFAULT_PARAMS - - protocol, operation, encoded_data = encoded - - { - type: :word_domains, - protocol: protocol, - operation: operation, - params: nil, - encoded_params: encoded_data - } - end -end \ No newline at end of file +end diff --git a/app/models/word_domains_parser.rb b/app/models/word_domains_parser.rb deleted file mode 100644 index 645fae5..0000000 --- a/app/models/word_domains_parser.rb +++ /dev/null @@ -1,73 +0,0 @@ -# Parser for legacy word-only domain inscriptions and related operations. -class WordDomainsParser - DEFAULT_PARAMS = [''.b, ''.b, ''.b].freeze - - PROTOCOL_STRING = 'word-domains'.freeze - PROTOCOL = PROTOCOL_STRING.b - REGISTER_OP = 'register'.b - SET_PRIMARY_OP = 'set_primary'.b - - NAME_REGEX = /\A[a-z0-9_]{1,30}\z/.freeze - - # Validate and encode protocol params - # Unified interface - accepts all possible parameters, uses what it needs - def self.validate_and_encode(decoded_content:, operation:, params:, source:, ethscription_id: nil, **_extras) - new.validate_and_encode( - decoded_content: decoded_content, - operation: operation, - params: params, - source: source - ) - end - - def validate_and_encode(decoded_content:, operation:, params:, source:) - case source - when :json - # JSON-based protocol (from payload) - validate_json_operation(operation, params) - when :plain - # Legacy plain text registration: data:,word - # The decoded_content should match our name regex - return DEFAULT_PARAMS unless decoded_content.is_a?(String) - return DEFAULT_PARAMS unless NAME_REGEX.match?(decoded_content) - - encoded = Eth::Abi.encode(['string'], [decoded_content]) - [PROTOCOL, REGISTER_OP, encoded.b] - else - # Word domains do not support header parameters - DEFAULT_PARAMS - end - end - - private - - def validate_json_operation(operation, params) - case operation - when 'register' - validate_register_operation(params) - when 'set_primary' - validate_set_primary_operation(params) - else - DEFAULT_PARAMS - end - end - - def validate_register_operation(params) - name = params['name'] - return DEFAULT_PARAMS unless name.is_a?(String) - return DEFAULT_PARAMS unless NAME_REGEX.match?(name) - - encoded = Eth::Abi.encode(['string'], [name]) - [PROTOCOL, REGISTER_OP, encoded.b] - end - - def validate_set_primary_operation(params) - name = params['name'] - return DEFAULT_PARAMS unless name.is_a?(String) - # Set primary allows blank to unset primary - return DEFAULT_PARAMS unless name.empty? || NAME_REGEX.match?(name) - - encoded = Eth::Abi.encode(['string'], [name]) - [PROTOCOL, SET_PRIMARY_OP, encoded.b] - end -end diff --git a/contracts/script/L2Genesis.s.sol b/contracts/script/L2Genesis.s.sol index 3f17239..69baa75 100644 --- a/contracts/script/L2Genesis.s.sol +++ b/contracts/script/L2Genesis.s.sol @@ -7,7 +7,6 @@ import {Predeploys} from "../src/libraries/Predeploys.sol"; import {Constants} from "../src/libraries/Constants.sol"; import {Ethscriptions} from "../src/Ethscriptions.sol"; import {MetaStoreLib} from "../src/libraries/MetaStoreLib.sol"; -import {NameRegistry} from "../src/NameRegistry.sol"; import "forge-std/console.sol"; /// @title GenesisEthscriptions @@ -212,8 +211,7 @@ contract L2Genesis is Script { bool isProxiedContract = addr == Predeploys.ETHSCRIPTIONS || addr == Predeploys.ERC20_FIXED_DENOMINATION_MANAGER || addr == Predeploys.ETHSCRIPTIONS_PROVER || - addr == Predeploys.ERC721_ETHSCRIPTIONS_COLLECTION_MANAGER || - addr == Predeploys.NAME_REGISTRY; + addr == Predeploys.ERC721_ETHSCRIPTIONS_COLLECTION_MANAGER; if (isProxiedContract) { address impl = Predeploys.predeployToCodeNamespace(addr); setImplementation(addr, impl); @@ -224,7 +222,6 @@ contract L2Genesis is Script { _setImplementationCodeNamed(Predeploys.ERC20_FIXED_DENOMINATION_MANAGER, "ERC20FixedDenominationManager"); _setImplementationCodeNamed(Predeploys.ERC721_ETHSCRIPTIONS_COLLECTION_MANAGER, "ERC721EthscriptionsCollectionManager"); _setImplementationCodeNamed(Predeploys.ETHSCRIPTIONS_PROVER, "EthscriptionsProver"); - _setImplementationCodeNamed(Predeploys.NAME_REGISTRY, "NameRegistry"); // Templates live directly at their addresses (no proxy wrapping) _setCodeAt(Predeploys.ERC20_FIXED_DENOMINATION_IMPLEMENTATION, "ERC20FixedDenomination"); _setCodeAt(Predeploys.ERC721_ETHSCRIPTIONS_COLLECTION_IMPLEMENTATION, "ERC721EthscriptionsCollection"); @@ -245,9 +242,6 @@ contract L2Genesis is Script { ethscriptions.registerProtocol("erc-721-ethscriptions-collection", Predeploys.ERC721_ETHSCRIPTIONS_COLLECTION_MANAGER); console.log("Registered erc-721-ethscriptions-collection protocol handler:", Predeploys.ERC721_ETHSCRIPTIONS_COLLECTION_MANAGER); - - ethscriptions.registerProtocol("word-domains", Predeploys.NAME_REGISTRY); - console.log("Registered word-domains protocol handler:", Predeploys.NAME_REGISTRY); } /// @notice Deploy L1Block contract (stores L1 block attributes) diff --git a/contracts/src/NameRegistry.sol b/contracts/src/NameRegistry.sol deleted file mode 100644 index 7be8bf5..0000000 --- a/contracts/src/NameRegistry.sol +++ /dev/null @@ -1,293 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.24; - -import {Base64} from "solady/utils/Base64.sol"; -import {LibString} from "solady/utils/LibString.sol"; -import "./ERC721EthscriptionsEnumerableUpgradeable.sol"; -import "./interfaces/IProtocolHandler.sol"; -import "./Ethscriptions.sol"; -import "./libraries/Predeploys.sol"; -import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; - -/// @title NameRegistry -/// @notice Handles legacy word-domain registrations and mirrors ownership as an ERC-721 collection. -contract NameRegistry is ERC721EthscriptionsEnumerableUpgradeable, IProtocolHandler { - using LibString for *; - - Ethscriptions public constant ethscriptions = Ethscriptions(Predeploys.ETHSCRIPTIONS); - - string public constant protocolName = "word-domains"; - - struct DomainRecord { - bytes32 packedName; - bytes32 ethscriptionId; - address owner; - uint256 tokenId; - } - - struct DomainInfo { - string name; - bytes32 ethscriptionId; - address owner; - uint256 tokenId; - } - - mapping(bytes32 => DomainRecord) private domains; - mapping(uint256 => bytes32) private nameKeyByTokenId; - mapping(address => bytes32) internal primaryNameKey; - - error OnlyEthscriptions(); - error NameAlreadyRegistered(); - error EthscriptionAlreadyLinked(); - error InvalidName(); - error DomainNotFound(); - error NotDomainOwner(); - error TransfersDisabled(); - error TokenDoesNotExist(); - - event DomainRegistered(bytes32 indexed nameKey, string name, address indexed owner, bytes32 indexed ethscriptionId, uint256 tokenId); - event PrimarySet(address indexed owner, bytes32 indexed nameKey); - event PrimaryCleared(address indexed owner); - - modifier onlyEthscriptions() { - if (msg.sender != address(ethscriptions)) revert OnlyEthscriptions(); - _; - } - - function name() public view override returns (string memory) { - return "Name Registry"; - } - - function symbol() public view override returns (string memory) { - return "NAME"; - } - - // ============================ - // Protocol handler functions - // ============================ - - function op_register(bytes32 ethscriptionId, bytes calldata data) external onlyEthscriptions { - // Ruby indexer already validated and normalized the name, just trust it - string memory name = abi.decode(data, (string)); - - Ethscriptions.Ethscription memory etsc = ethscriptions.getEthscription(ethscriptionId, false); - - bytes32 nameKey = LibString.packOne(name); - DomainRecord storage record = domains[nameKey]; - if (record.ethscriptionId != bytes32(0)) revert NameAlreadyRegistered(); - - address owner = ethscriptions.ownerOf(ethscriptionId); - uint256 tokenId = etsc.ethscriptionNumber; - - domains[nameKey] = DomainRecord({ - packedName: nameKey, - ethscriptionId: ethscriptionId, - owner: owner, - tokenId: tokenId - }); - nameKeyByTokenId[tokenId] = nameKey; - - if (owner == address(0)) { - _mint(etsc.creator, tokenId); - _transfer(etsc.creator, address(0), tokenId); - } else { - _mint(owner, tokenId); - } - - emit DomainRegistered(nameKey, name, owner, ethscriptionId, tokenId); - } - - function op_set_primary(bytes32 ethscriptionId, bytes calldata data) external onlyEthscriptions { - // Ruby indexer already validated and normalized the name, just trust it - string memory name = abi.decode(data, (string)); - address actor = ethscriptions.ownerOf(ethscriptionId); - - if (bytes(name).length == 0) { - primaryNameKey[actor] = bytes32(0); - emit PrimaryCleared(actor); - return; - } - - bytes32 nameKey = LibString.packOne(name); - DomainRecord storage record = domains[nameKey]; - if (record.ethscriptionId == bytes32(0)) revert DomainNotFound(); - if (record.owner != actor) revert NotDomainOwner(); - - primaryNameKey[actor] = nameKey; - emit PrimarySet(actor, nameKey); - } - - function onTransfer(bytes32 ethscriptionId, address from, address to) external override onlyEthscriptions { - uint256 tokenId = ethscriptions.getTokenId(ethscriptionId); - bytes32 nameKey = nameKeyByTokenId[tokenId]; - DomainRecord storage record = domains[nameKey]; - if (record.ethscriptionId == bytes32(0)) return; - - record.owner = to; - - _transfer(from, to, tokenId); - - if (primaryNameKey[from] == nameKey) { - primaryNameKey[from] = bytes32(0); - emit PrimaryCleared(from); - } - } - - // ============================ - // View helpers - // ============================ - - function getDomainInfo(bytes32 nameKey) external view returns (DomainInfo memory info) { - return _domainInfo(nameKey); - } - - function primaryName(address owner) - external - view - returns (string memory name, bytes32 nameKey, bytes32 ethscriptionId) - { - nameKey = primaryNameKey[owner]; - if (nameKey == bytes32(0)) { - return ("", bytes32(0), bytes32(0)); - } - - DomainRecord storage record = domains[nameKey]; - - // Validate ownership - if they don't own it, return empty - if (record.owner != owner) { - return ("", bytes32(0), bytes32(0)); - } - - return (LibString.unpackOne(record.packedName), nameKey, record.ethscriptionId); - } - - function tokenIdForNameKey(bytes32 nameKey) external view returns (uint256) { - DomainRecord storage record = domains[nameKey]; - if (record.ethscriptionId == bytes32(0)) revert DomainNotFound(); - return record.tokenId; - } - - function nameKeyForToken(uint256 tokenId) external view returns (bytes32) { - bytes32 nameKey = nameKeyByTokenId[tokenId]; - if (domains[nameKey].ethscriptionId == bytes32(0)) revert TokenDoesNotExist(); - return nameKey; - } - - // ============================ - // Metadata - // ============================ - - function tokenURI(uint256 tokenId) public view override returns (string memory) { - if (_ownerOf(tokenId) == address(0)) revert TokenDoesNotExist(); - - bytes32 nameKey = nameKeyByTokenId[tokenId]; - DomainRecord storage record = domains[nameKey]; - if (record.ethscriptionId == bytes32(0)) revert TokenDoesNotExist(); - string memory name = LibString.unpackOne(record.packedName); - - // Get the ethscription data to extract the ethscription number - Ethscriptions.Ethscription memory ethscription = ethscriptions.getEthscription(record.ethscriptionId, false); - - // Get the media URI from the ethscription - (string memory mediaType, string memory mediaUri) = ethscriptions.getMediaUri(record.ethscriptionId); - - // Convert ethscriptionId to hex string (0x prefixed) - string memory ethscriptionIdHex = uint256(record.ethscriptionId).toHexString(32); - - bytes memory json = abi.encodePacked( - '{"name":"', - name.escapeJSON(), - '","description":"An Ethscription name"', - ',"ethscription_id":"', - ethscriptionIdHex, - '","ethscription_number":', - ethscription.ethscriptionNumber.toString(), - ',"', - mediaType, - '":"', - mediaUri, - '","attributes":[', - '{"trait_type":"Length","value":', - bytes(name).length.toString(), - ',"display_type":"number"}', - ']}' - ); - - return string(abi.encodePacked("data:application/json;base64,", Base64.encode(json))); - } - - /// @notice OpenSea collection-level metadata - /// @return JSON string with collection metadata - function contractURI() external pure returns (string memory) { - return string(abi.encodePacked( - 'data:application/json;base64,', - Base64.encode(bytes( - '{"name":"Ethscription Names",' - '"description":"On-chain name system for Ethscriptions. Allowed characters: a-z, 0-9, and _ (underscore). Max length: 30 characters.",' - '"image":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAwIiBoZWlnaHQ9IjUwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iNTAwIiBoZWlnaHQ9IjUwMCIgZmlsbD0iIzEwMTAxMCIvPjx0ZXh0IHg9IjI1MCIgeT0iMjUwIiBmb250LXNpemU9IjgwIiBmb250LWZhbWlseT0ibW9ub3NwYWNlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMDBmZjAwIj5bTkFNRVNdPC90ZXh0Pjwvc3ZnPg==",' - '"external_link":"https://ethscriptions.com"}' - )) - )); - } - - // --- Transfer/approvals blocked externally --------------------------------- - - function transferFrom(address, address, uint256) - public - pure - override(ERC721EthscriptionsUpgradeable, IERC721) - { - revert TransfersDisabled(); - } - - function safeTransferFrom(address, address, uint256) - public - pure - override(ERC721EthscriptionsUpgradeable, IERC721) - { - revert TransfersDisabled(); - } - - function safeTransferFrom(address, address, uint256, bytes memory) - public - pure - override(ERC721EthscriptionsUpgradeable, IERC721) - { - revert TransfersDisabled(); - } - - function approve(address, uint256) - public - pure - override(ERC721EthscriptionsUpgradeable, IERC721) - { - revert TransfersDisabled(); - } - - function setApprovalForAll(address, bool) - public - pure - override(ERC721EthscriptionsUpgradeable, IERC721) - { - revert TransfersDisabled(); - } - - function _update(address to, uint256 tokenId, address auth) internal override returns (address) { - if (auth != address(0) && auth != address(this)) { - revert TransfersDisabled(); - } - return super._update(to, tokenId, auth); - } - - function _domainInfo(bytes32 nameKey) internal view returns (DomainInfo memory info) { - DomainRecord storage record = domains[nameKey]; - if (record.ethscriptionId == bytes32(0)) revert DomainNotFound(); - - info = DomainInfo({ - name: LibString.unpackOne(record.packedName), - ethscriptionId: record.ethscriptionId, - owner: record.owner, - tokenId: record.tokenId - }); - } -} diff --git a/contracts/src/libraries/Predeploys.sol b/contracts/src/libraries/Predeploys.sol index 58f1459..2c8814b 100644 --- a/contracts/src/libraries/Predeploys.sol +++ b/contracts/src/libraries/Predeploys.sol @@ -41,9 +41,6 @@ library Predeploys { /// @notice ERC721 Ethscriptions collection manager address constant ERC721_ETHSCRIPTIONS_COLLECTION_MANAGER = 0x3300000000000000000000000000000000000006; - - /// @notice Name registry handler for word-domain protocol - address constant NAME_REGISTRY = 0x3300000000000000000000000000000000000007; // ============ Helper Functions ============ diff --git a/contracts/test/NameRegistry.t.sol b/contracts/test/NameRegistry.t.sol deleted file mode 100644 index 55086a0..0000000 --- a/contracts/test/NameRegistry.t.sol +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.24; - -import "forge-std/Test.sol"; -import "./TestSetup.sol"; -import "../src/NameRegistry.sol"; - -contract NameRegistryTest is TestSetup { - NameRegistry internal registry; - address internal alice = address(0xA11CE); - address internal bob = address(0xB0B); - - function setUp() public override { - super.setUp(); - registry = NameRegistry(Predeploys.NAME_REGISTRY); - vm.deal(alice, 10 ether); - vm.deal(bob, 10 ether); - } - - function testRegisterWordCreatesToken() public { - bytes32 txHash = keccak256("alpha"); - Ethscriptions.CreateEthscriptionParams memory params = createTestParams(txHash, alice, "data:,alpha", false); - params.protocolParams = Ethscriptions.ProtocolParams({ - protocolName: "word-domains", - operation: "register", - data: abi.encode("alpha") - }); - - vm.prank(alice); - ethscriptions.createEthscription(params); - - uint256 expectedTokenId = ethscriptions.getTokenId(txHash); - bytes32 nameKey = registry.nameKeyForToken(expectedTokenId); - NameRegistry.DomainInfo memory info = registry.getDomainInfo(nameKey); - - assertEq(info.name, "alpha"); - assertEq(info.owner, alice); - assertEq(info.tokenId, expectedTokenId); - assertEq(registry.ownerOf(info.tokenId), alice); - assertEq(info.ethscriptionId, txHash); - assertEq(registry.tokenIdForNameKey(nameKey), expectedTokenId); - assertEq(registry.nameKeyForToken(expectedTokenId), nameKey); - } - - function testPrimarySetAndClear() public { - bytes32 txHash = keccak256("beta"); - Ethscriptions.CreateEthscriptionParams memory registerParams = createTestParams(txHash, alice, "data:,beta", false); - registerParams.protocolParams = Ethscriptions.ProtocolParams({ - protocolName: "word-domains", - operation: "register", - data: abi.encode("beta") - }); - - vm.prank(alice); - ethscriptions.createEthscription(registerParams); - - // Set primary via second inscription - bytes32 setPrimaryTx = keccak256("set-primary"); - Ethscriptions.CreateEthscriptionParams memory primaryParams = createTestParams( - setPrimaryTx, - alice, - 'data:,{"p":"word-domains","op":"set_primary","name":"beta"}', - false - ); - primaryParams.protocolParams = Ethscriptions.ProtocolParams({ - protocolName: "word-domains", - operation: "set_primary", - data: abi.encode("beta") - }); - - vm.prank(alice); - ethscriptions.createEthscription(primaryParams); - - (,, bytes32 primaryEthscription) = registry.primaryName(alice); - assertEq(primaryEthscription, txHash); - - // Clear primary - bytes32 clearTx = keccak256("clear-primary"); - Ethscriptions.CreateEthscriptionParams memory clearParams = createTestParams( - clearTx, - alice, - 'data:,{"p":"word-domains","op":"set_primary","name":""}', - false - ); - clearParams.protocolParams = Ethscriptions.ProtocolParams({ - protocolName: "word-domains", - operation: "set_primary", - data: abi.encode("") - }); - - vm.prank(alice); - ethscriptions.createEthscription(clearParams); - - (,, primaryEthscription) = registry.primaryName(alice); - assertEq(primaryEthscription, bytes32(0)); - } - - function testTransfersMirrorCollection() public { - bytes32 txHash = keccak256("gamma"); - Ethscriptions.CreateEthscriptionParams memory params = createTestParams(txHash, alice, "data:,gamma", false); - params.protocolParams = Ethscriptions.ProtocolParams({ - protocolName: "word-domains", - operation: "register", - data: abi.encode("gamma") - }); - - vm.prank(alice); - ethscriptions.createEthscription(params); - - uint256 tokenId = ethscriptions.getTokenId(txHash); - - vm.prank(alice); - ethscriptions.transferEthscription(bob, txHash); - - assertEq(registry.ownerOf(tokenId), bob); - - vm.prank(bob); - vm.expectRevert(NameRegistry.TransfersDisabled.selector); - registry.transferFrom(bob, alice, tokenId); - } -} diff --git a/spec/models/erc721_ethscriptions_collection_parser_spec.rb b/spec/models/erc721_ethscriptions_collection_parser_spec.rb index f4effd1..d8dd4bc 100644 --- a/spec/models/erc721_ethscriptions_collection_parser_spec.rb +++ b/spec/models/erc721_ethscriptions_collection_parser_spec.rb @@ -368,8 +368,6 @@ test_cases = [ 'data:,{broken json', 'data:,', - # Note: 'data:,null' is a valid word-domains registration for the word "null" - # so it's excluded from this test 'data:,[]', 'data:,"string"' ] diff --git a/spec/models/protocol_parser_spec.rb b/spec/models/protocol_parser_spec.rb index bd033ac..a02cd1b 100644 --- a/spec/models/protocol_parser_spec.rb +++ b/spec/models/protocol_parser_spec.rb @@ -4,28 +4,6 @@ let(:zero_merkle_root) { '0x' + '0' * 64 } describe '.extract' do - context 'word domains protocol' do - it 'parses raw word registrations' do - content_uri = 'data:,alpha' - - result = described_class.extract(content_uri) - - expect(result).not_to be_nil - expect(result[:type]).to eq(:word_domains) - expect(result[:protocol]).to eq('word-domains'.b) - expect(result[:operation]).to eq('register'.b) - end - - it 'parses set_primary JSON operations' do - content_uri = 'data:,{"p":"word-domains","op":"set_primary","name":"alpha"}' - - result = described_class.extract(content_uri) - - expect(result).not_to be_nil - expect(result[:type]).to eq(:word_domains) - expect(result[:operation]).to eq('set_primary'.b) - end - end context 'erc-20-fixed-denomination protocol' do it 'parses a valid deploy inscription' do content_uri = 'data:,{"p":"erc-20","op":"deploy","tick":"punk","max":"21000000","lim":"1000"}' @@ -99,23 +77,6 @@ end describe '.for_calldata' do - it 'encodes word domain registrations' do - protocol, operation, encoded = described_class.for_calldata('data:,beta') - - expect(protocol).to eq('word-domains'.b) - expect(operation).to eq('register'.b) - expect(Eth::Abi.decode(['string'], encoded).first).to eq('beta') - end - - it 'encodes word domain set_primary operations' do - content_uri = 'data:,{"p":"word-domains","op":"set_primary","name":"beta"}' - - protocol, operation, encoded = described_class.for_calldata(content_uri) - - expect(protocol).to eq('word-domains'.b) - expect(operation).to eq('set_primary'.b) - expect(Eth::Abi.decode(['string'], encoded).first).to eq('beta') - end it 'encodes erc-20 deploy params' do content_uri = 'data:,{"p":"erc-20","op":"deploy","tick":"punk","max":"21000000","lim":"1000"}' diff --git a/spec/models/word_domains_parser_spec.rb b/spec/models/word_domains_parser_spec.rb deleted file mode 100644 index 7cf537b..0000000 --- a/spec/models/word_domains_parser_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'rails_helper' - -RSpec.describe WordDomainsParser do - describe 'via ProtocolParser' do - it 'parses raw word inscriptions' do - params = ProtocolParser.for_calldata('data:,pizza') - - expect(params[0]).to eq('word-domains'.b) - expect(params[1]).to eq('register'.b) - decoded = Eth::Abi.decode(['string'], params[2]) - expect(decoded.first).to eq('pizza') - end - - it 'rejects mixed-case words' do - params = ProtocolParser.for_calldata('data:,Pizza') - expect(params).to eq([''.b, ''.b, ''.b]) - end - - it 'rejects disallowed characters' do - params = ProtocolParser.for_calldata('data:,hello.world') - expect(params).to eq([''.b, ''.b, ''.b]) - end - - it 'parses JSON set_primary operations' do - json = 'data:,{"p":"word-domains","op":"set_primary","name":"alpha"}' - params = ProtocolParser.for_calldata(json) - - expect(params[0]).to eq('word-domains'.b) - expect(params[1]).to eq('set_primary'.b) - expect(Eth::Abi.decode(['string'], params[2]).first).to eq('alpha') - end - - it 'allows clearing primary with empty string' do - json = 'data:,{"p":"word-domains","op":"set_primary","name":""}' - params = ProtocolParser.for_calldata(json) - - expect(params[1]).to eq('set_primary'.b) - expect(Eth::Abi.decode(['string'], params[2]).first).to eq('') - end - - it 'accepts 31 character names and rejects longer ones' do - valid = 'a' * 30 - invalid = 'b' * 31 - - expect(ProtocolParser.for_calldata("data:,#{valid}")[1]).to eq('register'.b) - expect(ProtocolParser.for_calldata("data:,#{invalid}")).to eq([''.b, ''.b, ''.b]) - end - end -end