Module: AbiCoderRb
- Included in:
- EventDecoder
- Defined in:
- lib/abi_coder_rb.rb,
lib/abi_coder_rb/utils.rb,
lib/abi_coder_rb/decode.rb,
lib/abi_coder_rb/encode.rb,
lib/abi_coder_rb/version.rb,
lib/abi_coder_rb/type/parse.rb,
lib/abi_coder_rb/type/types.rb,
lib/abi_coder_rb/parser/abi_parser.rb,
lib/abi_coder_rb/decode/decode_array.rb,
lib/abi_coder_rb/decode/decode_tuple.rb,
lib/abi_coder_rb/encode/encode_array.rb,
lib/abi_coder_rb/encode/encode_tuple.rb,
lib/abi_coder_rb/parser/abi_tokenizer.rb,
lib/abi_coder_rb/decode/decode_fixed_array.rb,
lib/abi_coder_rb/encode/encode_fixed_array.rb,
lib/abi_coder_rb/decode/decode_primitive_type.rb,
lib/abi_coder_rb/encode/encode_primitive_type.rb
Defined Under Namespace
Classes: AbiParser, AbiTokenizer, Address, Array, Bool, Bytes, DecodingError, EncodingError, Error, FixedArray, FixedBytes, Int, ParseError, String, Tuple, Type, Uint, ValueError, ValueOutOfBounds
Constant Summary
collapse
- BYTE_EMPTY =
"".b.freeze
- BYTE_ZERO =
"\x00".b.freeze
- BYTE_ONE =
note: used for encoding bool for now
"\x01".b.freeze
- UINT_MAX =
same as 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
2**256 - 1
- UINT_MIN =
0
- INT_MAX =
same as 57896044618658097711785492504343953926634992332820282019728792003956564819967
2**255 - 1
- INT_MIN =
same as -57896044618658097711785492504343953926634992332820282019728792003956564819968
-2**255
- VERSION =
"0.2.9"
Instance Attribute Summary collapse
Instance Method Summary
collapse
-
#abi_to_int_signed(hex_str, bits) ⇒ Object
-
#after_decoding(action) ⇒ Object
-
#before_encoding(action) ⇒ Object
-
#bin_to_hex(bin) ⇒ Object
-
#ceil32(x) ⇒ Object
-
#decode(type_str, data) ⇒ Object
-
#decode_array(type, data) ⇒ Object
-
#decode_fixed_array(type, data) ⇒ Object
-
#decode_primitive_type(type, data) ⇒ Object
-
#decode_tuple(type, data) ⇒ Object
-
#encode(str, value, packed = false) ⇒ Object
-
#encode_address(arg, packed = false) ⇒ Object
-
#encode_array(type, args, packed = false) ⇒ Object
-
#encode_bool(arg, packed = false) ⇒ Object
-
#encode_bytes(arg, length: nil, packed: false) ⇒ Object
-
#encode_fixed_array(type, args, packed = false) ⇒ Object
-
#encode_int(arg, bits, packed = false) ⇒ Object
-
#encode_primitive_type(type, arg, packed = false) ⇒ Object
-
#encode_string(arg, packed = false) ⇒ Object
-
#encode_tuple(tuple, args, packed = false) ⇒ Object
-
#encode_uint(arg, bits, packed = false) ⇒ Object
-
#encode_uint256(arg) ⇒ Object
-
#hex?(str) ⇒ Boolean
-
#hex_to_bin(hex) ⇒ Object
(also: #hex)
-
#int_to_abi_signed(value, bits) ⇒ Object
-
#int_to_abi_signed_256bit(value) ⇒ Object
-
#lpad(bin, l = 32) ⇒ Object
rename to lpad32 or such - why? why not? example: lpad(“hello”, ‘x’, 10) => “xxxxxxhello”.
-
#lpad_hex(hex) ⇒ Object
rename to lpad32_hex or such - why? why not?.
-
#lpad_int(n, l = 32) ⇒ Object
rename to lpad32_int or such - why? why not?.
-
#rpad(bin, l = 32) ⇒ Object
encoding helpers / utils with “hard-coded” fill symbol as BYTE_ZERO.
Instance Attribute Details
#after_decoding_action ⇒ Object
Returns the value of attribute after_decoding_action.
31
32
33
|
# File 'lib/abi_coder_rb.rb', line 31
def after_decoding_action
@after_decoding_action
end
|
#before_encoding_action ⇒ Object
Returns the value of attribute before_encoding_action.
31
32
33
|
# File 'lib/abi_coder_rb.rb', line 31
def before_encoding_action
@before_encoding_action
end
|
Instance Method Details
#abi_to_int_signed(hex_str, bits) ⇒ Object
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
# File 'lib/abi_coder_rb/utils.rb', line 92
def abi_to_int_signed(hex_str, bits)
hex_str = "0x#{hex_str}" if hex_str[0, 2] != "0x" || hex_str[0, 2] != "0X"
expected_length = bits / 4
extended_hex_str = if hex_str.length < expected_length
extend_char = hex_str[0] == "f" ? "f" : "0"
extend_char * (expected_length - hex_str.length) + hex_str
else
hex_str
end
binary_str = extended_hex_str.to_i(16).to_s(2).rjust(bits, extended_hex_str[0])
if binary_str[0] == "1" -((binary_str.tr("01", "10").to_i(2) + 1) & ((1 << bits) - 1))
else binary_str.to_i(2)
end
end
|
#after_decoding(action) ⇒ Object
37
38
39
|
# File 'lib/abi_coder_rb.rb', line 37
def after_decoding(action)
self.after_decoding_action = action
end
|
#before_encoding(action) ⇒ Object
33
34
35
|
# File 'lib/abi_coder_rb.rb', line 33
def before_encoding(action)
self.before_encoding_action = action
end
|
#bin_to_hex(bin) ⇒ Object
8
9
10
|
# File 'lib/abi_coder_rb/utils.rb', line 8
def bin_to_hex(bin)
bin.each_byte.map { |byte| "%02x" % byte }.join
end
|
#ceil32(x) ⇒ Object
60
61
62
|
# File 'lib/abi_coder_rb/utils.rb', line 60
def ceil32(x)
x % 32 == 0 ? x : (x + 32 - x % 32)
end
|
#decode(type_str, data) ⇒ Object
7
8
9
10
11
|
# File 'lib/abi_coder_rb/decode.rb', line 7
def decode(type_str, data)
raise DecodingError, "Empty data" if data.nil? || data.empty?
decode_type(Type.parse(type_str), data)
end
|
#decode_array(type, data) ⇒ Object
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# File 'lib/abi_coder_rb/decode/decode_array.rb', line 2
def decode_array(type, data)
size = decode_uint256(data[0, 32])
raise DecodingError, "Too many elements: #{size}" if size > 100_000
inner_type = type.inner_type
if inner_type.dynamic?
raise DecodingError, "Not enough data for head" unless data.size >= 32 + 32 * size
start_positions = (1..size).map { |i| 32 + decode_uint256(data[32 * i, 32]) }
start_positions.push(data.size)
outputs = (0...size).map { |i| data[start_positions[i]...start_positions[i + 1]] }
outputs.map { |out| decode_type(inner_type, out) }
else
(0...size).map { |i| decode_type(inner_type, data[(32 + inner_type.size * i)..]) }
end
end
|
#decode_fixed_array(type, data) ⇒ Object
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# File 'lib/abi_coder_rb/decode/decode_fixed_array.rb', line 2
def decode_fixed_array(type, data)
l = type.length
inner_type = type.inner_type
if inner_type.dynamic?
start_positions = (0...l).map { |i| decode_uint256(data[32 * i, 32]) }
start_positions.push(data.size)
outputs = (0...l).map { |i| data[start_positions[i]...start_positions[i + 1]] }
outputs.map { |out| decode_type(inner_type, out) }
else
(0...l).map { |i| decode_type(inner_type, data[inner_type.size * i, inner_type.size]) }
end
end
|
#decode_primitive_type(type, data) ⇒ Object
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
# File 'lib/abi_coder_rb/decode/decode_primitive_type.rb', line 2
def decode_primitive_type(type, data)
result =
case type
when Uint
decode_uint256(data[0, 32])
when Int
abi_to_int_signed(bin_to_hex(data[0, 32]), type.bits)
when Bool
data[31] == BYTE_ONE
when String
size = decode_uint256(data[0, 32])
data[32...(32 + size)].force_encoding("UTF-8")
when Bytes
size = decode_uint256(data[0, 32])
data[32...(32 + size)]
when FixedBytes
data[0, type.length]
when Address
bin_to_hex(data[12...32]).force_encoding("UTF-8")
else
raise DecodingError, "Unknown primitive type: #{type.class.name} #{type.format}"
end
result = after_decoding_action.call(type.format, result) if after_decoding_action
result
end
|
#decode_tuple(type, data) ⇒ Object
2
3
4
|
# File 'lib/abi_coder_rb/decode/decode_tuple.rb', line 2
def decode_tuple(type, data)
decode_types(type.inner_types, data)
end
|
#encode(str, value, packed = false) ⇒ Object
8
9
10
11
12
13
14
15
16
|
# File 'lib/abi_coder_rb/encode.rb', line 8
def encode(str, value, packed = false)
return encode_type(Type.parse(str), value, packed) if str.is_a?(::String)
return str.map.with_index do |type, i|
encode(type, value[i], packed)
end.join("") if str.is_a?(::Array) && value.is_a?(::Array) && str.size == value.size
raise EncodingError, "There is something wrong with #{str.inspect}, #{value.inspect}"
end
|
#encode_address(arg, packed = false) ⇒ Object
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 102
def encode_address(arg, packed = false)
if arg.is_a?(Integer)
packed ? lpad_int(arg, 20) : lpad_int(arg)
elsif arg.is_a?(::String)
if arg.size == 20
arg = arg.b if arg.encoding != Encoding::BINARY
packed ? arg : lpad(arg)
elsif arg.size == 40
packed ? hex_to_bin(arg) : lpad_hex(arg)
elsif arg.size == 42 && arg[0, 2] == "0x" arg = arg[2..-1] packed ? hex_to_bin(arg) : lpad_hex(arg)
else
raise EncodingError, "Could not parse address: #{arg}"
end
end
end
|
#encode_array(type, args, packed = false) ⇒ Object
2
3
4
5
6
|
# File 'lib/abi_coder_rb/encode/encode_array.rb', line 2
def encode_array(type, args, packed = false)
raise EncodingError, "args must be an array" unless args.is_a?(::Array)
_encode_array(type: type, args: args, packed: packed)
end
|
#encode_bool(arg, packed = false) ⇒ Object
53
54
55
56
57
58
59
60
61
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 53
def encode_bool(arg, packed = false)
raise EncodingError, "arg is not bool: #{arg}" unless arg.is_a?(TrueClass) || arg.is_a?(FalseClass)
if packed
arg ? BYTE_ONE : BYTE_ZERO
else
lpad(arg ? BYTE_ONE : BYTE_ZERO) end
end
|
#encode_bytes(arg, length: nil, packed: false) ⇒ Object
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 79
def encode_bytes(arg, length: nil, packed: false)
raise EncodingError, "Expecting string: #{arg}" unless arg.is_a?(::String)
arg = arg.b if arg.encoding != Encoding::BINARY
if length raise ValueOutOfBounds, "invalid bytes length #{arg.size}, should be #{length}" if arg.size > length
raise ValueOutOfBounds, "invalid bytes length #{length}" if length < 0 || length > 32
packed ? arg : rpad(arg)
else raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size > UINT_MAX
if packed
arg
else
size = lpad_int(arg.size)
value = rpad(arg, ceil32(arg.size))
size + value
end
end
end
|
#encode_fixed_array(type, args, packed = false) ⇒ Object
2
3
4
5
6
7
8
9
10
|
# File 'lib/abi_coder_rb/encode/encode_fixed_array.rb', line 2
def encode_fixed_array(type, args, packed = false)
raise EncodingError, "args must be an array" unless args.is_a?(::Array)
raise EncodingError, "Wrong array size: found #{args.size}, expecting #{type.length}" unless args.size == type.length
_encode_array(type: type, args: args, packed: packed)
end
|
#encode_int(arg, bits, packed = false) ⇒ Object
42
43
44
45
46
47
48
49
50
51
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 42
def encode_int(arg, bits, packed = false)
raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
if packed
hex_to_bin(int_to_abi_signed(arg, bits))
else
hex_to_bin(int_to_abi_signed_256bit(arg))
end
end
|
#encode_primitive_type(type, arg, packed = false) ⇒ Object
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 2
def encode_primitive_type(type, arg, packed = false)
arg = before_encoding_action.call(type.format, arg) if before_encoding_action
case type
when Uint
encode_uint(arg, type.bits, packed)
when Int
encode_int(arg, type.bits, packed)
when Bool
encode_bool(arg, packed)
when String
encode_string(arg, packed)
when FixedBytes
encode_bytes(arg, length: type.length, packed: packed)
when Bytes
encode_bytes(arg, packed: packed)
when Address
encode_address(arg, packed)
else
raise EncodingError, "Unknown type: #{type}"
end
end
|
#encode_string(arg, packed = false) ⇒ Object
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 63
def encode_string(arg, packed = false)
raise EncodingError, "Expecting string: #{arg}" unless arg.is_a?(::String)
arg = arg.b if arg.encoding != "BINARY"
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size > UINT_MAX
if packed
arg
else
size = lpad_int(arg.size)
value = rpad(arg, ceil32(arg.size))
size + value
end
end
|
#encode_tuple(tuple, args, packed = false) ⇒ Object
2
3
4
5
6
|
# File 'lib/abi_coder_rb/encode/encode_tuple.rb', line 2
def encode_tuple(tuple, args, packed = false)
raise "#{tuple.class} with multi inner type is not supported in packed mode" if packed && tuple.inner_types.size > 1
encode_types(tuple.inner_types, args, packed)
end
|
#encode_uint(arg, bits, packed = false) ⇒ Object
27
28
29
30
31
32
33
34
35
36
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 27
def encode_uint(arg, bits, packed = false)
raise EncodingError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**bits
if packed
lpad_int(arg, bits / 8)
else
lpad_int(arg)
end
end
|
#encode_uint256(arg) ⇒ Object
38
39
40
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 38
def encode_uint256(arg)
encode_uint(arg, 256)
end
|
#hex?(str) ⇒ Boolean
12
13
14
|
# File 'lib/abi_coder_rb/utils.rb', line 12
def hex?(str)
str.start_with?("0x") && str.length.even? && str[2..].match?(/\A\b[0-9a-fA-F]+\b\z/)
end
|
#hex_to_bin(hex) ⇒ Object
Also known as:
hex
2
3
4
5
|
# File 'lib/abi_coder_rb/utils.rb', line 2
def hex_to_bin(hex)
hex = hex[2..] if %w[0x 0X].include?(hex[0, 2]) hex.scan(/../).map { |x| x.hex.chr }.join
end
|
#int_to_abi_signed(value, bits) ⇒ Object
64
65
66
67
68
69
70
71
72
73
74
|
# File 'lib/abi_coder_rb/utils.rb', line 64
def int_to_abi_signed(value, bits)
min = -2**(bits - 1)
max = 2**(bits - 1) - 1
raise "Value out of range" if value < min || value > max
value = (1 << bits) + value if value < 0
hex_str = value.to_s(16)
hex_str.rjust(bits / 4, "0")
end
|
#int_to_abi_signed_256bit(value) ⇒ Object
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
# File 'lib/abi_coder_rb/utils.rb', line 76
def int_to_abi_signed_256bit(value)
min = -2**255
max = 2**255 - 1
raise "Value out of range" if value < min || value > max
value = (1 << 256) + value if value < 0
hex_str = value.to_s(16)
hex_str.rjust(64, "0")
end
|
#lpad(bin, l = 32) ⇒ Object
rename to lpad32 or such - why? why not? example: lpad(“hello”, ‘x’, 10) => “xxxxxxhello”
30
31
32
33
34
|
# File 'lib/abi_coder_rb/utils.rb', line 30
def lpad(bin, l = 32) return bin if bin.size >= l
BYTE_ZERO * (l - bin.size) + bin
end
|
#lpad_hex(hex) ⇒ Object
rename to lpad32_hex or such - why? why not?
51
52
53
54
55
56
57
58
|
# File 'lib/abi_coder_rb/utils.rb', line 51
def lpad_hex(hex)
raise TypeError, "Value must be a string" unless hex.is_a?(::String)
raise TypeError, "Non-hexadecimal digit found" unless hex =~ /\A[0-9a-fA-F]*\z/
bin = hex_to_bin(hex)
lpad(bin)
end
|
#lpad_int(n, l = 32) ⇒ Object
rename to lpad32_int or such - why? why not?
37
38
39
40
41
42
43
44
45
46
47
48
|
# File 'lib/abi_coder_rb/utils.rb', line 37
def lpad_int(n, l = 32)
unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
raise ArgumentError,
"Integer invalid or out of range: #{n}"
end
hex = n.to_s(16)
hex = "0#{hex}" if hex.length.odd? bin = hex_to_bin(hex)
lpad(bin, l)
end
|
#rpad(bin, l = 32) ⇒ Object
encoding helpers / utils
with "hard-coded" fill symbol as BYTE_ZERO
20
21
22
23
24
25
|
# File 'lib/abi_coder_rb/utils.rb', line 20
def rpad(bin, l = 32) return bin if bin.size >= l
bin + BYTE_ZERO * (l - bin.size)
end
|