Module: Eth::Abi
- Extended by:
- Abi
- Included in:
- Abi
- Defined in:
- lib/eth/abi.rb,
lib/eth/abi/type.rb,
lib/eth/abi/event.rb,
lib/eth/abi/decoder.rb,
lib/eth/abi/encoder.rb,
lib/eth/abi/packed/encoder.rb
Overview
Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
Defined Under Namespace
Modules: Decoder, Encoder, Event, Packed Classes: DecodingError, EncodingError, Type, ValueOutOfBounds
Class Method Summary collapse
-
.decode(types, data) ⇒ Array
Decodes Application Binary Interface (ABI) data.
-
.encode(types, args, packed = false) ⇒ String
Encodes Application Binary Interface (ABI) data.
-
.solidity_packed(types, args) ⇒ String
Encodes Application Binary Interface (ABI) data in non-standard packed mode.
Instance Method Summary collapse
-
#decode(types, data) ⇒ Array
Decodes Application Binary Interface (ABI) data.
-
#encode(types, args, packed = false) ⇒ String
Encodes Application Binary Interface (ABI) data.
-
#solidity_packed(types, args) ⇒ String
Encodes Application Binary Interface (ABI) data in non-standard packed mode.
Class Method Details
.decode(types, data) ⇒ Array
Decodes Application Binary Interface (ABI) data. It accepts multiple arguments and decodes using the head/tail mechanism.
97 98 99 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 |
# File 'lib/eth/abi.rb', line 97 def decode(types, data) # accept hex abi but decode it first data = Util.hex_to_bin data if Util.hex? data # parse all types parsed_types = types.map { |t| Type.parse(t) } # prepare output data outputs = [nil] * types.size start_positions = [nil] * types.size + [data.size] pos = 0 parsed_types.each_with_index do |t, i| if t.dynamic? # record start position for dynamic type start_positions[i] = Util.deserialize_big_endian_to_int(data[pos, 32]) j = i - 1 while j >= 0 and start_positions[j].nil? start_positions[j] = start_positions[i] j -= 1 end pos += 32 else # get data directly for static types outputs[i] = data[pos, t.size] pos += t.size end end # add start position equal the length of the entire data j = types.size - 1 while j >= 0 and start_positions[j].nil? start_positions[j] = start_positions[types.size] j -= 1 end raise DecodingError, "Not enough data for head" unless pos <= data.size # add dynamic types parsed_types.each_with_index do |t, i| if t.dynamic? offset, next_offset = start_positions[i, 2] outputs[i] = data[offset...next_offset] end end # return the decoded ABI types and data parsed_types.zip(outputs).map { |(type, out)| Abi::Decoder.type(type, out) } end |
.encode(types, args, packed = false) ⇒ String
Encodes Application Binary Interface (ABI) data. It accepts multiple arguments and encodes using the head/tail mechanism.
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 |
# File 'lib/eth/abi.rb', line 43 def encode(types, args, packed = false) return solidity_packed(types, args) if packed types = [types] unless types.instance_of? Array args = [args] unless args.instance_of? Array # parse all types parsed_types = types.map { |t| Type === t ? t : Type.parse(t) } # prepare the "head" head_size = (0...args.size) .map { |i| parsed_types[i].size or 32 } .reduce(0, &:+) head, tail = "", "" # encode types and arguments args.each_with_index do |arg, i| if parsed_types[i].dynamic? head += Abi::Encoder.type(Type.size_type, head_size + tail.size) tail += Abi::Encoder.type(parsed_types[i], arg) else head += Abi::Encoder.type(parsed_types[i], arg) end end # return the encoded ABI blob "#{head}#{tail}" end |
.solidity_packed(types, args) ⇒ String
Encodes Application Binary Interface (ABI) data in non-standard packed mode. It accepts multiple arguments and encodes using the head/tail mechanism.
78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/eth/abi.rb', line 78 def solidity_packed(types, args) raise ArgumentError, "Types and values must be the same length" if types.length != args.length # We do not use the type system for packed encoding but want to call the parser once # to enforce the type validation. _ = types.map { |t| Type === t ? t : Type.parse(t) } packed = types.zip(args).map do |type, arg| Abi::Packed::Encoder.type(type, arg) end.join packed.force_encoding(Encoding::ASCII_8BIT) end |
Instance Method Details
#decode(types, data) ⇒ Array
Decodes Application Binary Interface (ABI) data. It accepts multiple arguments and decodes using the head/tail mechanism.
97 98 99 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 |
# File 'lib/eth/abi.rb', line 97 def decode(types, data) # accept hex abi but decode it first data = Util.hex_to_bin data if Util.hex? data # parse all types parsed_types = types.map { |t| Type.parse(t) } # prepare output data outputs = [nil] * types.size start_positions = [nil] * types.size + [data.size] pos = 0 parsed_types.each_with_index do |t, i| if t.dynamic? # record start position for dynamic type start_positions[i] = Util.deserialize_big_endian_to_int(data[pos, 32]) j = i - 1 while j >= 0 and start_positions[j].nil? start_positions[j] = start_positions[i] j -= 1 end pos += 32 else # get data directly for static types outputs[i] = data[pos, t.size] pos += t.size end end # add start position equal the length of the entire data j = types.size - 1 while j >= 0 and start_positions[j].nil? start_positions[j] = start_positions[types.size] j -= 1 end raise DecodingError, "Not enough data for head" unless pos <= data.size # add dynamic types parsed_types.each_with_index do |t, i| if t.dynamic? offset, next_offset = start_positions[i, 2] outputs[i] = data[offset...next_offset] end end # return the decoded ABI types and data parsed_types.zip(outputs).map { |(type, out)| Abi::Decoder.type(type, out) } end |
#encode(types, args, packed = false) ⇒ String
Encodes Application Binary Interface (ABI) data. It accepts multiple arguments and encodes using the head/tail mechanism.
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 |
# File 'lib/eth/abi.rb', line 43 def encode(types, args, packed = false) return solidity_packed(types, args) if packed types = [types] unless types.instance_of? Array args = [args] unless args.instance_of? Array # parse all types parsed_types = types.map { |t| Type === t ? t : Type.parse(t) } # prepare the "head" head_size = (0...args.size) .map { |i| parsed_types[i].size or 32 } .reduce(0, &:+) head, tail = "", "" # encode types and arguments args.each_with_index do |arg, i| if parsed_types[i].dynamic? head += Abi::Encoder.type(Type.size_type, head_size + tail.size) tail += Abi::Encoder.type(parsed_types[i], arg) else head += Abi::Encoder.type(parsed_types[i], arg) end end # return the encoded ABI blob "#{head}#{tail}" end |
#solidity_packed(types, args) ⇒ String
Encodes Application Binary Interface (ABI) data in non-standard packed mode. It accepts multiple arguments and encodes using the head/tail mechanism.
78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/eth/abi.rb', line 78 def solidity_packed(types, args) raise ArgumentError, "Types and values must be the same length" if types.length != args.length # We do not use the type system for packed encoding but want to call the parser once # to enforce the type validation. _ = types.map { |t| Type === t ? t : Type.parse(t) } packed = types.zip(args).map do |type, arg| Abi::Packed::Encoder.type(type, arg) end.join packed.force_encoding(Encoding::ASCII_8BIT) end |