Module: Eth::Abi::Decoder

Extended by:
Decoder
Included in:
Decoder
Defined in:
lib/eth/abi/decoder.rb

Overview

Provides a utility module to assist decoding ABIs.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.primitive_type(type, data) ⇒ String

Decodes primitive types.

Parameters:

  • type (Eth::Abi::Type)

    type to be decoded.

  • data (String)

    encoded primitive type data string.

Returns:

  • (String)

    the decoded data for the type.

Raises:



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/eth/abi/decoder.rb', line 100

def primitive_type(type, data)
  case type.base_type
  when "address"

    # decoded address with 0x-prefix
    Address.new(Util.bin_to_hex data[12..-1]).to_s.downcase
  when "string", "bytes"
    if type.sub_type.empty?
      size = Util.deserialize_big_endian_to_int data[0, 32]

      # decoded dynamic-sized array
      data[32..-1][0, size]
    else

      # decoded static-sized array
      data[0, type.sub_type.to_i]
    end
  when "hash"

    # decoded hash
    data[(32 - type.sub_type.to_i), type.sub_type.to_i]
  when "uint"

    # decoded unsigned integer
    Util.deserialize_big_endian_to_int data
  when "int"
    u = Util.deserialize_big_endian_to_int data
    i = u >= 2 ** (type.sub_type.to_i - 1) ? (u - 2 ** 256) : u

    # decoded integer
    i
  when "ureal", "ufixed"
    high, low = type.sub_type.split("x").map(&:to_i)

    # decoded unsigned fixed point numeric
    Util.deserialize_big_endian_to_int(data) * 1.0 / 2 ** low
  when "real", "fixed"
    high, low = type.sub_type.split("x").map(&:to_i)
    u = Util.deserialize_big_endian_to_int data
    i = u >= 2 ** (high + low - 1) ? (u - 2 ** (high + low)) : u

    # decoded fixed point numeric
    i * 1.0 / 2 ** low
  when "bool"

    # decoded boolean
    data[-1] == Constant::BYTE_ONE
  else
    raise DecodingError, "Unknown primitive type: #{type.base_type}"
  end
end

.type(type, arg) ⇒ String

Decodes a specific value, either static or dynamic.

Parameters:

  • type (Eth::Abi::Type)

    type to be decoded.

  • arg (String)

    encoded type data string.

Returns:

  • (String)

    the decoded data for the type.

Raises:



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/eth/abi/decoder.rb', line 33

def type(type, arg)
  if %w(string bytes).include?(type.base_type) and type.sub_type.empty?
    # Case: decoding a string/bytes
    if type.dimensions.empty?
      l = Util.deserialize_big_endian_to_int arg[0, 32]
      data = arg[32..-1]
      raise DecodingError, "Wrong data size for string/bytes object" unless data.size == Util.ceil32(l)

      # decoded strings and bytes
      data[0, l]
      # Case: decoding array of string/bytes
    else
      l = Util.deserialize_big_endian_to_int arg[0, 32]

      # Decode each element of the array
      (1..l).map do |i|
        pointer = Util.deserialize_big_endian_to_int arg[i * 32, 32] # Pointer to the size of the array's element
        data_l = Util.deserialize_big_endian_to_int arg[32 + pointer, 32] # length of the element
        type(Type.parse(type.base_type), arg[pointer + 32, Util.ceil32(data_l) + 32])
      end
    end
  elsif type.base_type == "tuple"
    offset = 0
    data = {}
    raise DecodingError, "Cannot decode tuples without known components" if type.components.nil?
    type.components.each do |c|
      if c.dynamic?
        pointer = Util.deserialize_big_endian_to_int arg[offset, 32] # Pointer to the size of the array's element
        data_len = Util.deserialize_big_endian_to_int arg[pointer, 32] # length of the element

        data[c.name] = type(c, arg[pointer, Util.ceil32(data_len) + 32])
        offset += 32
      else
        size = c.size
        data[c.name] = type(c, arg[offset, size])
        offset += size
      end
    end
    data
  elsif type.dynamic?
    l = Util.deserialize_big_endian_to_int arg[0, 32]
    nested_sub = type.nested_sub

    # ref https://github.com/ethereum/tests/issues/691
    raise NotImplementedError, "Decoding dynamic arrays with nested dynamic sub-types is not implemented for ABI." if nested_sub.dynamic?

    # decoded dynamic-sized arrays
    (0...l).map { |i| type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) }
  elsif !type.dimensions.empty?
    l = type.dimensions.first
    nested_sub = type.nested_sub

    # decoded static-size arrays
    (0...l).map { |i| type(nested_sub, arg[nested_sub.size * i, nested_sub.size]) }
  else

    # decoded primitive types
    primitive_type type, arg
  end
