Class: Eth::Abi::Type

Inherits:
Object
  • Object
show all
Defined in:
lib/eth/abi/type.rb

Overview

Provides a class to handle and parse common ABI types.

Defined Under Namespace

Classes: ParseError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_type, sub_type, dimensions, components = nil, component_name = nil) ⇒ Eth::Abi::Type

Create a new Type object for base types, sub types, and dimensions. Should not be used; use #parse instead.

Parameters:

  • base_type (String)

    the base-type attribute.

  • sub_type (String)

    the sub-type attribute.

  • dimensions (Array)

    the dimension attribute.

  • components (Array) (defaults to: nil)

    the components attribute.

  • component_name (String) (defaults to: nil)

    the tuple component’s name.



53
54
55
56
57
58
59
60
# File 'lib/eth/abi/type.rb', line 53

def initialize(base_type, sub_type, dimensions, components = nil, component_name = nil)
  sub_type = sub_type.to_s
  @base_type = base_type
  @sub_type = sub_type
  @dimensions = dimensions
  @components = components
  @name = component_name
end

Instance Attribute Details

#base_typeObject (readonly)

The base attribute, e.g., string or bytes.



30
31
32
# File 'lib/eth/abi/type.rb', line 30

def base_type
  @base_type
end

#componentsObject (readonly)

The components of a tuple type.



39
40
41
# File 'lib/eth/abi/type.rb', line 39

def components
  @components
end

#dimensionsObject (readonly)

The dimension attribute, e.g., [10] for an array of size 10.



36
37
38
# File 'lib/eth/abi/type.rb', line 36

def dimensions
  @dimensions
end

#nameObject (readonly)

The name of tuple component.



42
43
44
# File 'lib/eth/abi/type.rb', line 42

def name
  @name
end

#sub_typeObject (readonly)

The sub-type attribute, e.g., 256 as size of an uint256.



33
34
35
# File 'lib/eth/abi/type.rb', line 33

def sub_type
  @sub_type
end

Class Method Details

.size_typeEth::Abi::Type

Creates a new uint256 type used for size.

Returns:



121
122
123
# File 'lib/eth/abi/type.rb', line 121

def self.size_type
  @size_type ||= new("uint", 256, [])
end

Instance Method Details

#==(another_type) ⇒ Boolean

Compares two types for their attributes.

Parameters:

Returns:

  • (Boolean)

    true if all attributes match.



129
130
131
132
133
# File 'lib/eth/abi/type.rb', line 129

def ==(another_type)
  base_type == another_type.base_type and
    sub_type == another_type.sub_type and
    dimensions == another_type.dimensions
end

#dynamic?Boolean

Helpes to determine whether array is of dynamic size.

Returns:

  • (Boolean)

    true if array is of dynamic size.



155
156
157
# File 'lib/eth/abi/type.rb', line 155

def dynamic?
  size.nil?
end

#nested_subEth::Abi::Type

Types can have nested sub-types in arrays.

Returns:



162
163
164
# File 'lib/eth/abi/type.rb', line 162

def nested_sub
  @nested_sub ||= self.class.new(base_type, sub_type, dimensions[0...-1], components, name)
end

#parse(type, components = nil, component_name = nil) ⇒ Eth::Abi::Type

Attempts to parse a string containing a common Solidity type. Creates a new Type upon success (using konstructor).

Parameters:

  • type (String)

    a common Solidity type.

  • components (Array) (defaults to: nil)

    the components attribute.

  • component_name (String) (defaults to: nil)

    the tuple component’s name.

Returns:

Raises:



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/eth/abi/type.rb', line 73

def parse(type, components = nil, component_name = nil)
  if type.is_a?(Type)
    @base_type = type.base_type
    @sub_type = type.sub_type
    @dimensions = type.dimensions
    @components = type.components
    @name = type.name
    return
  end

  # ensure the type string is reasonable before attempting to parse
  raise ParseError, "Invalid type format" unless type.is_a? String

  if type.start_with?("tuple(") || type.start_with?("(")
    tuple_str = type.start_with?("tuple(") ? type : "tuple#{type}"
    inner, rest = extract_tuple(tuple_str)
    inner_types = split_tuple_types(inner)
    inner_types.each { |t| Type.parse(t) }
    base_type = "tuple"
    sub_type = ""
    dimension = rest
    components ||= inner_types.map { |t| { "type" => t } }
  else
    match = /\A([a-z]+)([0-9]*x?[0-9]*)((?:\[\d+\]|\[\])*)\z/.match(type)
    raise ParseError, "Invalid type format" unless match
    _, base_type, sub_type, dimension = match.to_a
    sub_type = "256" if %w[uint int].include?(base_type) && sub_type.empty?
  end

  # type dimension can only be numeric or empty for dynamic arrays
  dims = dimension.scan(/\[\d+\]|\[\]/)
  raise ParseError, "Unknown characters found in array declaration" if dims.join != dimension

  # enforce base types
  validate_base_type base_type, sub_type

  # return a new Type (using konstructor)
  sub_type = sub_type.to_s
  @base_type = base_type
  @sub_type = sub_type
  @dimensions = dims.map { |x| x == "[]" ? 0 : x[1...-1].to_i }
  @components = components.map { |component| Abi::Type.parse(component["type"], component.dig("components"), component.dig("name")) } if components&.any?
  @name = component_name
end

#sizeInteger

Computes the size of a type if possible.

Returns:

  • (Integer)

    the size of the type; or nil if not available.



138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/eth/abi/type.rb', line 138

def size
  s = nil
  if dimensions.empty?
    if !(["string", "bytes", "tuple"].include?(base_type) and sub_type.empty?)
      s = 32
    elsif base_type == "tuple" and components&.none?(&:dynamic?)
      s = components.sum(&:size)
    end
  elsif dimensions.last != 0 and !nested_sub.dynamic?
    s = dimensions.last * nested_sub.size
  end
  @size ||= s
end

#to_sString

Allows exporting the type as string.

Returns:

  • (String)

    the type string.



169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/eth/abi/type.rb', line 169

def to_s
  if base_type == "tuple"
    "(" + components.map(&:to_s).join(",") + ")" + (dimensions.size > 0 ? dimensions.map { |x| "[#{x == 0 ? "" : x}]" }.join : "")
  elsif dimensions.empty?
    if %w[string bytes].include?(base_type) and sub_type.empty?
      base_type
    else
      "#{base_type}#{sub_type}"
    end
  else
    "#{base_type}#{sub_type}#{dimensions.map { |x| "[#{x == 0 ? "" : x}]" }.join}"
  end
end