viem_rb 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6fa59c405af0163f103e886bf29fa2c6d9c76f5e97d8e9fb5288446f44d626f4
4
- data.tar.gz: 89cdeaabac33bfc76f46d644d1aee348babf49d61d64e2988325cd1f1d1bb99b
3
+ metadata.gz: b484347f34f49daa07a62c0de9470faefab1b9ea6eb95573fc92b8785fc2e228
4
+ data.tar.gz: 431aadd5602eebd269ebea09884d1614355b57c3979e5e301c1dbd0451d60bf3
5
5
  SHA512:
6
- metadata.gz: e1dc6bb79f74d3eba580dded3d3229b150a7cc408b2eaa5f4e7dea4375a6f671aed14342bb9ccf4912df3504fa7445eef7538b1cd48da91deb004a8f8076857c
7
- data.tar.gz: 87fe6920ac0199ef8a1749406e20a6c3cb85bec40d4a2b9e274799f575d513da16918809009c97d9f886249bb537893f22352fd5819e194c8d3bbd29fe5120e4
6
+ metadata.gz: 1e6e9137eee7e6840dd5fd62702d7689d2f8dde28e74338e0cc4fbcfa5ab160633d64d616e8c992e3111b8a401424d3762753f72d24494b2adc4402af7805514
7
+ data.tar.gz: 62a543abdc1014f1e750a2ec7158a90ebc9dea761cabe06a99da25d6efacc2ae4cb83fe2f33c0e1d3369ae5532736d7c88c28a4732db7228e7d92ed2d2a3a76f
data/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.3] - 2026-03-19
9
+
10
+ ### Added
11
+ - `get_logs` fully completed:
12
+ - `block_hash:` param — filter logs from a specific block hash (mutually exclusive with `from_block`/`to_block`)
13
+ - Multi-address support — `address:` now accepts an array of contract addresses
14
+ - Address validation on `address:` param (raises `InvalidAddressError` for invalid inputs)
15
+ - Full indexed args encoding — `args:` hash now encodes topic1/2/3 by type (`address`, `bool`, `uint`, `int`, `bytes32`, `bytes`, `string`)
16
+ - OR filtering — pass an array as an arg value to generate a multi-value OR topic filter
17
+ - Raw `topics:` override — bypass event/args encoding and pass topics directly
18
+ - Tuple event support — canonical type resolution for events with `tuple` inputs
19
+ - Trailing nil wildcard stripping — topics array is trimmed to the last meaningful filter
20
+ - 31 new specs for `get_logs` (192 total, 0 failures)
21
+
8
22
  ## [0.1.2] - 2026-03-19
9
23
 
10
24
  ### Fixed
@@ -4,29 +4,127 @@ module Viem
4
4
  module Actions
5
5
  module Public
6
6
  module GetLogs
7
- def get_logs(address: nil, event: nil, args: {}, from_block: nil, to_block: nil)
7
+ # Fetch event logs from the chain.
8
+ #
9
+ # @param address [String, Array<String>, nil] Contract address(es) to filter
10
+ # @param event [Hash, nil] ABI item for the event (generates topic0)
11
+ # @param args [Hash] Indexed argument values to filter (topic1+).
12
+ # Values can be arrays for OR filtering.
13
+ # @param from_block [Integer, String, nil] Start block (number, "latest", "earliest", "pending")
14
+ # @param to_block [Integer, String, nil] End block
15
+ # @param block_hash [String, nil] Filter by specific block hash (exclusive with from/to)
16
+ # @param topics [Array, nil] Raw topics override (skips event+args encoding)
17
+ #
18
+ # @return [Array<Hash>] Formatted log objects
19
+ def get_logs(
20
+ address: nil,
21
+ event: nil,
22
+ args: {},
23
+ from_block: nil,
24
+ to_block: nil,
25
+ block_hash: nil,
26
+ topics: nil
27
+ )
28
+ raise ArgumentError, "block_hash is mutually exclusive with from_block/to_block" \
29
+ if block_hash && (from_block || to_block)
30
+
8
31
  params = {}
