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.
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 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/eth/abi/decoder.rb', line 111 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 93 94 95 96 97 98 99 100 101 102 103 |
# 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] raise DecodingError, "Wrong data size for dynamic array" unless arg.size >= 32 + 32 * l # 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 raise DecodingError, "Offset out of bounds" if pointer < 32 * l || pointer > arg.size - 64 data_l = Util.deserialize_big_endian_to_int arg[32 + pointer, 32] # length of the element raise DecodingError, "Offset out of bounds" if pointer + 32 + Util.ceil32(data_l) > arg.size 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 if nested_sub.dynamic? raise DecodingError, "Wrong data size for dynamic array" unless arg.size >= 32 + 32 * l offsets = (0...l).map do |i| off = Util.deserialize_big_endian_to_int arg[32 + 32 * i, 32] raise DecodingError, "Offset out of bounds" if off < 32 * l || off > arg.size - 64 off end offsets.map { |off| type(nested_sub, arg[32 + off..]) } else raise DecodingError, "Wrong data size for dynamic array" unless arg.size >= 32 + nested_sub.size * l # decoded dynamic-sized arrays with static sub-types (0...l).map { |i| type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) } end 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.
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 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/eth/abi/decoder.rb', line 111 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 93 94 95 96 97 98 99 100 101 102 103 |
# 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] raise DecodingError, "Wrong data size for dynamic array" unless arg.size >= 32 + 32 * l # 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 raise DecodingError, "Offset out of bounds" if pointer < 32 * l || pointer > arg.size - 64 data_l = Util.deserialize_big_endian_to_int arg[32 + pointer, 32] # length of the element raise DecodingError, "Offset out of bounds" if pointer + 32 + Util.ceil32(data_l) > arg.size 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 if nested_sub.dynamic? raise DecodingError, "Wrong data size for dynamic array" unless arg.size >= 32 + 32 * l offsets = (0...l).map do |i| off = Util.deserialize_big_endian_to_int arg[32 + 32 * i, 32] raise DecodingError, "Offset out of bounds" if off < 32 * l || off > arg.size - 64 off end offsets.map { |off| type(nested_sub, arg[32 + off..]) } else raise DecodingError, "Wrong data size for dynamic array" unless arg.size >= 32 + nested_sub.size * l # decoded dynamic-sized arrays with static sub-types (0...l).map { |i| type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) } end 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 |