Module: BlockGiven::Utils

Defined in:
lib/block_given/utils.rb

Overview

Stateless helpers, mirroring viem's utils (parseUnits, formatUnits, keccak256, ...).

Constant Summary collapse

ZERO_ADDRESS =
"0x0000000000000000000000000000000000000000"
BLOCK_TAGS =
%w[latest earliest pending safe finalized].freeze

Class Method Summary collapse

Class Method Details

.address?(value) ⇒ Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/block_given/utils.rb', line 61

def address?(value)
  value.is_a?(String) && value.match?(/\A0x[0-9a-fA-F]{40}\z/)
end

.bin_to_hex(bin) ⇒ Object



46
47
48
# File 'lib/block_given/utils.rb', line 46

def bin_to_hex(bin)
  "0x#{bin.unpack1('H*')}"
end

.block_tag(value) ⇒ Object

Accepts an Integer, a hex QUANTITY or a block tag (:latest, "pending", ...).



112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/block_given/utils.rb', line 112

def block_tag(value)
  case value
  when nil then "latest"
  when Integer then to_hex(value)
  when Symbol then block_tag(value.to_s)
  when String
    return value if hex?(value) || BLOCK_TAGS.include?(value)

    raise InvalidArgumentError, "invalid block tag: #{value.inspect}"
  else raise InvalidArgumentError, "invalid block: #{value.inspect}"
  end
end

.checksum_address(value) ⇒ Object



65
66
67
68
69
70
# File 'lib/block_given/utils.rb', line 65

def checksum_address(value)
  value = value.address if value.respond_to?(:address) && !value.is_a?(String)
  raise InvalidAddressError, "invalid address: #{value.inspect}" unless address?(value)

  Eth::Address.new(value).checksummed
end

.format_ether(value) ⇒ Object



94
# File 'lib/block_given/utils.rb', line 94

def format_ether(value) = format_units(value, 18)

.format_gwei(value) ⇒ Object



96
# File 'lib/block_given/utils.rb', line 96

def format_gwei(value) = format_units(value, 9)

.format_units(value, decimals) ⇒ Object

1_500_000, 6 -> "1.5"



86
87
88
89
90
91
# File 'lib/block_given/utils.rb', line 86

def format_units(value, decimals)
  decimal = BigDecimal(value.to_i) / (BigDecimal(10)**decimals)
  str = decimal.to_s("F")
  str = str.sub(/\.?0+\z/, "") if str.include?(".")
  str
end

.hex?(value) ⇒ Boolean

Returns:

  • (Boolean)


14
15
16
# File 'lib/block_given/utils.rb', line 14

def hex?(value)
  value.is_a?(String) && value.match?(/\A0x[0-9a-fA-F]*\z/)
end

.hex_to_bin(hex) ⇒ Object



42
43
44
# File 'lib/block_given/utils.rb', line 42

def hex_to_bin(hex)
  [strip_hex(hex)].pack("H*")
end

.hex_to_int(hex) ⇒ Object



35
36
37
38
39
40
# File 'lib/block_given/utils.rb', line 35

def hex_to_int(hex)
  return nil if hex.nil?
  return hex if hex.is_a?(Integer)

  Integer(strip_hex(hex), 16)
end

.keccak256(data) ⇒ Object

keccak256 of raw bytes (or of the bytes represented by a 0x hex string).



56
57
58
59
# File 'lib/block_given/utils.rb', line 56

def keccak256(data)
  bytes = hex?(data) ? hex_to_bin(data) : data.to_s
  bin_to_hex(Eth::Util.keccak256(bytes))
end

.pad_hex(hex, bytes: 32) ⇒ Object

Left-pads a hex value to 32 bytes (used for event topics).



51
52
53
# File 'lib/block_given/utils.rb', line 51

def pad_hex(hex, bytes: 32)
  "0x#{strip_hex(hex).rjust(bytes * 2, '0')}"
end

.parse_ether(value) ⇒ Object



93
# File 'lib/block_given/utils.rb', line 93

def parse_ether(value) = parse_units(value, 18)

.parse_gwei(value) ⇒ Object



95
# File 'lib/block_given/utils.rb', line 95

def parse_gwei(value) = parse_units(value, 9)

.parse_units(value, decimals) ⇒ Object

"1.5", 6 -> 1_500_000. Accepts String, Integer, Float, Rational, BigDecimal.



77
78
79
80
81
82
83
# File 'lib/block_given/utils.rb', line 77

def parse_units(value, decimals)
  decimal = to_decimal(value)
  scaled = decimal * (BigDecimal(10)**decimals)
  raise InvalidArgumentError, "#{value} has more than #{decimals} decimals" unless scaled.frac.zero?

  scaled.to_i
end

.prefix_hex(hex) ⇒ Object



18
19
20
# File 'lib/block_given/utils.rb', line 18

def prefix_hex(hex)
  hex.start_with?("0x") ? hex : "0x#{hex}"
end

.same_address?(a, b) ⇒ Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/block_given/utils.rb', line 72

def same_address?(a, b)
  a.to_s.downcase == b.to_s.downcase
end

.snake_case(name) ⇒ Object



125
126
127
128
129
130
131
132
# File 'lib/block_given/utils.rb', line 125

def snake_case(name)
  name.to_s
      .sub(/\A_+/, "")
      .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
      .gsub(/([a-z\d])([A-Z])/, '\1_\2')
      .tr("-", "_")
      .downcase
end

.strip_hex(hex) ⇒ Object



22
23
24
# File 'lib/block_given/utils.rb', line 22

def strip_hex(hex)
  hex.start_with?("0x") ? hex[2..] : hex
end

.to_decimal(value) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/block_given/utils.rb', line 98

def to_decimal(value)
  case value
  when BigDecimal then value
  when Integer then BigDecimal(value)
  when Float then BigDecimal(value.to_s)
  when Rational then BigDecimal(value, 40)
  when String then BigDecimal(value.strip)
  else raise InvalidArgumentError, "cannot convert #{value.inspect} to a decimal"
  end
rescue ::ArgumentError
  raise InvalidArgumentError, "cannot convert #{value.inspect} to a decimal"
end

.to_hex(value) ⇒ Object

Integer -> "0x1a" (no leading zeros, as JSON-RPC QUANTITY expects).



27
28
29
30
31
32
33
# File 'lib/block_given/utils.rb', line 27

def to_hex(value)
  case value
  when Integer then "0x#{value.to_s(16)}"
  when String then prefix_hex(value)
  else raise InvalidArgumentError, "cannot convert #{value.inspect} to hex"
  end
end