Module: Eth::Abi::Decoder
Overview
Provides a utility module to assist decoding ABIs.
Class Method Summary collapse
-
.primitive_type(type, data) ⇒ String
Decodes primitive types.
-
.type(type, arg) ⇒ String
Decodes a specific value, either static or dynamic.
Instance Method Summary collapse
-
#primitive_type(type, data) ⇒ String
Decodes primitive types.
-
#type(type, arg) ⇒ String
Decodes a specific value, either static or dynamic.
Class Method Details
.primitive_type(type, data) ⇒ String
Decodes primitive types.
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.
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.
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.
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 |