From 4cf3d5ad1fa65efd201fd09c56a9014ba112e809 Mon Sep 17 00:00:00 2001 From: tunnckoCore <5038030+tunnckoCore@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:06:21 +0300 Subject: [PATCH] feat: add additional contract creation way Signed-off-by: tunnckoCore <5038030+tunnckoCore@users.noreply.github.com> --- app/models/eth_transaction.rb | 136 ++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 64 deletions(-) diff --git a/app/models/eth_transaction.rb b/app/models/eth_transaction.rb index 4b848c1..2428586 100644 --- a/app/models/eth_transaction.rb +++ b/app/models/eth_transaction.rb @@ -1,6 +1,6 @@ class EthTransaction < ApplicationRecord class HowDidWeGetHereError < StandardError; end - + belongs_to :eth_block, foreign_key: :block_number, primary_key: :block_number, optional: true, inverse_of: :eth_transactions has_one :ethscription, foreign_key: :transaction_hash, primary_key: :transaction_hash, @@ -14,47 +14,48 @@ class HowDidWeGetHereError < StandardError; end def block_blob_sidecars @block_blob_sidecars ||= eth_block.ensure_blob_sidecars end - + scope :newest_first, -> { order(block_number: :desc, transaction_index: :desc) } scope :oldest_first, -> { order(block_number: :asc, transaction_index: :asc) } - + scope :with_blobs, -> { where("blob_versioned_hashes != '[]'::jsonb") } scope :without_blobs, -> { where("blob_versioned_hashes = '[]'::jsonb") } - + def has_blob? blob_versioned_hashes.present? end - + def self.event_signature(event_name) "0x" + Digest::Keccak256.hexdigest(event_name) end - + CreateEthscriptionEventSig = event_signature("ethscriptions_protocol_CreateEthscription(address,string)") + CreateEthscriptionFromEventSig = event_signature("ethscriptions_protocol_CreateEthscriptionFrom(address,string)") Esip2EventSig = event_signature("ethscriptions_protocol_TransferEthscriptionForPreviousOwner(address,address,bytes32)") Esip1EventSig = event_signature("ethscriptions_protocol_TransferEthscription(address,bytes32)") - + def possibly_relevant? status != 0 && (possibly_creates_ethscription? || possibly_transfers_ethscription?) end - + def possibly_creates_ethscription? (DataUri.valid?(utf8_input) && to_address.present?) || ethscription_creation_events.present? end - + def possibly_transfers_ethscription? transfers_ethscription_via_input? || ethscription_transfer_events.present? end - + def utf8_input HexDataProcessor.hex_to_utf8( input, support_gzip: EthTransaction.esip7_enabled?(block_number) ) end - + def ethscription_attrs { transaction_hash: transaction_hash, @@ -71,20 +72,20 @@ def ethscription_attrs def process! self.transfer_index = 0 - + create_ethscription_from_input! create_ethscription_from_events! create_ethscription_transfers_from_input! create_ethscription_transfers_from_events! end - + def blob_from_version_hash(version_hash) block_blob_sidecars.find do |blob| kzg_commitment = blob["kzg_commitment"].sub(/\A0x/, '') binary_kzg_commitment = [kzg_commitment].pack("H*") sha256_hash = Digest::SHA256.hexdigest(binary_kzg_commitment) modified_hash = "0x01" + sha256_hash[2..-1] - + version_hash == modified_hash end end @@ -94,20 +95,20 @@ def blobs blob_from_version_hash(version_hash) end end - + def create_ethscription_attachment_if_needed! return unless EthTransaction.esip8_enabled?(block_number) - + if ethscription.blank? || !has_blob? raise HowDidWeGetHereError, "Invalid state to create attachment: #{transaction_hash}" end - + return if ethscription.event_log_index.present? - + attachment = EthscriptionAttachment.from_eth_transaction(self) - + attachment.create_unless_exists! - + ethscription.update!( attachment_sha: attachment.sha, attachment_content_type: attachment.content_type, @@ -126,55 +127,62 @@ def create_ethscription_from_input! content_uri: utf8_input, }.merge(ethscription_attrs) ) - + save_if_valid_and_no_ethscription_created!(potentially_valid) end - + def create_ethscription_from_events! ethscription_creation_events.each do |creation_event| next if creation_event['topics'].length != 2 - + begin initial_owner = Eth::Abi.decode(['address'], creation_event['topics'].second).first - + content_uri_data = Eth::Abi.decode(['string'], creation_event['data']).first content_uri = HexDataProcessor.clean_utf8(content_uri_data) rescue Eth::Abi::DecodingError next end - + + # Determine creator based on event type + creator = if creation_event['topics'].first == CreateEthscriptionFromEventSig + from_address # Use transaction.from for CreateEthscriptionFrom + else + creation_event['address'] # Use contract address for CreateEthscription + end + potentially_valid = Ethscription.new( { - creator: creation_event['address'], - previous_owner: creation_event['address'], + creator: creator, + previous_owner: creator, current_owner: initial_owner, initial_owner: initial_owner, content_uri: content_uri, event_log_index: creation_event['logIndex'].to_i(16), }.merge(ethscription_attrs) ) - + save_if_valid_and_no_ethscription_created!(potentially_valid) end end - + def save_if_valid_and_no_ethscription_created!(potentially_valid) return if ethscription.present? - + if potentially_valid.valid_ethscription? potentially_valid.eth_transaction = self potentially_valid.save! end end - + def ethscription_creation_events return [] unless EthTransaction.esip3_enabled?(block_number) - + ordered_events.select do |log| - CreateEthscriptionEventSig == log['topics'].first + [CreateEthscriptionEventSig, CreateEthscriptionFromEventSig].include?(log['topics'].first) end end - + def transfer_attrs { eth_transaction: self, @@ -184,17 +192,17 @@ def transfer_attrs transaction_index: transaction_index, } end - + def create_ethscription_transfers_from_input! return unless transfers_ethscription_via_input? - + concatenated_hashes = input_no_prefix.scan(/.{64}/).map { |hash| "0x#{hash}" } matching_ethscriptions = Ethscription.where(transaction_hash: concatenated_hashes) sorted_ethscriptions = concatenated_hashes.map do |hash| matching_ethscriptions.detect { |e| e.transaction_hash == hash } end.compact - + sorted_ethscriptions.each do |ethscription| potentially_valid = EthscriptionTransfer.new({ ethscription: ethscription, @@ -202,19 +210,19 @@ def create_ethscription_transfers_from_input! to_address: to_address, transfer_index: transfer_index, }.merge(transfer_attrs)) - + potentially_valid.create_if_valid! end end - + def create_ethscription_transfers_from_events! ethscription_transfer_events.each do |log| topics = log['topics'] event_type = topics.first - + if event_type == Esip1EventSig next if topics.length != 3 - + begin event_to = Eth::Abi.decode(['address'], topics.second).first tx_hash = Eth::Util.bin_to_prefixed_hex( @@ -223,9 +231,9 @@ def create_ethscription_transfers_from_events! rescue Eth::Abi::DecodingError next end - + target_ethscription = Ethscription.find_by(transaction_hash: tx_hash) - + if target_ethscription.present? potentially_valid = EthscriptionTransfer.new({ ethscription: target_ethscription, @@ -234,12 +242,12 @@ def create_ethscription_transfers_from_events! event_log_index: log['logIndex'].to_i(16), transfer_index: transfer_index, }.merge(transfer_attrs)) - + potentially_valid.create_if_valid! end elsif event_type == Esip2EventSig next if topics.length != 4 - + begin event_previous_owner = Eth::Abi.decode(['address'], topics.second).first event_to = Eth::Abi.decode(['address'], topics.third).first @@ -249,9 +257,9 @@ def create_ethscription_transfers_from_events! rescue Eth::Abi::DecodingError next end - + target_ethscription = Ethscription.find_by(transaction_hash: tx_hash) - + if target_ethscription.present? potentially_valid = EthscriptionTransfer.new({ ethscription: target_ethscription, @@ -261,33 +269,33 @@ def create_ethscription_transfers_from_events! transfer_index: transfer_index, enforced_previous_owner: event_previous_owner, }.merge(transfer_attrs)) - + potentially_valid.create_if_valid! end end end end - + def transfers_ethscription_via_input? valid_length = if EthTransaction.esip5_enabled?(block_number) input_no_prefix.length > 0 && input_no_prefix.length % 64 == 0 else input_no_prefix.length == 64 end - + to_address.present? && valid_length - end - + end + def transfers_ethscription_via_event? ethscription_transfer_events.present? end - + def ethscription_transfer_events ordered_events.select do |log| EthTransaction.contract_transfer_event_signatures(block_number).include?(log['topics'].first) end end - + def ordered_events logs.select do |log| !log['removed'] @@ -295,42 +303,42 @@ def ordered_events log['logIndex'].to_i(16) end end - + def input_no_prefix input.gsub(/\A0x/, '') end - + def self.esip3_enabled?(block_number) on_testnet? || block_number >= 18130000 end - + def self.esip5_enabled?(block_number) on_testnet? || block_number >= 18330000 end - + def self.esip2_enabled?(block_number) on_testnet? || block_number >= 17764910 end - + def self.esip1_enabled?(block_number) on_testnet? || block_number >= 17672762 end - + def self.esip7_enabled?(block_number) on_testnet? || block_number >= 19376500 end - + def self.esip8_enabled?(block_number) on_testnet? || block_number >= 19526000 end - + def self.contract_transfer_event_signatures(block_number) [].tap do |res| res << Esip1EventSig if esip1_enabled?(block_number) res << Esip2EventSig if esip2_enabled?(block_number) end end - + def self.prune_transactions(block_number) EthTransaction.where(block_number: block_number) .where.not( @@ -341,7 +349,7 @@ def self.prune_transactions(block_number) ) .delete_all end - + def self.on_testnet? ENV['ETHEREUM_NETWORK'] != "eth-mainnet" end