From 3ba345a7ec3a45a70aef031dd8031f3ae49d1003 Mon Sep 17 00:00:00 2001 From: Tom Lehman Date: Fri, 17 Oct 2025 15:07:50 -0400 Subject: [PATCH 1/2] Optimize contracts --- app/models/ethscription_transaction.rb | 11 +- contracts/script/L2Genesis.s.sol | 29 +++-- contracts/script/TestTokenUri.s.sol | 10 -- contracts/src/Ethscriptions.sol | 63 +++++----- contracts/src/EthscriptionsProver.sol | 30 ++--- .../libraries/EthscriptionsRendererLib.sol | 29 ++--- contracts/test/CollectionsManager.t.sol | 36 ------ contracts/test/CollectionsProtocol.t.sol | 2 - contracts/test/ERC721Enumerable.t.sol | 2 - contracts/test/EndToEndCompression.t.sol | 2 - contracts/test/EthscriptionsCompression.t.sol | 4 - .../test/EthscriptionsFailureHandling.t.sol | 4 - .../test/EthscriptionsMultiTransfer.t.sol | 4 +- contracts/test/EthscriptionsProver.t.sol | 6 - contracts/test/EthscriptionsToken.t.sol | 2 - contracts/test/EthscriptionsTokenParams.t.sol | 10 -- contracts/test/EthscriptionsWithContent.t.sol | 22 ++-- .../test/EthscriptionsWithTestFunctions.sol | 6 +- contracts/test/GasDebug.t.sol | 2 +- contracts/test/ProtocolRegistration.t.sol | 4 - contracts/test/TestImageSVGWrapper.t.sol | 2 - contracts/test/TestSetup.sol | 20 --- lib/block_validator.rb | 15 +-- lib/storage_reader.rb | 115 +++++++++--------- 24 files changed, 141 insertions(+), 289 deletions(-) diff --git a/app/models/ethscription_transaction.rb b/app/models/ethscription_transaction.rb index 76b932a..1f7ebc8 100644 --- a/app/models/ethscription_transaction.rb +++ b/app/models/ethscription_transaction.rb @@ -30,6 +30,7 @@ class EthscriptionTransaction < T::Struct # Debug info (can be removed if not needed) prop :ethscription_operation, T.nilable(String) # 'create', 'transfer', 'transfer_with_previous_owner' + MAX_MIMETYPE_LENGTH = 1000 DEPOSIT_TX_TYPE = 0x7D MINT = 0 VALUE = 0 @@ -107,7 +108,7 @@ def self.transfer_multiple_ethscriptions( def function_selector function_signature = case ethscription_operation when 'create' - 'createEthscription((bytes32,bytes32,address,bytes,string,string,string,bool,(string,string,bytes)))' + 'createEthscription((bytes32,bytes32,address,bytes,string,bool,(string,string,bytes)))' when 'transfer' if transfer_ids && transfer_ids.any? 'transferMultipleEthscriptions(bytes32[],address)' @@ -218,9 +219,7 @@ def build_create_calldata # Both input and event-based creates use data URI format # Events are "equivalent of an EOA hex-encoding contentURI and putting it in the calldata" data_uri = DataUri.new(content_uri) - mimetype = data_uri.mimetype.to_s - media_type = mimetype&.split('/')&.first - mime_subtype = mimetype&.split('/')&.last + mimetype = data_uri.mimetype.to_s.first(MAX_MIMETYPE_LENGTH) raw_content = data_uri.decoded_data.b esip6 = DataUri.esip6?(content_uri) || false @@ -249,14 +248,12 @@ def build_create_calldata owner_bin, # address raw_content, # bytes content mimetype.b, # string - media_type.to_s.b, # string - mime_subtype.to_s.b, # string esip6, # bool esip6 protocol_params # ProtocolParams tuple ] encoded = Eth::Abi.encode( - ['(bytes32,bytes32,address,bytes,string,string,string,bool,(string,string,bytes))'], + ['(bytes32,bytes32,address,bytes,string,bool,(string,string,bytes))'], [params] ) diff --git a/contracts/script/L2Genesis.s.sol b/contracts/script/L2Genesis.s.sol index 6232960..57d09d6 100644 --- a/contracts/script/L2Genesis.s.sol +++ b/contracts/script/L2Genesis.s.sol @@ -44,22 +44,23 @@ contract GenesisEthscriptions is Ethscriptions { // Set all values including genesis-specific ones ethscriptions[params.transactionHash] = Ethscription({ - content: ContentInfo({ - contentUriHash: params.contentUriHash, - contentSha: contentSha, - mimetype: params.mimetype, - mediaType: params.mediaType, - mimeSubtype: params.mimeSubtype, - esip6: params.esip6 - }), + // Fixed-size fields + contentUriHash: params.contentUriHash, + contentSha: contentSha, + l1BlockHash: l1BlockHash, + // Packed slot 3 creator: creator, + createdAt: uint48(createdAt), + l1BlockNumber: uint48(l1BlockNumber), + // Dynamic field + mimetype: params.mimetype, + // Packed slot N initialOwner: params.initialOwner, + ethscriptionNumber: uint48(totalSupply()), + esip6: params.esip6, + // Packed slot N+1 previousOwner: creator, - ethscriptionNumber: totalSupply(), - createdAt: createdAt, - l1BlockNumber: l1BlockNumber, - l2BlockNumber: 0, // Genesis ethscriptions have no L2 block - l1BlockHash: l1BlockHash + l2BlockNumber: 0 // Genesis ethscriptions have no L2 block }); // Use ethscription number as token ID @@ -337,8 +338,6 @@ contract L2Genesis is Script { params.initialOwner = initialOwner; params.content = vm.parseJsonBytes(json, string.concat(basePath, ".content")); params.mimetype = vm.parseJsonString(json, string.concat(basePath, ".mimetype")); - params.mediaType = vm.parseJsonString(json, string.concat(basePath, ".media_type")); - params.mimeSubtype = vm.parseJsonString(json, string.concat(basePath, ".mime_subtype")); params.esip6 = vm.parseJsonBool(json, string.concat(basePath, ".esip6")); params.protocolParams = Ethscriptions.ProtocolParams({ protocolName: "", diff --git a/contracts/script/TestTokenUri.s.sol b/contracts/script/TestTokenUri.s.sol index 4349213..76e7406 100644 --- a/contracts/script/TestTokenUri.s.sol +++ b/contracts/script/TestTokenUri.s.sol @@ -22,8 +22,6 @@ contract TestTokenUri is Script { initialOwner: address(0x1111), content: bytes("Hello World!"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams("", "", "") })); @@ -36,8 +34,6 @@ contract TestTokenUri is Script { initialOwner: address(0x2222), content: bytes('{"p":"erc-20","op":"mint","tick":"test","amt":"1000"}'), mimetype: "application/json", - mediaType: "application", - mimeSubtype: "json", esip6: false, protocolParams: Ethscriptions.ProtocolParams("", "", "") })); @@ -50,8 +46,6 @@ contract TestTokenUri is Script { initialOwner: address(0x3333), content: bytes('

