Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/models/ethscription_transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ def build_create_calldata
)

# Hash the content for protocol uniqueness
content_uri_hash_hex = Digest::SHA256.hexdigest(content_uri)
content_uri_hash = [content_uri_hash_hex].pack('H*')
content_uri_sha_hex = Digest::SHA256.hexdigest(content_uri)
content_uri_sha = [content_uri_sha_hex].pack('H*')

# Convert hex strings to binary for ABI encoding
tx_hash_bin = hex_to_bin(eth_transaction.transaction_hash)
Expand All @@ -204,7 +204,7 @@ def build_create_calldata
# Encode parameters
params = [
tx_hash_bin, # bytes32 ethscriptionId (L1 tx hash)
content_uri_hash, # bytes32 contentUriHash
content_uri_sha, # bytes32 contentUriHash
owner_bin, # address
raw_content, # bytes content
mimetype.b, # string
Expand Down
28 changes: 19 additions & 9 deletions contracts/script/L2Genesis.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {L2GenesisConfig} from "./L2GenesisConfig.sol";
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 "forge-std/console.sol";

/// @title GenesisEthscriptions
Expand All @@ -32,28 +33,37 @@ contract GenesisEthscriptions is Ethscriptions {
require(ethscriptions[params.ethscriptionId].creator == address(0), "Ethscription already exists");

// Check protocol uniqueness using content URI hash
if (firstEthscriptionByContentUri[params.contentUriHash] != bytes32(0)) {
if (firstEthscriptionByContentUri[params.contentUriSha] != bytes32(0)) {
if (!params.esip6) revert DuplicateContentUri();
}

// Store content and get content SHA (reusing parent's helper)
bytes32 contentSha = _storeContent(params.content);
// Store content and get content hash (reusing parent's helper)
bytes32 contentHash = _storeContent(params.content);

// Mark content URI as used by storing this ethscription's tx hash
firstEthscriptionByContentUri[params.contentUriHash] = params.ethscriptionId;
firstEthscriptionByContentUri[params.contentUriSha] = params.ethscriptionId;

// Store metadata (mimetype, protocol, operation)
// Genesis ethscriptions have no protocol parameters
bytes32 metaRef = MetaStoreLib.store(
params.mimetype,
params.protocolParams.protocolName,
params.protocolParams.operation,
metadataStorage
);

// Set all values including genesis-specific ones
ethscriptions[params.ethscriptionId] = EthscriptionStorage({
// Fixed-size fields
contentUriHash: params.contentUriHash,
contentSha: contentSha,
contentUriSha: params.contentUriSha,
contentHash: contentHash,
l1BlockHash: l1BlockHash,
// Packed slot 3
creator: creator,
createdAt: uint48(createdAt),
l1BlockNumber: uint48(l1BlockNumber),
// Dynamic field
mimetype: params.mimetype,
// Metadata reference
metaRef: metaRef,
// Packed slot N
initialOwner: params.initialOwner,
ethscriptionNumber: uint48(totalSupply()),
Expand Down Expand Up @@ -333,7 +343,7 @@ contract L2Genesis is Script {
// The JSON already has all the properly processed data
Ethscriptions.CreateEthscriptionParams memory params;
params.ethscriptionId = vm.parseJsonBytes32(json, string.concat(basePath, ".transaction_hash"));
params.contentUriHash = vm.parseJsonBytes32(json, string.concat(basePath, ".content_uri_hash"));
params.contentUriSha = vm.parseJsonBytes32(json, string.concat(basePath, ".content_uri_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 JSON field name is incorrect. The Ruby script process_genesis_json.rb writes the field as content_uri_sha (line 17), but this code is trying to read from content_uri_hash. This will cause the genesis script to fail. Change .content_uri_hash to .content_uri_sha to match the JSON output.

Suggested change
params.contentUriSha = vm.parseJsonBytes32(json, string.concat(basePath, ".content_uri_hash"));
params.contentUriSha = vm.parseJsonBytes32(json, string.concat(basePath, ".content_uri_sha"));

Copilot uses AI. Check for mistakes.
params.initialOwner = initialOwner;
params.content = vm.parseJsonBytes(json, string.concat(basePath, ".content"));
params.mimetype = vm.parseJsonString(json, string.concat(basePath, ".mimetype"));
Expand Down
10 changes: 5 additions & 5 deletions contracts/script/TestTokenUri.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ contract TestTokenUri is Script {
vm.prank(address(0x1111));
eth.createEthscription(Ethscriptions.CreateEthscriptionParams({
ethscriptionId: keccak256("text1"),
contentUriHash: keccak256("data:text/plain,Hello World!"),
contentUriSha: keccak256("data:text/plain,Hello World!"),
initialOwner: address(0x1111),
content: bytes("Hello World!"),
mimetype: "text/plain",
Expand All @@ -30,7 +30,7 @@ contract TestTokenUri is Script {
vm.prank(address(0x2222));
eth.createEthscription(Ethscriptions.CreateEthscriptionParams({
ethscriptionId: keccak256("json1"),
contentUriHash: keccak256('data:application/json,{"p":"erc-20","op":"mint","tick":"test","amt":"1000"}'),
contentUriSha: keccak256('data:application/json,{"p":"erc-20","op":"mint","tick":"test","amt":"1000"}'),
initialOwner: address(0x2222),
content: bytes('{"p":"erc-20","op":"mint","tick":"test","amt":"1000"}'),
mimetype: "application/json",
Expand All @@ -42,7 +42,7 @@ contract TestTokenUri is Script {
vm.prank(address(0x3333));
eth.createEthscription(Ethscriptions.CreateEthscriptionParams({
ethscriptionId: keccak256("html1"),
contentUriHash: keccak256('data:text/html,<html><body style="background:linear-gradient(45deg,#ff006e,#8338ec);color:white;font-family:monospace;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><h1>Ethscriptions Rule!</h1></body></html>'),
contentUriSha: keccak256('data:text/html,<html><body style="background:linear-gradient(45deg,#ff006e,#8338ec);color:white;font-family:monospace;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><h1>Ethscriptions Rule!</h1></body></html>'),
initialOwner: address(0x3333),
content: bytes('<html><body style="background:linear-gradient(45deg,#ff006e,#8338ec);color:white;font-family:monospace;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><h1>Ethscriptions Rule!</h1></body></html>'),
mimetype: "text/html",
Expand All @@ -55,7 +55,7 @@ contract TestTokenUri is Script {
vm.prank(address(0x4444));
eth.createEthscription(Ethscriptions.CreateEthscriptionParams({
ethscriptionId: keccak256("image1"),
contentUriHash: keccak256("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAm0lEQVR42mNgGITgPxTTxvBleTo0swBsOK0s+N8aJkczC1AMR7KAKpb8v72xAY5hFsD4lFoCN+j56ZUoliBbSoklGIZjwxRbQAjT1YK7d+82kGUBeuQii5FrAYYrL81NwCpGFQtoEUT/6RoHWAyknQV0S6ZI5RE6Jt8CZIOOHTuGgR9Fq5FkCf19QM3wx5rZKHEtsRZQt5qkhgUAR6cGaUehOD4AAAAASUVORK5CYII="),
contentUriSha: keccak256("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAm0lEQVR42mNgGITgPxTTxvBleTo0swBsOK0s+N8aJkczC1AMR7KAKpb8v72xAY5hFsD4lFoCN+j56ZUoliBbSoklGIZjwxRbQAjT1YK7d+82kGUBeuQii5FrAYYrL81NwCpGFQtoEUT/6RoHWAyknQV0S6ZI5RE6Jt8CZIOOHTuGgR9Fq5FkCf19QM3wx5rZKHEtsRZQt5qkhgUAR6cGaUehOD4AAAAASUVORK5CYII="),
initialOwner: address(0x4444),
content: redPixelPng,
mimetype: "image/png",
Expand All @@ -67,7 +67,7 @@ contract TestTokenUri is Script {
vm.prank(address(0x5555));
eth.createEthscription(Ethscriptions.CreateEthscriptionParams({
ethscriptionId: keccak256("css1"),
contentUriHash: keccak256("data:text/css,body { background: #000; color: #0f0; font-family: 'Courier New'; }"),
contentUriSha: keccak256("data:text/css,body { background: #000; color: #0f0; font-family: 'Courier New'; }"),
initialOwner: address(0x5555),
content: bytes("body { background: #000; color: #0f0; font-family: 'Courier New'; }"),
mimetype: "text/css",
Expand Down
6 changes: 3 additions & 3 deletions contracts/script/process_genesis_json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
content_uri = ethscription['content_uri']

# Calculate content URI hash (as hex string with 0x prefix for JSON)
content_uri_hash = '0x' + Digest::SHA256.hexdigest(content_uri)
ethscription['content_uri_hash'] = content_uri_hash
content_uri_sha = '0x' + Digest::SHA256.hexdigest(content_uri)
ethscription['content_uri_sha'] = content_uri_sha

# Parse the data URI
if content_uri.start_with?('data:')
Expand Down Expand Up @@ -56,4 +56,4 @@
File.write(json_path, JSON.pretty_generate(data))

puts "Processed #{data['ethscriptions'].length} ethscriptions"
puts "Added content_uri_hash and content fields"
puts "Added content_uri_sha and content fields"
Loading
Loading