9
- params[:address] = address if address
10
- params[:fromBlock] =
11
- from_block.is_a?(Integer) ? Utils::Hex.number_to_hex(from_block) : (from_block || "earliest")
12
- params[:toBlock] = to_block.is_a?(Integer) ? Utils::Hex.number_to_hex(to_block) : (to_block || "latest")
13
- params[:topics] = encode_event_topics(event, args) if event
32
+
33
+ params[:address] = encode_address_filter(address) if address
34
+
35
+ if block_hash
36
+ params[:blockHash] = block_hash
37
+ else
38
+ params[:fromBlock] = encode_block_param(from_block) if from_block
39
+ params[:toBlock] = encode_block_param(to_block) if to_block
40
+ end
41
+
42
+ encoded_topics = topics || (event ? encode_event_topics(event, args) : nil)
43
+ params[:topics] = encoded_topics if encoded_topics
44
+
14
45
  results = @transport.request("eth_getLogs", [stringify_keys(params)])
15
46
  results.map { |l| format_log(l) }
16
47
  end
17
48
 
18
49
  private
19
50
 
20
- def encode_event_topics(event_abi, _args)
21
- sig = event_signature(event_abi)
22
- topic0 = Utils::Hash.keccak256(sig)
23
- [topic0]
51
+ # Encode address or array of addresses, validating each one.
52
+ def encode_address_filter(address)
53
+ if address.is_a?(Array)
54
+ address.map { |a| Utils::Address.get_address(a) }
55
+ else
56
+ Utils::Address.get_address(address)
57
+ end
58
+ end
59
+
60
+ # Convert block param to hex string or pass through string tags.
61
+ def encode_block_param(value)
62
+ return value if value.is_a?(String)
63
+
64
+ Utils::Hex.number_to_hex(value)
65
+ end
66
+
67
+ # Build topics array from event ABI + args.
68
+ # topic0 = keccak256(event signature)
69
+ # topic1+ = encoded indexed argument values (nil = wildcard)
70
+ def encode_event_topics(event_abi, args)
71
+ topic0 = Utils::Hash.keccak256(event_signature(event_abi))
72
+ topics = [topic0]
73
+
74
+ indexed_inputs = (event_abi["inputs"] || []).select { |i| i["indexed"] }
75
+ indexed_inputs.each do |input|
76
+ name = input["name"]
77
+ value = if args.is_a?(Hash)
78
+ args.key?(name) ? args[name] : args[name.to_sym]
79
+ end
80
+
81
+ topics << if value.nil?
82
+ nil # wildcard — match any value for this topic position
83
+ elsif value.is_a?(Array)
84
+ value.map { |v| encode_topic_value(input["type"], v) }
85
+ else
86
+ encode_topic_value(input["type"], value)
87
+ end
88
+ end
89
+
90
+ # Strip trailing wildcards to keep the request minimal
91
+ topics.reverse.drop_while(&:nil?).reverse
24
92
  end
25
93
 
94
+ # ABI-encode a single indexed value to a 32-byte hex topic.
95
+ def encode_topic_value(type, value)
96
+ case type
97
+ when "address"
98
+ addr = Utils::Address.get_address(value).delete_prefix("0x").downcase
99
+ "0x#{"0" * 24}#{addr}"
100
+ when "bool"
101
+ value ? "0x#{"0" * 63}1" : "0x#{"0" * 64}"
102
+ when /\Auint\d*\z/, /\Aint\d*\z/
103
+ Abi::Encoder.encode_abi_parameters([type], [value])
104
+ when /\Abytes(\d+)\z/
105
+ raw = value.delete_prefix("0x")
106
+ "0x#{raw.ljust(64, "0")}"
107
+ when "bytes", "string"
108
+ # Dynamic types are stored as keccak256 of their content
109
+ Utils::Hash.keccak256(value)
110
+ else
111
+ value
112
+ end
113
+ end
114
+
115
+ # Build canonical event signature string for keccak256 hashing.
26
116
  def event_signature(abi_item)
27
- inputs = (abi_item["inputs"] || []).map { |i| i["type"] }.join(",")
117
+ inputs = (abi_item["inputs"] || []).map { |i| canonical_type(i) }.join(",")
28
118
  "#{abi_item["name"]}(#{inputs})"
29
119
  end
120
+
121
+ # Resolve tuple types to their canonical form for signature hashing.
122
+ def canonical_type(input)
123
+ return input["type"] unless input["type"] == "tuple"
124
+
125
+ inner = (input["components"] || []).map { |c| canonical_type(c) }.join(",")
126
+ "(#{inner})"
127
+ end
30
128
  end
31
129
  end
32
130
  end
data/lib/viem/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Viem
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: viem_rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nyrok