Module: AbiCoderRb
- Included in:
- EventDecoder
- Defined in:
- lib/abi_coder_rb.rb,
lib/abi_coder_rb/types.rb,
lib/abi_coder_rb/utils.rb,
lib/abi_coder_rb/decode.rb,
lib/abi_coder_rb/encode.rb,
lib/abi_coder_rb/parser.rb,
lib/abi_coder_rb/version.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/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: Address, Array, Bool, Bytes, DecodingError, EncodingError, FixedArray, FixedBytes, Int, 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.6"
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(type, value) ⇒ Object
-
#encode_address(arg) ⇒ Object
-
#encode_array(type, args) ⇒ Object
-
#encode_bool(arg) ⇒ Object
-
#encode_bytes(arg, length = nil) ⇒ Object
-
#encode_fixed_array(type, args) ⇒ Object
-
#encode_int(arg, _bits) ⇒ Object
-
#encode_primitive_type(type, arg) ⇒ Object
-
#encode_string(arg) ⇒ Object
-
#encode_tuple(tuple, args) ⇒ Object
-
#encode_uint(arg, bits) ⇒ Object
-
#encode_uint256(arg) ⇒ Object
-
#hex?(str) ⇒ Boolean
-
#hex_to_bin(hex) ⇒ Object
(also: #hex)
-
#int_to_abi_signed_256bit(value) ⇒ Object
-
#lpad(bin) ⇒ 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) ⇒ 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.
29
30
31
|
# File 'lib/abi_coder_rb.rb', line 29
def after_decoding_action
@after_decoding_action
end
|
#before_encoding_action ⇒ Object
Returns the value of attribute before_encoding_action.
29
30
31
|
# File 'lib/abi_coder_rb.rb', line 29
def before_encoding_action
@before_encoding_action
end
|
Instance Method Details
#abi_to_int_signed(hex_str, bits) ⇒ Object
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
# File 'lib/abi_coder_rb/utils.rb', line 81
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
35
36
37
|
# File 'lib/abi_coder_rb.rb', line 35
def after_decoding(action)
self.after_decoding_action = action
end
|
#before_encoding(action) ⇒ Object
31
32
33
|
# File 'lib/abi_coder_rb.rb', line 31
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
61
62
63
|
# File 'lib/abi_coder_rb/utils.rb', line 61
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
subtype = type.subtype
if subtype.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(subtype, out) }
else
(0...size).map { |i| decode_type(subtype, data[(32 + subtype.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.dim
subtype = type.subtype
if subtype.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(subtype, out) }
else
(0...l).map { |i| decode_type(subtype, data[subtype.size * i, subtype.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.types, data)
end
|
#encode(type, value) ⇒ Object
8
9
10
11
12
13
14
|
# File 'lib/abi_coder_rb/encode.rb', line 8
def encode(type, value)
raise EncodingError, "Value can not be nil" if value.nil?
parsed = Type.parse(type)
encode_type(parsed, value)
end
|
#encode_address(arg) ⇒ Object
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 85
def encode_address(arg)
if arg.is_a?(Integer)
lpad_int(arg)
elsif arg.size == 20
arg = arg.b if arg.encoding != Encoding::BINARY
lpad(arg)
elsif arg.size == 40
lpad_hex(arg)
elsif arg.size == 42 && arg[0, 2] == "0x"
lpad_hex(arg[2..-1])
else
raise EncodingError, "Could not parse address: #{arg}"
end
end
|
#encode_array(type, args) ⇒ Object
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# File 'lib/abi_coder_rb/encode/encode_array.rb', line 2
def encode_array(type, args)
raise ArgumentError, "arg must be an array" unless args.is_a?(::Array)
head = "".b
tail = "".b
head += encode_uint256(args.size)
subtype = type.subtype
args.each do |arg|
if subtype.dynamic?
head += encode_uint256(32 * args.size + tail.size)
tail += encode_type(subtype, arg)
else
head += encode_type(subtype, arg)
end
end
head + tail
end
|
#encode_bool(arg) ⇒ Object
45
46
47
48
49
50
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 45
def encode_bool(arg)
raise ArgumentError, "arg is not bool: #{arg}" unless arg.is_a?(TrueClass) || arg.is_a?(FalseClass)
lpad(arg ? BYTE_ONE : BYTE_ZERO)
end
|
#encode_bytes(arg, length = nil) ⇒ Object
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 65
def encode_bytes(arg, length = nil)
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
rpad(arg)
else
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size > UINT_MAX
size = lpad_int(arg.size)
value = rpad(arg, ceil32(arg.size))
size + value
end
end
|
#encode_fixed_array(type, args) ⇒ Object
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# File 'lib/abi_coder_rb/encode/encode_fixed_array.rb', line 2
def encode_fixed_array(type, args)
raise ArgumentError, "arg must be an array" unless args.is_a?(::Array)
raise ArgumentError, "Wrong array size: found #{args.size}, expecting #{type.dim}" unless args.size == type.dim
subtype = type.subtype
if subtype.dynamic?
head = "".b
tail = "".b
args.each do |arg|
head += encode_uint256(32 * args.size + tail.size)
tail += encode_type(subtype, arg)
end
head + tail
else
args.map { |arg| encode_type(type.subtype, arg) }.join
end
end
|
#encode_int(arg, _bits) ⇒ Object
38
39
40
41
42
43
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 38
def encode_int(arg, _bits)
raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
hex_to_bin(int_to_abi_signed_256bit(arg))
end
|
#encode_primitive_type(type, arg) ⇒ 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)
arg = before_encoding_action.call(type.format, arg) if before_encoding_action
case type
when Uint
encode_uint(arg, type.bits)
when Int
encode_int(arg, type.bits)
when Bool
encode_bool(arg)
when String
encode_string(arg)
when FixedBytes
encode_bytes(arg, type.length)
when Bytes
encode_bytes(arg)
when Address
encode_address(arg)
else
raise EncodingError, "Unknown type: #{type}"
end
end
|
#encode_string(arg) ⇒ Object
52
53
54
55
56
57
58
59
60
61
62
63
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 52
def encode_string(arg)
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
size = lpad_int(arg.size)
value = rpad(arg, ceil32(arg.size))
size + value
end
|
#encode_tuple(tuple, args) ⇒ Object
2
3
4
|
# File 'lib/abi_coder_rb/encode/encode_tuple.rb', line 2
def encode_tuple(tuple, args)
encode_types(tuple.types, args)
end
|
#encode_uint(arg, bits) ⇒ Object
27
28
29
30
31
32
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 27
def encode_uint(arg, bits)
raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**bits
lpad_int(arg)
end
|
#encode_uint256(arg) ⇒ Object
34
35
36
|
# File 'lib/abi_coder_rb/encode/encode_primitive_type.rb', line 34
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_256bit(value) ⇒ Object
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
# File 'lib/abi_coder_rb/utils.rb', line 65
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) ⇒ Object
rename to lpad32 or such - why? why not? example: lpad(“hello”, ‘x’, 10) => “xxxxxxhello”
30
31
32
33
34
35
|
# 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?
52
53
54
55
56
57
58
59
|
# File 'lib/abi_coder_rb/utils.rb', line 52
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) ⇒ Object
rename to lpad32_int or such - why? why not?
38
39
40
41
42
43
44
45
46
47
48
49
|
# File 'lib/abi_coder_rb/utils.rb', line 38
def lpad_int(n)
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)
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
|