end

Instance Method Details

#primitive_type(type, data) ⇒ String

Decodes primitive types.

Parameters:

  • type (Eth::Abi::Type)

    type to be decoded.

  • data (String)

    encoded primitive type data string.

Returns:

  • (String)

    the decoded data for the type.

Raises:



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/eth/abi/decoder.rb', line 100

def primitive_type(type, data)
  case type.base_type
  when "address"

    # decoded address with 0x-prefix
    Address.new(Util.bin_to_hex data[12..-1]).to_s.downcase
  when "string", "bytes"
    if type.sub_type.empty?
      size = Util.deserialize_big_endian_to_int data[0, 32]

      # decoded dynamic-sized array
      data[32..-1][0, size]
    else

      # decoded static-sized array
      data[0, type.sub_type.to_i]
    end
  when "hash"

    # decoded hash
    data[(32 - type.sub_type.to_i), type.sub_type.to_i]
  when "uint"

    # decoded unsigned integer
    Util.deserialize_big_endian_to_int data
  when "int"
    u = Util.deserialize_big_endian_to_int data
    i = u >= 2 ** (type.sub_type.to_i - 1) ? (u - 2 ** 256) : u

    # decoded integer
    i
  when "ureal", "ufixed"
    high, low = type.sub_type.split("x").map(&:to_i)

    # decoded unsigned fixed point numeric
    Util.deserialize_big_endian_to_int(data) * 1.0 / 2 ** low
  when "real", "fixed"
    high, low = type.sub_type.split("x").map(&:to_i)
    u = Util.deserialize_big_endian_to_int data
    i = u >= 2 ** (high + low - 1) ? (u - 2 ** (high + low)) : u

    # decoded fixed point numeric
    i * 1.0 / 2 ** low
  when "bool"

    # decoded boolean
    data[-1] == Constant::BYTE_ONE
  else
    raise DecodingError, "Unknown primitive type: #{type.base_type}"
  end
end

#type(type, arg) ⇒ String

Decodes a specific value, either static or dynamic.

Parameters:

  • type (Eth::Abi::Type)

    type to be decoded.

  • arg (String)

    encoded type data string.

Returns:

  • (String)

    the decoded data for the type.

Raises:



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/eth/abi/decoder.rb', line 33

def type(type, arg)
  if %w(string bytes).include?(type.base_type) and type.sub_type.empty?
    # Case: decoding a string/bytes
    if type.dimensions.empty?
      l = Util.deserialize_big_endian_to_int arg[0, 32]
      data = arg[32..-1]
      raise DecodingError, "Wrong data size for string/bytes object" unless data.size == Util.ceil32(l)

      # decoded strings and bytes
      data[0, l]
      # Case: decoding array of string/bytes
    else
      l = Util.deserialize_big_endian_to_int arg[0, 32]

      # Decode each element of the array
      (1..l).map do |i|
        pointer = Util.deserialize_big_endian_to_int arg[i * 32, 32] # Pointer to the size of the array's element
        data_l = Util.deserialize_big_endian_to_int arg[32 + pointer, 32] # length of the element
        type(Type.parse(type.base_type), arg[pointer + 32, Util.ceil32(data_l) + 32])
      end
    end
  elsif type.base_type == "tuple"
    offset = 0
    data = {}
    raise DecodingError, "Cannot decode tuples without known components" if type.components.nil?
    type.components.each do |c|
      if c.dynamic?
        pointer = Util.deserialize_big_endian_to_int arg[offset, 32] # Pointer to the size of the array's element
        data_len = Util.deserialize_big_endian_to_int arg[pointer, 32] # length of the element

        data[c.name] = type(c, arg[pointer, Util.ceil32(data_len) + 32])
        offset += 32
      else
        size = c.size
        data[c.name] = type(c, arg[offset, size])
        offset += size
      end
    end
    data
  elsif type.dynamic?
    l = Util.deserialize_big_endian_to_int arg[0, 32]
    nested_sub = type.nested_sub

    # ref https://github.com/ethereum/tests/issues/691
    raise NotImplementedError, "Decoding dynamic arrays with nested dynamic sub-types is not implemented for ABI." if nested_sub.dynamic?

    # decoded dynamic-sized arrays
    (0...l).map { |i| type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) }
  elsif !type.dimensions.empty?
    l = type.dimensions.first
    nested_sub = type.nested_sub

    # decoded static-size arrays
    (0...l).map { |i| type(nested_sub, arg[nested_sub.size * i, nested_sub.size]) }
  else

    # decoded primitive types
    primitive_type type, arg
  end
end