Ethscriptions Rule!

'), mimetype: "text/html", - mediaType: "text", - mimeSubtype: "html", esip6: false, protocolParams: Ethscriptions.ProtocolParams("", "", "") })); @@ -65,8 +59,6 @@ contract TestTokenUri is Script { initialOwner: address(0x4444), content: redPixelPng, mimetype: "image/png", - mediaType: "image", - mimeSubtype: "png", esip6: false, protocolParams: Ethscriptions.ProtocolParams("", "", "") })); @@ -79,8 +71,6 @@ contract TestTokenUri is Script { initialOwner: address(0x5555), content: bytes("body { background: #000; color: #0f0; font-family: 'Courier New'; }"), mimetype: "text/css", - mediaType: "text", - mimeSubtype: "css", esip6: false, protocolParams: Ethscriptions.ProtocolParams("", "", "") })); diff --git a/contracts/src/Ethscriptions.sol b/contracts/src/Ethscriptions.sol index dc838f6..562f510 100644 --- a/contracts/src/Ethscriptions.sol +++ b/contracts/src/Ethscriptions.sol @@ -23,25 +23,24 @@ contract Ethscriptions is ERC721EthscriptionsUpgradeable { // STRUCTS // ============================================================= - struct ContentInfo { - bytes32 contentUriHash; // SHA256 of raw content URI string (for protocol uniqueness) - bytes32 contentSha; // SHA256 of decoded raw bytes (for storage reference) - string mimetype; // Full MIME type (e.g., "text/plain") - string mediaType; // e.g., "text", "image" - string mimeSubtype; // e.g., "plain", "png" - bool esip6; - } - struct Ethscription { - ContentInfo content; + // Full slots + bytes32 contentUriHash; + bytes32 contentSha; + bytes32 l1BlockHash; + // Packed slot (32 bytes) address creator; + uint48 createdAt; + uint48 l1BlockNumber; + // Dynamic + string mimetype; + // Packed slot (27 bytes used, 5 free) address initialOwner; + uint48 ethscriptionNumber; + bool esip6; + // Packed slot (26 bytes used, 6 free) address previousOwner; - uint256 ethscriptionNumber; - uint256 createdAt; // Timestamp when created - uint64 l1BlockNumber; // L1 block number when created - uint64 l2BlockNumber; // L2 block number when created - bytes32 l1BlockHash; // L1 block hash when created + uint48 l2BlockNumber; } struct ProtocolParams { @@ -56,8 +55,6 @@ contract Ethscriptions is ERC721EthscriptionsUpgradeable { address initialOwner; bytes content; // Raw decoded bytes (not Base64) string mimetype; - string mediaType; - string mimeSubtype; bool esip6; ProtocolParams protocolParams; // Protocol operation data (optional) } @@ -216,22 +213,18 @@ contract Ethscriptions is ERC721EthscriptionsUpgradeable { bytes32 contentSha = _storeContent(params.content); ethscriptions[params.transactionHash] = Ethscription({ - content: ContentInfo({ - contentUriHash: params.contentUriHash, - contentSha: contentSha, - mimetype: params.mimetype, - mediaType: params.mediaType, - mimeSubtype: params.mimeSubtype, - esip6: params.esip6 - }), + contentUriHash: params.contentUriHash, + contentSha: contentSha, + l1BlockHash: l1Block.hash(), creator: creator, + createdAt: uint48(block.timestamp), + l1BlockNumber: uint48(l1Block.number()), + mimetype: params.mimetype, initialOwner: params.initialOwner, - previousOwner: creator, // Initially same as creator - ethscriptionNumber: totalSupply(), - createdAt: block.timestamp, - l1BlockNumber: l1Block.number(), - l2BlockNumber: uint64(block.number), - l1BlockHash: l1Block.hash() + ethscriptionNumber: uint48(totalSupply()), + esip6: params.esip6, + previousOwner: creator, + l2BlockNumber: uint48(block.number) }); // Use ethscription number as token ID @@ -377,7 +370,7 @@ contract Ethscriptions is ERC721EthscriptionsUpgradeable { /// @notice Get content for an ethscription function getEthscriptionContent(bytes32 txHash) public view requireExists(txHash) returns (bytes memory) { Ethscription storage etsc = ethscriptions[txHash]; - address[] storage pointers = contentPointersBySha[etsc.content.contentSha]; + address[] storage pointers = contentPointersBySha[etsc.contentSha]; // Empty content is valid - returns "" for empty pointers array return pointers.read(); } @@ -388,7 +381,7 @@ contract Ethscriptions is ERC721EthscriptionsUpgradeable { /// @return content The content bytes function getEthscriptionWithContent(bytes32 txHash) external view requireExists(txHash) returns (Ethscription memory ethscription, bytes memory content) { ethscription = ethscriptions[txHash]; - address[] storage pointers = contentPointersBySha[ethscription.content.contentSha]; + address[] storage pointers = contentPointersBySha[ethscription.contentSha]; // Empty content is valid - returns "" for empty pointers array content = pointers.read(); } @@ -620,8 +613,8 @@ contract Ethscriptions is ERC721EthscriptionsUpgradeable { txHash, etsc.creator, etsc.initialOwner, - etsc.content.contentUriHash, - etsc.content.contentSha, + etsc.contentUriHash, + etsc.contentSha, etsc.ethscriptionNumber ); } diff --git a/contracts/src/EthscriptionsProver.sol b/contracts/src/EthscriptionsProver.sol index fce87df..09acd17 100644 --- a/contracts/src/EthscriptionsProver.sol +++ b/contracts/src/EthscriptionsProver.sol @@ -19,10 +19,10 @@ contract EthscriptionsProver { /// @notice Info stored when an ethscription is queued for proving struct QueuedProof { - uint256 l2BlockNumber; - uint256 l2BlockTimestamp; bytes32 l1BlockHash; - uint256 l1BlockNumber; + uint48 l2BlockNumber; + uint48 l2BlockTimestamp; + uint48 l1BlockNumber; } /// @notice Struct for ethscription data proof @@ -30,15 +30,15 @@ contract EthscriptionsProver { bytes32 ethscriptionTxHash; bytes32 contentSha; bytes32 contentUriHash; + bytes32 l1BlockHash; address creator; address currentOwner; address previousOwner; - uint256 ethscriptionNumber; bool esip6; - bytes32 l1BlockHash; - uint256 l1BlockNumber; - uint256 l2BlockNumber; - uint256 l2Timestamp; + uint48 ethscriptionNumber; + uint48 l1BlockNumber; + uint48 l2BlockNumber; + uint48 l2Timestamp; } // ============================================================= @@ -99,10 +99,10 @@ contract EthscriptionsProver { // Capture the L1 block hash and number at the time of queuing L1Block l1Block = L1Block(L1_BLOCK); queuedProofInfo[txHash] = QueuedProof({ - l2BlockNumber: block.number, - l2BlockTimestamp: block.timestamp, l1BlockHash: l1Block.hash(), - l1BlockNumber: l1Block.number() + l2BlockNumber: uint48(block.number), + l2BlockTimestamp: uint48(block.timestamp), + l1BlockNumber: uint48(l1Block.number()) }); } } @@ -143,14 +143,14 @@ contract EthscriptionsProver { // Create proof struct with all ethscription data EthscriptionDataProof memory proof = EthscriptionDataProof({ ethscriptionTxHash: ethscriptionTxHash, - contentSha: etsc.content.contentSha, - contentUriHash: etsc.content.contentUriHash, + contentSha: etsc.contentSha, + contentUriHash: etsc.contentUriHash, + l1BlockHash: proofInfo.l1BlockHash, creator: etsc.creator, currentOwner: currentOwner, previousOwner: etsc.previousOwner, + esip6: etsc.esip6, ethscriptionNumber: etsc.ethscriptionNumber, - esip6: etsc.content.esip6, - l1BlockHash: proofInfo.l1BlockHash, l1BlockNumber: proofInfo.l1BlockNumber, l2BlockNumber: proofInfo.l2BlockNumber, l2Timestamp: proofInfo.l2BlockTimestamp diff --git a/contracts/src/libraries/EthscriptionsRendererLib.sol b/contracts/src/libraries/EthscriptionsRendererLib.sol index b95ed38..f855689 100644 --- a/contracts/src/libraries/EthscriptionsRendererLib.sol +++ b/contracts/src/libraries/EthscriptionsRendererLib.sol @@ -34,18 +34,11 @@ library EthscriptionsRendererLib { string memory part2 = string.concat( '"},{"trait_type":"Content SHA","value":"', - uint256(etsc.content.contentSha).toHexString(), + uint256(etsc.contentSha).toHexString(), '"},{"trait_type":"MIME Type","value":"', - etsc.content.mimetype.escapeJSON(), - '"},{"trait_type":"Media Type","value":"', - etsc.content.mediaType.escapeJSON(), - '"},{"trait_type":"MIME Subtype","value":"', - etsc.content.mimeSubtype.escapeJSON() - ); - - string memory part3 = string.concat( + etsc.mimetype.escapeJSON(), '"},{"trait_type":"ESIP-6","value":"', - etsc.content.esip6 ? "true" : "false", + etsc.esip6 ? "true" : "false", '"},{"trait_type":"L1 Block Number","display_type":"number","value":', uint256(etsc.l1BlockNumber).toString(), '},{"trait_type":"L2 Block Number","display_type":"number","value":', @@ -55,7 +48,7 @@ library EthscriptionsRendererLib { '}]' ); - return string.concat(part1, part2, part3); + return string.concat(part1, part2); } /// @notice Generate the media URI for an ethscription @@ -68,22 +61,22 @@ library EthscriptionsRendererLib { view returns (string memory mediaType, string memory mediaUri) { - if (etsc.content.mimetype.startsWith("image/")) { + if (etsc.mimetype.startsWith("image/")) { // Image content: wrap in SVG for pixel-perfect rendering - string memory imageDataUri = constructDataURI(etsc.content.mimetype, content); + string memory imageDataUri = constructDataURI(etsc.mimetype, content); string memory svg = wrapImageInSVG(imageDataUri); mediaUri = constructDataURI("image/svg+xml", bytes(svg)); return ("image", mediaUri); } else { // Non-image content: use animation_url - if (etsc.content.mimetype.startsWith("video/") || - etsc.content.mimetype.startsWith("audio/") || - etsc.content.mimetype.eq("text/html")) { + if (etsc.mimetype.startsWith("video/") || + etsc.mimetype.startsWith("audio/") || + etsc.mimetype.eq("text/html")) { // Video, audio, and HTML pass through directly as data URIs - mediaUri = constructDataURI(etsc.content.mimetype, content); + mediaUri = constructDataURI(etsc.mimetype, content); } else { // Everything else (text/plain, application/json, etc.) uses the HTML viewer - mediaUri = createTextViewerDataURI(etsc.content.mimetype, content); + mediaUri = createTextViewerDataURI(etsc.mimetype, content); } return ("animation_url", mediaUri); } diff --git a/contracts/test/CollectionsManager.t.sol b/contracts/test/CollectionsManager.t.sol index 0509c44..f8d11bf 100644 --- a/contracts/test/CollectionsManager.t.sol +++ b/contracts/test/CollectionsManager.t.sol @@ -46,8 +46,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes(collectionContent), mimetype: "application/json", - mediaType: "application", - mimeSubtype: "json", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -128,8 +126,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes(itemContent), mimetype: "application/json", - mediaType: "application", - mimeSubtype: "json", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -224,8 +220,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes(removeContent), mimetype: "application/json", - mediaType: "application", - mimeSubtype: "json", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -267,8 +261,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: bob, content: bytes("remove"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -331,8 +323,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: owners[i], content: abi.encodePacked("item", i), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -408,8 +398,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes(imageContent), mimetype: "image/png", - mediaType: "image", - mimeSubtype: "png", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -479,8 +467,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes("edit"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -518,8 +504,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes("item content"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "", @@ -556,8 +540,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes("add"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -587,8 +569,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes("partial-edit"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -632,8 +612,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: bob, content: bytes("bad-edit"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -672,8 +650,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes("no-item"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -723,8 +699,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: charlie, content: bytes("sync"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -772,8 +746,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes("sync-multi"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -804,8 +776,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes("sync-nonexistent"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -832,8 +802,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes("sync-fake"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -862,8 +830,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes("lock"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", @@ -893,8 +859,6 @@ contract CollectionsManagerTest is TestSetup { initialOwner: alice, content: bytes("locked-edit"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", diff --git a/contracts/test/CollectionsProtocol.t.sol b/contracts/test/CollectionsProtocol.t.sol index 424c2e3..ea0d4e4 100644 --- a/contracts/test/CollectionsProtocol.t.sol +++ b/contracts/test/CollectionsProtocol.t.sol @@ -82,8 +82,6 @@ contract CollectionsProtocolTest is TestSetup { initialOwner: alice, content: bytes(json), mimetype: "application/json", - mediaType: "application", - mimeSubtype: "json", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "collections", diff --git a/contracts/test/ERC721Enumerable.t.sol b/contracts/test/ERC721Enumerable.t.sol index 5a89542..2a48ed7 100644 --- a/contracts/test/ERC721Enumerable.t.sol +++ b/contracts/test/ERC721Enumerable.t.sol @@ -23,8 +23,6 @@ contract ERC721EnumerableTest is TestSetup { initialOwner: owner, content: bytes(content), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "", diff --git a/contracts/test/EndToEndCompression.t.sol b/contracts/test/EndToEndCompression.t.sol index c1ea961..89a369c 100644 --- a/contracts/test/EndToEndCompression.t.sol +++ b/contracts/test/EndToEndCompression.t.sol @@ -53,8 +53,6 @@ contract EndToEndCompressionTest is TestSetup { // // initialOwner: owner, // // contentUri: isCompressed ? compressedContent : bytes(originalContent), // // mimetype: "image/png", - // // mediaType: "image", - // // mimeSubtype: "png", // // esip6: false, // // isCompressed: isCompressed, // // protocolParams: Ethscriptions.ProtocolParams({ diff --git a/contracts/test/EthscriptionsCompression.t.sol b/contracts/test/EthscriptionsCompression.t.sol index 9a51d09..b5e6ab4 100644 --- a/contracts/test/EthscriptionsCompression.t.sol +++ b/contracts/test/EthscriptionsCompression.t.sol @@ -94,8 +94,6 @@ contract EthscriptionsCompressionTest is TestSetup { // initialOwner: owner, // contentUri: originalContent, // mimetype: "image/png", - // mediaType: "image", - // mimeSubtype: "png", // esip6: false, // isCompressed: false, // protocolParams: Ethscriptions.ProtocolParams({ @@ -112,8 +110,6 @@ contract EthscriptionsCompressionTest is TestSetup { // initialOwner: owner, // contentUri: compressedContent, // mimetype: "image/png", - // mediaType: "image", - // mimeSubtype: "png", // esip6: false, // isCompressed: true, // protocolParams: Ethscriptions.ProtocolParams({ diff --git a/contracts/test/EthscriptionsFailureHandling.t.sol b/contracts/test/EthscriptionsFailureHandling.t.sol index 7a941f3..c693dc9 100644 --- a/contracts/test/EthscriptionsFailureHandling.t.sol +++ b/contracts/test/EthscriptionsFailureHandling.t.sol @@ -104,8 +104,6 @@ contract EthscriptionsFailureHandlingTest is TestSetup { initialOwner: address(this), content: bytes("Hello World with failing token manager"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "test", @@ -188,8 +186,6 @@ contract EthscriptionsFailureHandlingTest is TestSetup { initialOwner: address(this), content: bytes("{\"p\":\"test\",\"op\":\"deploy\",\"tick\":\"FAIL\",\"max\":\"1000\",\"lim\":\"10\"}"), mimetype: "application/json", - mediaType: "application", - mimeSubtype: "json", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "test", diff --git a/contracts/test/EthscriptionsMultiTransfer.t.sol b/contracts/test/EthscriptionsMultiTransfer.t.sol index 209ed0d..5928b9b 100644 --- a/contracts/test/EthscriptionsMultiTransfer.t.sol +++ b/contracts/test/EthscriptionsMultiTransfer.t.sol @@ -226,15 +226,13 @@ contract EthscriptionsMultiTransferTest is TestSetup { string memory json = string(decodedJson); // Check all expected attributes - string[12] memory expectedTraits = [ + string[10] memory expectedTraits = [ "Transaction Hash", "Ethscription Number", "Creator", "Initial Owner", "Content SHA", "MIME Type", - "Media Type", - "MIME Subtype", "ESIP-6", "L1 Block Number", "L2 Block Number", diff --git a/contracts/test/EthscriptionsProver.t.sol b/contracts/test/EthscriptionsProver.t.sol index bc33f26..aa485e4 100644 --- a/contracts/test/EthscriptionsProver.t.sol +++ b/contracts/test/EthscriptionsProver.t.sol @@ -107,8 +107,6 @@ contract EthscriptionsProverTest is TestSetup { initialOwner: alice, content: bytes("test1"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams("", "", "") }) @@ -123,8 +121,6 @@ contract EthscriptionsProverTest is TestSetup { initialOwner: bob, content: bytes("test2"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams("", "", "") }) @@ -145,8 +141,6 @@ contract EthscriptionsProverTest is TestSetup { initialOwner: charlie, content: bytes("test3"), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams("", "", "") }) diff --git a/contracts/test/EthscriptionsToken.t.sol b/contracts/test/EthscriptionsToken.t.sol index 49d3659..dae131c 100644 --- a/contracts/test/EthscriptionsToken.t.sol +++ b/contracts/test/EthscriptionsToken.t.sol @@ -51,8 +51,6 @@ contract EthscriptionsTokenTest is TestSetup { initialOwner: initialOwner, content: content, mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: protocol, diff --git a/contracts/test/EthscriptionsTokenParams.t.sol b/contracts/test/EthscriptionsTokenParams.t.sol index 27d6fcf..fb54a78 100644 --- a/contracts/test/EthscriptionsTokenParams.t.sol +++ b/contracts/test/EthscriptionsTokenParams.t.sol @@ -24,8 +24,6 @@ contract EthscriptionsTokenParamsTest is TestSetup { initialOwner: address(this), content: bytes(tokenJson), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "erc-20", @@ -64,8 +62,6 @@ contract EthscriptionsTokenParamsTest is TestSetup { initialOwner: address(this), content: bytes(tokenJson), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "erc-20", @@ -93,8 +89,6 @@ contract EthscriptionsTokenParamsTest is TestSetup { initialOwner: address(this), content: bytes(content), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "", @@ -128,8 +122,6 @@ contract EthscriptionsTokenParamsTest is TestSetup { initialOwner: address(this), content: bytes(deployJson), mimetype: "application/json", - mediaType: "application", - mimeSubtype: "json", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "erc-20", @@ -156,8 +148,6 @@ contract EthscriptionsTokenParamsTest is TestSetup { initialOwner: address(this), content: bytes(mintJson), mimetype: "application/json", - mediaType: "application", - mimeSubtype: "json", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "erc-20", diff --git a/contracts/test/EthscriptionsWithContent.t.sol b/contracts/test/EthscriptionsWithContent.t.sol index 573e2ea..59de4e4 100644 --- a/contracts/test/EthscriptionsWithContent.t.sol +++ b/contracts/test/EthscriptionsWithContent.t.sol @@ -20,8 +20,6 @@ contract EthscriptionsWithContentTest is TestSetup { initialOwner: initialOwner, content: bytes(testContent), mimetype: "text/plain", - mediaType: "text", - mimeSubtype: "plain", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "", @@ -40,10 +38,8 @@ contract EthscriptionsWithContentTest is TestSetup { assertEq(ethscription.initialOwner, initialOwner); assertEq(ethscription.previousOwner, creator); assertEq(ethscription.ethscriptionNumber, tokenId); - assertEq(ethscription.content.mimetype, "text/plain"); - assertEq(ethscription.content.mediaType, "text"); - assertEq(ethscription.content.mimeSubtype, "plain"); - assertEq(ethscription.content.esip6, false); + assertEq(ethscription.mimetype, "text/plain"); + assertEq(ethscription.esip6, false); // Verify content assertEq(content, bytes(testContent)); @@ -62,13 +58,11 @@ contract EthscriptionsWithContentTest is TestSetup { assertEq(ethscription.l2BlockNumber, ethscriptionSeparate.l2BlockNumber); assertEq(ethscription.l1BlockHash, ethscriptionSeparate.l1BlockHash); - // Compare content info - assertEq(ethscription.content.contentUriHash, ethscriptionSeparate.content.contentUriHash); - assertEq(ethscription.content.contentSha, ethscriptionSeparate.content.contentSha); - assertEq(ethscription.content.mimetype, ethscriptionSeparate.content.mimetype); - assertEq(ethscription.content.mediaType, ethscriptionSeparate.content.mediaType); - assertEq(ethscription.content.mimeSubtype, ethscriptionSeparate.content.mimeSubtype); - assertEq(ethscription.content.esip6, ethscriptionSeparate.content.esip6); + // Compare content fields + assertEq(ethscription.contentUriHash, ethscriptionSeparate.contentUriHash); + assertEq(ethscription.contentSha, ethscriptionSeparate.contentSha); + assertEq(ethscription.mimetype, ethscriptionSeparate.mimetype); + assertEq(ethscription.esip6, ethscriptionSeparate.esip6); // Compare content assertEq(content, contentSeparate); @@ -102,8 +96,6 @@ contract EthscriptionsWithContentTest is TestSetup { initialOwner: initialOwner, content: largeContent, mimetype: "application/octet-stream", - mediaType: "application", - mimeSubtype: "octet-stream", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "", diff --git a/contracts/test/EthscriptionsWithTestFunctions.sol b/contracts/test/EthscriptionsWithTestFunctions.sol index 79edec6..92c1fa2 100644 --- a/contracts/test/EthscriptionsWithTestFunctions.sol +++ b/contracts/test/EthscriptionsWithTestFunctions.sol @@ -14,14 +14,14 @@ contract EthscriptionsWithTestFunctions is Ethscriptions { /// @dev Test-only function to inspect storage chunks function getContentPointerCount(bytes32 transactionHash) external view requireExists(transactionHash) returns (uint256) { Ethscription storage etsc = ethscriptions[transactionHash]; - return contentPointersBySha[etsc.content.contentSha].length; + return contentPointersBySha[etsc.contentSha].length; } /// @notice Get all content pointers for an ethscription /// @dev Test-only function to inspect SSTORE2 addresses function getContentPointers(bytes32 transactionHash) external view requireExists(transactionHash) returns (address[] memory) { Ethscription storage etsc = ethscriptions[transactionHash]; - return contentPointersBySha[etsc.content.contentSha]; + return contentPointersBySha[etsc.contentSha]; } /// @notice Read a specific chunk of content @@ -31,7 +31,7 @@ contract EthscriptionsWithTestFunctions is Ethscriptions { /// @return The chunk data function readChunk(bytes32 transactionHash, uint256 index) external view requireExists(transactionHash) returns (bytes memory) { Ethscription storage etsc = ethscriptions[transactionHash]; - address[] storage pointers = contentPointersBySha[etsc.content.contentSha]; + address[] storage pointers = contentPointersBySha[etsc.contentSha]; require(index < pointers.length, "Chunk index out of bounds"); return SSTORE2.read(pointers[index]); } diff --git a/contracts/test/GasDebug.t.sol b/contracts/test/GasDebug.t.sol index d62af1a..7f587bd 100644 --- a/contracts/test/GasDebug.t.sol +++ b/contracts/test/GasDebug.t.sol @@ -41,7 +41,7 @@ contract GasDebugTest is TestSetup { Ethscriptions.Ethscription memory etsc = ethscriptions.getEthscription(params.transactionHash); assertEq(etsc.creator, INITIAL_OWNER); assertEq(etsc.initialOwner, INITIAL_OWNER); - assertEq(etsc.content.mimetype, "image/png"); + assertEq(etsc.mimetype, "image/png"); } catch Error(string memory reason) { console.log("Failed with reason:", reason); revert(reason); diff --git a/contracts/test/ProtocolRegistration.t.sol b/contracts/test/ProtocolRegistration.t.sol index 7b36a22..f284887 100644 --- a/contracts/test/ProtocolRegistration.t.sol +++ b/contracts/test/ProtocolRegistration.t.sol @@ -117,8 +117,6 @@ contract ProtocolRegistrationTest is TestSetup { initialOwner: alice, content: bytes('{"p":"mock-protocol","op":"test"}'), mimetype: "application/json", - mediaType: "application", - mimeSubtype: "json", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "mock-protocol", @@ -163,8 +161,6 @@ contract ProtocolRegistrationTest is TestSetup { initialOwner: alice, content: bytes('{"p":"unregistered","op":"test"}'), mimetype: "application/json", - mediaType: "application", - mimeSubtype: "json", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "unregistered", diff --git a/contracts/test/TestImageSVGWrapper.t.sol b/contracts/test/TestImageSVGWrapper.t.sol index e0543af..da15309 100644 --- a/contracts/test/TestImageSVGWrapper.t.sol +++ b/contracts/test/TestImageSVGWrapper.t.sol @@ -22,8 +22,6 @@ contract TestImageSVGWrapper is TestSetup { initialOwner: alice, content: pngContent, mimetype: "image/png", - mediaType: "image", - mimeSubtype: "png", esip6: false, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "", diff --git a/contracts/test/TestSetup.sol b/contracts/test/TestSetup.sol index 0daeff4..63749fc 100644 --- a/contracts/test/TestSetup.sol +++ b/contracts/test/TestSetup.sol @@ -69,8 +69,6 @@ abstract contract TestSetup is Test { // Simple parsing for tests bytes memory content; string memory mimetype = "text/plain"; - string memory mediaType = "text"; - string memory mimeSubtype = "plain"; bool isBase64 = false; // Check if data URI and parse @@ -125,22 +123,6 @@ abstract contract TestSetup is Test { for (uint256 i = 0; i < mimeEnd - 5; i++) { bytes(mimetype)[i] = contentUriBytes[5 + i]; } - - // Parse media type and subtype - bytes memory mimetypeBytes = bytes(mimetype); - for (uint256 i = 0; i < mimetypeBytes.length; i++) { - if (mimetypeBytes[i] == '/') { - mediaType = string(new bytes(i)); - for (uint256 j = 0; j < i; j++) { - bytes(mediaType)[j] = mimetypeBytes[j]; - } - mimeSubtype = string(new bytes(mimetypeBytes.length - i - 1)); - for (uint256 j = 0; j < mimetypeBytes.length - i - 1; j++) { - bytes(mimeSubtype)[j] = mimetypeBytes[i + 1 + j]; - } - break; - } - } } } } @@ -154,8 +136,6 @@ abstract contract TestSetup is Test { initialOwner: initialOwner, content: content, mimetype: mimetype, - mediaType: mediaType, - mimeSubtype: mimeSubtype, esip6: esip6, protocolParams: Ethscriptions.ProtocolParams({ protocolName: "", diff --git a/lib/block_validator.rb b/lib/block_validator.rb index 4311a6e..8c26672 100644 --- a/lib/block_validator.rb +++ b/lib/block_validator.rb @@ -465,19 +465,8 @@ def verify_ethscription_storage(creation, l1_block_num, block_tag) @errors << "Storage mimetype mismatch for #{tx_hash}: stored=#{stored[:mimetype]}, expected=#{creation[:mimetype]}" end - # Verify media_type - normalize to binary for comparison (with instrumentation) - media_type_match = binary_equal?(stored[:media_type], creation[:media_type]) - record_comparison("storage_media_type_check", tx_hash, creation[:media_type], stored[:media_type], media_type_match, { l1_block: l1_block_num }) - if !media_type_match - @errors << "Storage media_type mismatch for #{tx_hash}: stored=#{stored[:media_type]}, expected=#{creation[:media_type]}" - end - - # Verify mime_subtype - normalize to binary for comparison (with instrumentation) - mime_subtype_match = binary_equal?(stored[:mime_subtype], creation[:mime_subtype]) - record_comparison("storage_mime_subtype_check", tx_hash, creation[:mime_subtype], stored[:mime_subtype], mime_subtype_match, { l1_block: l1_block_num }) - if !mime_subtype_match - @errors << "Storage mime_subtype mismatch for #{tx_hash}: stored=#{stored[:mime_subtype]}, expected=#{creation[:mime_subtype]}" - end + # Note: media_type and mime_subtype are no longer stored in the contract + # They are derived from mimetype in StorageReader for backward compatibility # Verify esip6 flag - must match exactly (with instrumentation) esip6_match = stored[:esip6] == creation[:esip6] diff --git a/lib/storage_reader.rb b/lib/storage_reader.rb index edb0f46..dc52ebf 100644 --- a/lib/storage_reader.rb +++ b/lib/storage_reader.rb @@ -1,31 +1,21 @@ class StorageReader ETHSCRIPTIONS_ADDRESS = SysConfig::ETHSCRIPTIONS_ADDRESS.to_hex - # Define the nested ContentInfo struct - CONTENT_INFO_STRUCT = { + # Define the flattened Ethscription struct ABI + ETHSCRIPTION_STRUCT_ABI = { 'components' => [ { 'name' => 'contentUriHash', 'type' => 'bytes32' }, { 'name' => 'contentSha', 'type' => 'bytes32' }, - { 'name' => 'mimetype', 'type' => 'string' }, - { 'name' => 'mediaType', 'type' => 'string' }, - { 'name' => 'mimeSubtype', 'type' => 'string' }, - { 'name' => 'esip6', 'type' => 'bool' } - ], - 'type' => 'tuple' - } - - # Define the Ethscription struct ABI with nested ContentInfo - ETHSCRIPTION_STRUCT_ABI = { - 'components' => [ - CONTENT_INFO_STRUCT, # ContentInfo content + { 'name' => 'l1BlockHash', 'type' => 'bytes32' }, { 'name' => 'creator', 'type' => 'address' }, + { 'name' => 'createdAt', 'type' => 'uint48' }, + { 'name' => 'l1BlockNumber', 'type' => 'uint48' }, + { 'name' => 'mimetype', 'type' => 'string' }, { 'name' => 'initialOwner', 'type' => 'address' }, + { 'name' => 'ethscriptionNumber', 'type' => 'uint48' }, + { 'name' => 'esip6', 'type' => 'bool' }, { 'name' => 'previousOwner', 'type' => 'address' }, - { 'name' => 'ethscriptionNumber', 'type' => 'uint256' }, - { 'name' => 'createdAt', 'type' => 'uint256' }, - { 'name' => 'l1BlockNumber', 'type' => 'uint64' }, - { 'name' => 'l2BlockNumber', 'type' => 'uint64' }, - { 'name' => 'l1BlockHash', 'type' => 'bytes32' } + { 'name' => 'l2BlockNumber', 'type' => 'uint48' } ], 'type' => 'tuple' } @@ -108,32 +98,35 @@ def get_ethscription_with_content(tx_hash, block_tag: 'latest') raise StandardError, "RPC call failed for ethscription #{tx_hash}" if result.nil? # Decode the tuple: (Ethscription, bytes) - types = ['((bytes32,bytes32,string,string,string,bool),address,address,address,uint256,uint256,uint64,uint64,bytes32)', 'bytes'] + types = ['(bytes32,bytes32,bytes32,address,uint48,uint48,string,address,uint48,bool,address,uint48)', 'bytes'] decoded = Eth::Abi.decode(types, result) # Extract ethscription struct and content ethscription_data = decoded[0] content_data = decoded[1] - content_info = ethscription_data[0] # Nested ContentInfo struct + + # Derive media_type and mime_subtype from mimetype + mimetype = ethscription_data[6] # mimetype is at index 6 + media_type, mime_subtype = mimetype.split('/', 2) if mimetype { - # ContentInfo fields - content_uri_hash: '0x' + content_info[0].unpack1('H*'), - content_sha: '0x' + content_info[1].unpack1('H*'), - mimetype: content_info[2], - media_type: content_info[3], - mime_subtype: content_info[4], - esip6: content_info[5], - - # Main Ethscription fields - creator: Eth::Address.new(ethscription_data[1]).to_s, - initial_owner: Eth::Address.new(ethscription_data[2]).to_s, - previous_owner: Eth::Address.new(ethscription_data[3]).to_s, - ethscription_number: ethscription_data[4], - created_at: ethscription_data[5], - l1_block_number: ethscription_data[6], - l2_block_number: ethscription_data[7], - l1_block_hash: '0x' + ethscription_data[8].unpack1('H*'), + # Content fields + content_uri_hash: '0x' + ethscription_data[0].unpack1('H*'), + content_sha: '0x' + ethscription_data[1].unpack1('H*'), + mimetype: mimetype, + media_type: media_type, + mime_subtype: mime_subtype, + esip6: ethscription_data[9], # bool at index 9 + + # Main fields + creator: Eth::Address.new(ethscription_data[3]).to_s, + initial_owner: Eth::Address.new(ethscription_data[7]).to_s, + previous_owner: Eth::Address.new(ethscription_data[10]).to_s, + ethscription_number: ethscription_data[8], + created_at: ethscription_data[4], + l1_block_number: ethscription_data[5], + l2_block_number: ethscription_data[11], + l1_block_hash: '0x' + ethscription_data[2].unpack1('H*'), # Content content: content_data @@ -161,33 +154,35 @@ def get_ethscription(tx_hash, block_tag: 'latest') # Nil indicates an RPC/network failure raise StandardError, "RPC call failed for ethscription #{tx_hash}" if result.nil? - # Decode using Eth::Abi - # Updated types for nested struct: ContentInfo is a tuple within the main tuple - types = ['((bytes32,bytes32,string,string,string,bool),address,address,address,uint256,uint256,uint64,uint64,bytes32)'] + # Decode using Eth::Abi - flattened struct + types = ['(bytes32,bytes32,bytes32,address,uint48,uint48,string,address,uint48,bool,address,uint48)'] decoded = Eth::Abi.decode(types, result) - # The struct is returned as an array within an array + # The struct is returned as an array ethscription_data = decoded[0] - content_info = ethscription_data[0] # Nested ContentInfo struct + + # Derive media_type and mime_subtype from mimetype + mimetype = ethscription_data[6] # mimetype is at index 6 + media_type, mime_subtype = mimetype.split('/', 2) if mimetype { - # ContentInfo fields - content_uri_hash: '0x' + content_info[0].unpack1('H*'), - content_sha: '0x' + content_info[1].unpack1('H*'), - mimetype: content_info[2], - media_type: content_info[3], - mime_subtype: content_info[4], - esip6: content_info[5], - - # Main Ethscription fields - creator: Eth::Address.new(ethscription_data[1]).to_s, - initial_owner: Eth::Address.new(ethscription_data[2]).to_s, - previous_owner: Eth::Address.new(ethscription_data[3]).to_s, - ethscription_number: ethscription_data[4], - created_at: ethscription_data[5], - l1_block_number: ethscription_data[6], - l2_block_number: ethscription_data[7], - l1_block_hash: '0x' + ethscription_data[8].unpack1('H*') + # Content fields + content_uri_hash: '0x' + ethscription_data[0].unpack1('H*'), + content_sha: '0x' + ethscription_data[1].unpack1('H*'), + mimetype: mimetype, + media_type: media_type, + mime_subtype: mime_subtype, + esip6: ethscription_data[9], # bool at index 9 + + # Main fields + creator: Eth::Address.new(ethscription_data[3]).to_s, + initial_owner: Eth::Address.new(ethscription_data[7]).to_s, + previous_owner: Eth::Address.new(ethscription_data[10]).to_s, + ethscription_number: ethscription_data[8], + created_at: ethscription_data[4], + l1_block_number: ethscription_data[5], + l2_block_number: ethscription_data[11], + l1_block_hash: '0x' + ethscription_data[2].unpack1('H*') } rescue EthRpcClient::ExecutionRevertedError => e # Contract reverted - ethscription doesn't exist From 33c88910a77f9066325455ecf34f0fa88f724bc9 Mon Sep 17 00:00:00 2001 From: Tom Lehman Date: Fri, 17 Oct 2025 15:16:25 -0400 Subject: [PATCH 2/2] Simplify --- lib/storage_reader.rb | 16 ++-------------- spec/integration/ethscriptions_creation_spec.rb | 4 ---- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/lib/storage_reader.rb b/lib/storage_reader.rb index dc52ebf..32bac3d 100644 --- a/lib/storage_reader.rb +++ b/lib/storage_reader.rb @@ -105,17 +105,11 @@ def get_ethscription_with_content(tx_hash, block_tag: 'latest') ethscription_data = decoded[0] content_data = decoded[1] - # Derive media_type and mime_subtype from mimetype - mimetype = ethscription_data[6] # mimetype is at index 6 - media_type, mime_subtype = mimetype.split('/', 2) if mimetype - { # Content fields content_uri_hash: '0x' + ethscription_data[0].unpack1('H*'), content_sha: '0x' + ethscription_data[1].unpack1('H*'), - mimetype: mimetype, - media_type: media_type, - mime_subtype: mime_subtype, + mimetype: ethscription_data[6], # mimetype at index 6 esip6: ethscription_data[9], # bool at index 9 # Main fields @@ -161,17 +155,11 @@ def get_ethscription(tx_hash, block_tag: 'latest') # The struct is returned as an array ethscription_data = decoded[0] - # Derive media_type and mime_subtype from mimetype - mimetype = ethscription_data[6] # mimetype is at index 6 - media_type, mime_subtype = mimetype.split('/', 2) if mimetype - { # Content fields content_uri_hash: '0x' + ethscription_data[0].unpack1('H*'), content_sha: '0x' + ethscription_data[1].unpack1('H*'), - mimetype: mimetype, - media_type: media_type, - mime_subtype: mime_subtype, + mimetype: ethscription_data[6], # mimetype at index 6 esip6: ethscription_data[9], # bool at index 9 # Main fields diff --git a/spec/integration/ethscriptions_creation_spec.rb b/spec/integration/ethscriptions_creation_spec.rb index 4a7bb33..691b82e 100644 --- a/spec/integration/ethscriptions_creation_spec.rb +++ b/spec/integration/ethscriptions_creation_spec.rb @@ -530,8 +530,6 @@ # Verify content fields expect(stored[:content]).to eq("test") expect(stored[:mimetype]).to eq("image/svg+xml") - expect(stored[:media_type]).to eq("image") - expect(stored[:mime_subtype]).to eq("svg+xml") # Verify content URI hash expected_hash = Digest::SHA256.hexdigest(content_uri) @@ -553,8 +551,6 @@ stored = get_ethscription_content(results[:ethscription_ids].first) expect(stored[:mimetype]).to eq("application/json") - expect(stored[:media_type]).to eq("application") - expect(stored[:mime_subtype]).to eq("json") # Verify content URI hash matches expected_hash = Digest::SHA256.hexdigest(content_uri)