abi_coder_rb 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/Gemfile.lock +25 -1
- data/README.md +8 -5
- data/lib/abi_coder_rb/decode/decode_primitive_type.rb +1 -1
- data/lib/abi_coder_rb/encode/encode_primitive_type.rb +1 -59
- data/lib/abi_coder_rb/utils.rb +101 -107
- data/lib/abi_coder_rb/version.rb +1 -1
- data/lib/abi_coder_rb.rb +2 -15
- data/lib/periphery/event_decoder.rb +179 -0
- metadata +17 -3
- data/flattened_abi_coder.rb +0 -669
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f20aa23f421459866cdbd1cd485027ec5fec4b52af891adc9c4025f247b42a9
|
4
|
+
data.tar.gz: 439e3e33e16d8cd9b20267f6e35218adb3c542f7aa6e80482c8d9aafee220bd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c423bbce7507e4dc2d79d73355c65b84c8ceff0bc4c9e89045e23a461084898c9b97e35225e6b0b8e0173ddb05d1dfd13c3df5e7071cfe05960254037e2a3f9
|
7
|
+
data.tar.gz: d2977279469c49bc63d67c360d12b281bb2442856dddc166ef784b1311198afb0dabbe19604160c53edd5c54047b031113a6ae18e5ef60b93611b68fa9f8b657
|
data/.DS_Store
CHANGED
Binary file
|
data/Gemfile.lock
CHANGED
@@ -1,16 +1,37 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
abi_coder_rb (0.2.
|
4
|
+
abi_coder_rb (0.2.2)
|
5
|
+
activesupport
|
5
6
|
|
6
7
|
GEM
|
7
8
|
remote: https://rubygems.org/
|
8
9
|
specs:
|
10
|
+
activesupport (7.1.1)
|
11
|
+
base64
|
12
|
+
bigdecimal
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
+
connection_pool (>= 2.2.5)
|
15
|
+
drb
|
16
|
+
i18n (>= 1.6, < 2)
|
17
|
+
minitest (>= 5.1)
|
18
|
+
mutex_m
|
19
|
+
tzinfo (~> 2.0)
|
9
20
|
ast (2.4.2)
|
21
|
+
base64 (0.1.1)
|
22
|
+
bigdecimal (3.1.4)
|
23
|
+
concurrent-ruby (1.2.2)
|
24
|
+
connection_pool (2.4.1)
|
10
25
|
diff-lcs (1.5.0)
|
26
|
+
drb (2.1.1)
|
27
|
+
ruby2_keywords
|
28
|
+
i18n (1.14.1)
|
29
|
+
concurrent-ruby (~> 1.0)
|
11
30
|
json (2.6.3)
|
12
31
|
keccak (1.3.1)
|
13
32
|
language_server-protocol (3.17.0.3)
|
33
|
+
minitest (5.20.0)
|
34
|
+
mutex_m (0.1.2)
|
14
35
|
parallel (1.23.0)
|
15
36
|
parser (3.2.2.4)
|
16
37
|
ast (~> 2.4.1)
|
@@ -47,6 +68,9 @@ GEM
|
|
47
68
|
rubocop-ast (1.30.0)
|
48
69
|
parser (>= 3.2.1.0)
|
49
70
|
ruby-progressbar (1.13.0)
|
71
|
+
ruby2_keywords (0.0.5)
|
72
|
+
tzinfo (2.0.6)
|
73
|
+
concurrent-ruby (~> 1.0)
|
50
74
|
unicode-display_width (2.5.0)
|
51
75
|
|
52
76
|
PLATFORMS
|
data/README.md
CHANGED
@@ -4,11 +4,14 @@ modified from https://github.com/rubycocos/blockchain/blob/master/abicoder
|
|
4
4
|
|
5
5
|
for better readability code and deep learning abi codec.
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
Changes compared to original code:
|
8
|
+
|
9
|
+
0. Adjusted files structure
|
10
|
+
1. The biggest change for readability is that the 'data' to decode in every decode_* function is no longer exact but now includes both the data needed for decoding and the remaining data. This change means that in the entry point('AbiCoderRb.decode'), there's no longer a need to calculate the precise data required for decoding for each type. This simplification streamlines the code.
|
11
|
+
2. Fixed some encoding end decoding bugs.
|
12
|
+
3. Use string to describe any abi type. This is for compatibility with other abi libs.
|
13
|
+
4. Added pre- encoding and post- decoding callbacks to facilitate transforming data before encoding and after decoding. See [1](https://github.com/wuminzhe/abi_coder_rb/blob/main/spec/transform_before_encode_spec.rb#L4C1-L12C4) [2](https://github.com/wuminzhe/abi_coder_rb/blob/main/spec/web3_js_abitests_spec.rb#L27C1-L49C4)
|
14
|
+
5. pass all web3.js tests in [encodeDecodeParams.test.ts](https://github.com/web3/web3.js/blob/c490c1814da646a83c6a5f7fee643e35507c9344/packages/web3-eth-abi/test/unit/encodeDecodeParams.test.ts). That is about 1024 unit tests from fixture [abitestsdata.json](https://github.com/web3/web3.js/blob/c490c1814da646a83c6a5f7fee643e35507c9344/packages/web3-eth-abi/test/fixtures/abitestsdata.json).
|
12
15
|
|
13
16
|
Also, some code was modified to compile to wasm. Try it online: https://wuminzhe.github.io/abi.html
|
14
17
|
|
@@ -39,7 +39,7 @@ module AbiCoderRb
|
|
39
39
|
## raise EncodingError or ArgumentError - why? why not?
|
40
40
|
raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
|
41
41
|
|
42
|
-
hex_to_bin(
|
42
|
+
hex_to_bin(int_to_abi_signed_256bit(arg))
|
43
43
|
end
|
44
44
|
|
45
45
|
def encode_bool(arg)
|
@@ -97,62 +97,4 @@ module AbiCoderRb
|
|
97
97
|
raise EncodingError, "Could not parse address: #{arg}"
|
98
98
|
end
|
99
99
|
end
|
100
|
-
|
101
|
-
private
|
102
|
-
|
103
|
-
def int_to_eth_abi(value, bits)
|
104
|
-
# 计算补码,如果是负数
|
105
|
-
value = 2**bits + value if value < 0
|
106
|
-
|
107
|
-
# 将值转换为十六进制字符串
|
108
|
-
hex = (value % 2**bits).to_s(16)
|
109
|
-
hex = "0#{hex}" if hex.length.odd?
|
110
|
-
|
111
|
-
# 确保字符串长度为16位(8个字节)
|
112
|
-
hex.rjust(bits / 4, "0")
|
113
|
-
end
|
114
|
-
|
115
|
-
###########
|
116
|
-
# encoding helpers / utils
|
117
|
-
# with "hard-coded" fill symbol as BYTE_ZERO
|
118
|
-
|
119
|
-
def rpad(bin, l = 32) ## note: same as builtin String#ljust !!!
|
120
|
-
# note: default l word is 32 bytes
|
121
|
-
return bin if bin.size >= l
|
122
|
-
|
123
|
-
bin + BYTE_ZERO * (l - bin.size)
|
124
|
-
end
|
125
|
-
|
126
|
-
## rename to lpad32 or such - why? why not?
|
127
|
-
def lpad(bin) ## note: same as builtin String#rjust !!!
|
128
|
-
l = 32 # NOTE: default l word is 32 bytes
|
129
|
-
return bin if bin.size >= l
|
130
|
-
|
131
|
-
BYTE_ZERO * (l - bin.size) + bin
|
132
|
-
end
|
133
|
-
|
134
|
-
## rename to lpad32_int or such - why? why not?
|
135
|
-
def lpad_int(n)
|
136
|
-
raise ArgumentError, "Integer invalid or out of range: #{n}" unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
|
137
|
-
|
138
|
-
hex = n.to_s(16)
|
139
|
-
hex = "0#{hex}" if hex.length.odd? # wasm, no .odd?
|
140
|
-
bin = hex_to_bin(hex)
|
141
|
-
|
142
|
-
lpad(bin)
|
143
|
-
end
|
144
|
-
|
145
|
-
## rename to lpad32_hex or such - why? why not?
|
146
|
-
def lpad_hex(hex)
|
147
|
-
raise TypeError, "Value must be a string" unless hex.is_a?(::String)
|
148
|
-
raise TypeError, "Non-hexadecimal digit found" unless hex =~ /\A[0-9a-fA-F]*\z/
|
149
|
-
|
150
|
-
bin = hex_to_bin(hex)
|
151
|
-
|
152
|
-
lpad(bin)
|
153
|
-
end
|
154
|
-
|
155
|
-
def ceil32(x)
|
156
|
-
x % 32 == 0 ? x : (x + 32 - x % 32)
|
157
|
-
end
|
158
100
|
end
|
data/lib/abi_coder_rb/utils.rb
CHANGED
@@ -1,111 +1,105 @@
|
|
1
1
|
module AbiCoderRb
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
# 取反加一以计算补码,然后转换为负数
|
104
|
-
-((binary_str.tr("01", "10").to_i(2) + 1) & ((1 << bits) - 1))
|
105
|
-
else # 正数
|
106
|
-
binary_str.to_i(2)
|
107
|
-
end
|
108
|
-
end
|
2
|
+
def hex_to_bin(hex)
|
3
|
+
hex = hex[2..] if %w[0x 0X].include?(hex[0, 2]) ## cut-of leading 0x or 0X if present
|
4
|
+
hex.scan(/../).map { |x| x.hex.chr }.join
|
5
|
+
end
|
6
|
+
alias hex hex_to_bin
|
7
|
+
|
8
|
+
def bin_to_hex(bin)
|
9
|
+
bin.each_byte.map { |byte| "%02x" % byte }.join
|
10
|
+
end
|
11
|
+
|
12
|
+
def hex?(str)
|
13
|
+
str.start_with?("0x") && str.length.even? && str[2..].match?(/\A\b[0-9a-fA-F]+\b\z/)
|
14
|
+
end
|
15
|
+
|
16
|
+
###########
|
17
|
+
# encoding helpers / utils
|
18
|
+
# with "hard-coded" fill symbol as BYTE_ZERO
|
19
|
+
|
20
|
+
def rpad(bin, l = 32) ## note: same as builtin String#ljust !!!
|
21
|
+
# note: default l word is 32 bytes
|
22
|
+
return bin if bin.size >= l
|
23
|
+
|
24
|
+
bin + BYTE_ZERO * (l - bin.size)
|
25
|
+
end
|
26
|
+
|
27
|
+
## rename to lpad32 or such - why? why not?
|
28
|
+
# example:
|
29
|
+
# lpad("hello", 'x', 10) => "xxxxxxhello"
|
30
|
+
def lpad(bin) ## note: same as builtin String#rjust !!!
|
31
|
+
l = 32 # NOTE: default l word is 32 bytes
|
32
|
+
return bin if bin.size >= l
|
33
|
+
|
34
|
+
BYTE_ZERO * (l - bin.size) + bin
|
35
|
+
end
|
36
|
+
|
37
|
+
## rename to lpad32_int or such - why? why not?
|
38
|
+
def lpad_int(n)
|
39
|
+
unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
|
40
|
+
raise ArgumentError,
|
41
|
+
"Integer invalid or out of range: #{n}"
|
42
|
+
end
|
43
|
+
|
44
|
+
hex = n.to_s(16)
|
45
|
+
hex = "0#{hex}" if hex.length.odd? # wasm, no .odd?
|
46
|
+
bin = hex_to_bin(hex)
|
47
|
+
|
48
|
+
lpad(bin)
|
49
|
+
end
|
50
|
+
|
51
|
+
## rename to lpad32_hex or such - why? why not?
|
52
|
+
def lpad_hex(hex)
|
53
|
+
raise TypeError, "Value must be a string" unless hex.is_a?(::String)
|
54
|
+
raise TypeError, "Non-hexadecimal digit found" unless hex =~ /\A[0-9a-fA-F]*\z/
|
55
|
+
|
56
|
+
bin = hex_to_bin(hex)
|
57
|
+
|
58
|
+
lpad(bin)
|
59
|
+
end
|
60
|
+
|
61
|
+
def ceil32(x)
|
62
|
+
x % 32 == 0 ? x : (x + 32 - x % 32)
|
63
|
+
end
|
64
|
+
|
65
|
+
def int_to_abi_signed_256bit(value)
|
66
|
+
# 确保值在256位有符号整数范围内
|
67
|
+
min = -2**255
|
68
|
+
max = 2**255 - 1
|
69
|
+
raise "Value out of range" if value < min || value > max
|
70
|
+
|
71
|
+
# 为负数计算补码
|
72
|
+
value = (1 << 256) + value if value < 0
|
73
|
+
|
74
|
+
# 转换为十六进制字符串
|
75
|
+
hex_str = value.to_s(16)
|
76
|
+
|
77
|
+
# 确保字符串长度为64字符(256位)
|
78
|
+
hex_str.rjust(64, "0")
|
79
|
+
end
|
80
|
+
|
81
|
+
def abi_to_int_signed(hex_str, bits)
|
82
|
+
hex_str = "0x#{hex_str}" if hex_str[0, 2] != "0x" || hex_str[0, 2] != "0X"
|
83
|
+
|
84
|
+
# 计算预期的十六进制字符串长度
|
85
|
+
expected_length = bits / 4
|
86
|
+
extended_hex_str = if hex_str.length < expected_length
|
87
|
+
# 如果输入长度小于预期,根据首位字符扩展字符串
|
88
|
+
extend_char = hex_str[0] == "f" ? "f" : "0"
|
89
|
+
extend_char * (expected_length - hex_str.length) + hex_str
|
90
|
+
else
|
91
|
+
hex_str
|
92
|
+
end
|
93
|
+
|
94
|
+
# 将十六进制字符串转换为二进制字符串
|
95
|
+
binary_str = extended_hex_str.to_i(16).to_s(2).rjust(bits, extended_hex_str[0])
|
96
|
+
|
97
|
+
# 检查符号位并转换为整数
|
98
|
+
if binary_str[0] == "1" # 负数
|
99
|
+
# 取反加一以计算补码,然后转换为负数
|
100
|
+
-((binary_str.tr("01", "10").to_i(2) + 1) & ((1 << bits) - 1))
|
101
|
+
else # 正数
|
102
|
+
binary_str.to_i(2)
|
109
103
|
end
|
110
104
|
end
|
111
105
|
end
|
data/lib/abi_coder_rb/version.rb
CHANGED
data/lib/abi_coder_rb.rb
CHANGED
@@ -9,6 +9,8 @@ require_relative "abi_coder_rb/types"
|
|
9
9
|
require_relative "abi_coder_rb/decode"
|
10
10
|
require_relative "abi_coder_rb/encode"
|
11
11
|
|
12
|
+
require_relative "periphery/event_decoder"
|
13
|
+
|
12
14
|
module AbiCoderRb
|
13
15
|
class DecodingError < StandardError; end
|
14
16
|
class EncodingError < StandardError; end
|
@@ -18,27 +20,12 @@ module AbiCoderRb
|
|
18
20
|
BYTE_EMPTY = "".b.freeze
|
19
21
|
BYTE_ZERO = "\x00".b.freeze
|
20
22
|
BYTE_ONE = "\x01".b.freeze ## note: used for encoding bool for now
|
21
|
-
BYTE_FF = "\xff".b.freeze
|
22
23
|
|
23
24
|
UINT_MAX = 2**256 - 1 ## same as 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
24
25
|
UINT_MIN = 0
|
25
26
|
INT_MAX = 2**255 - 1 ## same as 57896044618658097711785492504343953926634992332820282019728792003956564819967
|
26
27
|
INT_MIN = -2**255 ## same as -57896044618658097711785492504343953926634992332820282019728792003956564819968
|
27
28
|
|
28
|
-
def hex_to_bin(hex) # convert hex(adecimal) string to binary string
|
29
|
-
hex = hex[2..] if %w[0x 0X].include?(hex[0, 2]) ## cut-of leading 0x or 0X if present
|
30
|
-
hex.scan(/../).map { |x| x.hex.chr }.join
|
31
|
-
end
|
32
|
-
alias hex hex_to_bin
|
33
|
-
|
34
|
-
def bin_to_hex(bin) # convert binary string to hex string
|
35
|
-
bin.each_byte.map { |byte| "%02x" % byte }.join
|
36
|
-
end
|
37
|
-
|
38
|
-
def hex?(str)
|
39
|
-
str.start_with?("0x") && str.length.even? && str[2..].match?(/\A\b[0-9a-fA-F]+\b\z/)
|
40
|
-
end
|
41
|
-
|
42
29
|
attr_accessor :before_encoding_action, :after_decoding_action
|
43
30
|
|
44
31
|
def before_encoding(action)
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext/string"
|
3
|
+
|
4
|
+
class EventDecoder
|
5
|
+
include AbiCoderRb
|
6
|
+
|
7
|
+
attr_reader :event_abi,
|
8
|
+
# indexed_topic_inputs
|
9
|
+
:indexed_topic_inputs, :indexed_topic_types,
|
10
|
+
# data_inputs
|
11
|
+
:data_inputs, :data_type_str, :data_field_names, :data_field_names_flattened
|
12
|
+
|
13
|
+
def initialize(event_abi)
|
14
|
+
@event_abi = event_abi
|
15
|
+
@indexed_topic_inputs, @data_inputs = event_abi["inputs"].partition { |input| input["indexed"] }
|
16
|
+
|
17
|
+
# indexed_topic_inputs:
|
18
|
+
@indexed_topic_types = @indexed_topic_inputs.map { |input| input["type"] }
|
19
|
+
|
20
|
+
# data_inputs:
|
21
|
+
@data_fields = fields_of(@data_inputs)
|
22
|
+
|
23
|
+
@data_type_str = fields_type_str(@data_fields)
|
24
|
+
|
25
|
+
@data_field_names = fields_names(@data_fields)
|
26
|
+
@data_field_names_flattened = fields_names_flatten(@data_fields)
|
27
|
+
|
28
|
+
# add after_decoding action
|
29
|
+
after_decoding lambda { |type, value|
|
30
|
+
if type == "address"
|
31
|
+
"0x#{value}"
|
32
|
+
elsif type.start_with?("bytes")
|
33
|
+
"0x#{bin_to_hex(value)}"
|
34
|
+
else
|
35
|
+
value
|
36
|
+
end
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def decode_topics(topics, with_names: false)
|
41
|
+
topics = topics[1..] if topics.count == @indexed_topic_inputs.count + 1 && @event_abi["anonymous"] == false
|
42
|
+
|
43
|
+
raise "topics count not match" if topics.count != @indexed_topic_inputs.count
|
44
|
+
|
45
|
+
values = topics.each_with_index.map do |topic, i|
|
46
|
+
indexed_topic_type = @indexed_topic_types[i]
|
47
|
+
decode(indexed_topic_type, hex_to_bin(topic))
|
48
|
+
end
|
49
|
+
|
50
|
+
if with_names
|
51
|
+
combine(@indexed_topic_inputs.map { |input| input["name"].underscore }, values)
|
52
|
+
else
|
53
|
+
values
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def decode_data(data, flatten: true, with_names: false)
|
58
|
+
return with_names ? {} : [] if @data_type_str == "()"
|
59
|
+
|
60
|
+
data_values = decode(@data_type_str, hex_to_bin(data))
|
61
|
+
|
62
|
+
case flatten
|
63
|
+
when true
|
64
|
+
if with_names
|
65
|
+
combine(@data_field_names_flattened, data_values.flatten)
|
66
|
+
else
|
67
|
+
data_values.flatten
|
68
|
+
end
|
69
|
+
when false
|
70
|
+
if with_names
|
71
|
+
combine(@data_field_names, data_values)
|
72
|
+
else
|
73
|
+
data_values
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# fields:
|
81
|
+
# [
|
82
|
+
# ["root", "bytes32"],
|
83
|
+
# ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
|
84
|
+
# ]
|
85
|
+
#
|
86
|
+
# returns:
|
87
|
+
# '(bytes32,(address,uint256,uint256,address,uint256,address,bytes))'
|
88
|
+
def fields_type_str(fields)
|
89
|
+
"(#{
|
90
|
+
fields.map do |_name, type|
|
91
|
+
if type.is_a?(::Array)
|
92
|
+
fields_type_str(type)
|
93
|
+
else
|
94
|
+
type
|
95
|
+
end
|
96
|
+
end.join(",")
|
97
|
+
})"
|
98
|
+
end
|
99
|
+
|
100
|
+
# fields:
|
101
|
+
# [
|
102
|
+
# ["root", "bytes32"],
|
103
|
+
# ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
|
104
|
+
# ]
|
105
|
+
#
|
106
|
+
# returns:
|
107
|
+
# ["root", {"message" => ["channel", "index", "fromChainId", "from", "toChainId", "to", "gasLimit", "encoded"]}
|
108
|
+
def fields_names(fields)
|
109
|
+
fields.map do |name, type|
|
110
|
+
name = name.underscore
|
111
|
+
if type.is_a?(::Array)
|
112
|
+
{ name => fields_names(type) }
|
113
|
+
elsif type.is_a?(::String)
|
114
|
+
name
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# fields:
|
120
|
+
# [
|
121
|
+
# ["root", "bytes32"],
|
122
|
+
# ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
|
123
|
+
# ]
|
124
|
+
#
|
125
|
+
# returns:
|
126
|
+
# ["root", "message_channel", "message_index", "message_fromChainId", "message_from", "message_toChainId", "message_to", "message_gasLimit", "message_encoded"]
|
127
|
+
def fields_names_flatten(fields, prefix = nil)
|
128
|
+
fields.map do |name, type|
|
129
|
+
name = name.underscore
|
130
|
+
if type.is_a?(::Array)
|
131
|
+
fields_names_flatten(
|
132
|
+
type,
|
133
|
+
prefix.nil? ? name : "#{prefix}.#{name}"
|
134
|
+
)
|
135
|
+
elsif type.is_a?(::String)
|
136
|
+
prefix.nil? ? name : "#{prefix}.#{name}"
|
137
|
+
end
|
138
|
+
end.flatten
|
139
|
+
end
|
140
|
+
|
141
|
+
# returns:
|
142
|
+
# [
|
143
|
+
# ["root", "bytes32"],
|
144
|
+
# ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
|
145
|
+
# ]
|
146
|
+
def fields_of(inputs)
|
147
|
+
inputs.map do |input|
|
148
|
+
if input["type"] == "tuple"
|
149
|
+
[input["name"], fields_of(input["components"])]
|
150
|
+
elsif input["type"] == "enum"
|
151
|
+
[input["name"], "uint8"]
|
152
|
+
else
|
153
|
+
[input["name"], input["type"]]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# params:
|
159
|
+
# keys = ['key1', 'key2' => ['key2_1', 'key2_2']]
|
160
|
+
# values = [1, [2.1, 2.2]]
|
161
|
+
#
|
162
|
+
# returns:
|
163
|
+
# {"key1"=>1, "key2"=>{"key2_1"=>2.1, "key2_2"=>2.2}}
|
164
|
+
def combine(keys, values)
|
165
|
+
result = {}
|
166
|
+
|
167
|
+
keys.each_with_index do |key, index|
|
168
|
+
if key.is_a?(Hash)
|
169
|
+
key.each do |k, v|
|
170
|
+
result[k] = combine(v, values[index])
|
171
|
+
end
|
172
|
+
else
|
173
|
+
result[key] = values[index]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
result
|
178
|
+
end
|
179
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abi_coder_rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aki Wu
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: keccak
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -41,7 +55,6 @@ files:
|
|
41
55
|
- README.md
|
42
56
|
- Rakefile
|
43
57
|
- flatten.rb
|
44
|
-
- flattened_abi_coder.rb
|
45
58
|
- lib/.DS_Store
|
46
59
|
- lib/abi_coder_rb.rb
|
47
60
|
- lib/abi_coder_rb/.DS_Store
|
@@ -59,6 +72,7 @@ files:
|
|
59
72
|
- lib/abi_coder_rb/types.rb
|
60
73
|
- lib/abi_coder_rb/utils.rb
|
61
74
|
- lib/abi_coder_rb/version.rb
|
75
|
+
- lib/periphery/event_decoder.rb
|
62
76
|
- sig/abi_coder_rb.rbs
|
63
77
|
homepage: https://github.com/wuminzhe/abi_coder_rb
|
64
78
|
licenses:
|
data/flattened_abi_coder.rb
DELETED
@@ -1,669 +0,0 @@
|
|
1
|
-
# Generated from https://github.com/wuminzhe/abi_coder_rb
|
2
|
-
module AbiCoderRb
|
3
|
-
def decode_array(type, data)
|
4
|
-
size = decode_uint256(data[0, 32])
|
5
|
-
raise DecodingError, "Too many elements: #{size}" if size > 100_000
|
6
|
-
subtype = type.subtype
|
7
|
-
if subtype.dynamic?
|
8
|
-
raise DecodingError, "Not enough data for head" unless data.size >= 32 + 32 * size
|
9
|
-
start_positions = (1..size).map { |i| 32 + decode_uint256(data[32 * i, 32]) }
|
10
|
-
start_positions.push(data.size)
|
11
|
-
outputs = (0...size).map { |i| data[start_positions[i]...start_positions[i + 1]] }
|
12
|
-
outputs.map { |out| decode_type(subtype, out) }
|
13
|
-
else
|
14
|
-
(0...size).map { |i| decode_type(subtype, data[(32 + subtype.size * i)..]) }
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
module AbiCoderRb
|
19
|
-
def decode_fixed_array(type, data)
|
20
|
-
l = type.dim
|
21
|
-
subtype = type.subtype
|
22
|
-
if subtype.dynamic?
|
23
|
-
start_positions = (0...l).map { |i| decode_uint256(data[32 * i, 32]) }
|
24
|
-
start_positions.push(data.size)
|
25
|
-
outputs = (0...l).map { |i| data[start_positions[i]...start_positions[i + 1]] }
|
26
|
-
outputs.map { |out| decode_type(subtype, out) }
|
27
|
-
else
|
28
|
-
(0...l).map { |i| decode_type(subtype, data[subtype.size * i, subtype.size]) }
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
module AbiCoderRb
|
33
|
-
def decode_primitive_type(type, data)
|
34
|
-
result =
|
35
|
-
case type
|
36
|
-
when Uint
|
37
|
-
decode_uint256(data[0, 32])
|
38
|
-
when Int
|
39
|
-
Utils.abi_to_int_signed(bin_to_hex(data[0, 32]), type.bits)
|
40
|
-
when Bool
|
41
|
-
data[31] == BYTE_ONE
|
42
|
-
when String
|
43
|
-
size = decode_uint256(data[0, 32])
|
44
|
-
data[32...(32 + size)].force_encoding("UTF-8")
|
45
|
-
when Bytes
|
46
|
-
size = decode_uint256(data[0, 32])
|
47
|
-
data[32...(32 + size)]
|
48
|
-
when FixedBytes
|
49
|
-
data[0, type.length]
|
50
|
-
when Address
|
51
|
-
bin_to_hex(data[12...32]).force_encoding("UTF-8")
|
52
|
-
else
|
53
|
-
raise DecodingError, "Unknown primitive type: #{type.class.name} #{type.format}"
|
54
|
-
end
|
55
|
-
result = after_decoding_action.call(type.format, result) if after_decoding_action
|
56
|
-
result
|
57
|
-
end
|
58
|
-
private
|
59
|
-
def decode_uint256(bin)
|
60
|
-
bin_to_hex(bin).to_i(16)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
module AbiCoderRb
|
64
|
-
def decode_tuple(type, data)
|
65
|
-
decode_types(type.types, data)
|
66
|
-
end
|
67
|
-
private
|
68
|
-
def decode_types(types, data)
|
69
|
-
start_positions = start_positions(types, data)
|
70
|
-
types.map.with_index do |type, index|
|
71
|
-
start_position = start_positions[index]
|
72
|
-
decode_type(type, data[start_position..])
|
73
|
-
end
|
74
|
-
end
|
75
|
-
def start_positions(types, data)
|
76
|
-
start_positions = ::Array.new(types.size)
|
77
|
-
offset = 0
|
78
|
-
types.each_with_index do |type, index|
|
79
|
-
if type.dynamic?
|
80
|
-
start_positions[index] = decode_uint256(data[offset, 32])
|
81
|
-
offset += 32
|
82
|
-
else
|
83
|
-
start_positions[index] = offset
|
84
|
-
offset += type.size
|
85
|
-
end
|
86
|
-
end
|
87
|
-
start_positions
|
88
|
-
end
|
89
|
-
end
|
90
|
-
module AbiCoderRb
|
91
|
-
def decode(type_str, data)
|
92
|
-
raise DecodingError, "Empty data" if data.nil? || data.empty?
|
93
|
-
decode_type(Type.parse(type_str), data)
|
94
|
-
end
|
95
|
-
private
|
96
|
-
def decode_type(type, data)
|
97
|
-
case type
|
98
|
-
when Tuple ## todo: support empty (unit) tuple - why? why not?
|
99
|
-
decode_tuple(type, data)
|
100
|
-
when FixedArray # static-sized arrays
|
101
|
-
decode_fixed_array(type, data)
|
102
|
-
when Array
|
103
|
-
decode_array(type, data)
|
104
|
-
else
|
105
|
-
decode_primitive_type(type, data)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
module AbiCoderRb
|
110
|
-
def encode_array(type, args)
|
111
|
-
raise ArgumentError, "arg must be an array" unless args.is_a?(::Array)
|
112
|
-
head = "".b
|
113
|
-
tail = "".b # 使用二进制字符串
|
114
|
-
head += encode_uint256(args.size)
|
115
|
-
subtype = type.subtype
|
116
|
-
args.each do |arg|
|
117
|
-
if subtype.dynamic?
|
118
|
-
head += encode_uint256(32 * args.size + tail.size)
|
119
|
-
tail += encode_type(subtype, arg)
|
120
|
-
else
|
121
|
-
head += encode_type(subtype, arg)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
head + tail
|
125
|
-
end
|
126
|
-
end
|
127
|
-
module AbiCoderRb
|
128
|
-
def encode_fixed_array(type, args)
|
129
|
-
raise ArgumentError, "arg must be an array" unless args.is_a?(::Array)
|
130
|
-
raise ArgumentError, "Wrong array size: found #{args.size}, expecting #{type.dim}" unless args.size == type.dim
|
131
|
-
subtype = type.subtype
|
132
|
-
if subtype.dynamic?
|
133
|
-
head = "".b
|
134
|
-
tail = "".b
|
135
|
-
args.each do |arg|
|
136
|
-
head += encode_uint256(32 * args.size + tail.size)
|
137
|
-
tail += encode_type(subtype, arg)
|
138
|
-
end
|
139
|
-
head + tail
|
140
|
-
else
|
141
|
-
args.map { |arg| encode_type(type.subtype, arg) }.join
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
module AbiCoderRb
|
146
|
-
def encode_primitive_type(type, arg)
|
147
|
-
arg = before_encoding_action.call(type.format, arg) if before_encoding_action
|
148
|
-
case type
|
149
|
-
when Uint
|
150
|
-
encode_uint(arg, type.bits)
|
151
|
-
when Int
|
152
|
-
encode_int(arg, type.bits)
|
153
|
-
when Bool
|
154
|
-
encode_bool(arg)
|
155
|
-
when String
|
156
|
-
encode_string(arg)
|
157
|
-
when FixedBytes
|
158
|
-
encode_bytes(arg, type.length)
|
159
|
-
when Bytes
|
160
|
-
encode_bytes(arg)
|
161
|
-
when Address
|
162
|
-
encode_address(arg)
|
163
|
-
else
|
164
|
-
raise EncodingError, "Unknown type: #{type}"
|
165
|
-
end
|
166
|
-
end
|
167
|
-
def encode_uint(arg, bits)
|
168
|
-
raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
|
169
|
-
raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**bits
|
170
|
-
lpad_int(arg)
|
171
|
-
end
|
172
|
-
def encode_uint256(arg)
|
173
|
-
encode_uint(arg, 256)
|
174
|
-
end
|
175
|
-
def encode_int(arg, _bits)
|
176
|
-
raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
|
177
|
-
hex_to_bin(Utils.int_to_abi_signed_256bit(arg))
|
178
|
-
end
|
179
|
-
def encode_bool(arg)
|
180
|
-
raise ArgumentError, "arg is not bool: #{arg}" unless arg.is_a?(TrueClass) || arg.is_a?(FalseClass)
|
181
|
-
lpad(arg ? BYTE_ONE : BYTE_ZERO) ## was lpad_int( arg ? 1 : 0 )
|
182
|
-
end
|
183
|
-
def encode_string(arg)
|
184
|
-
raise EncodingError, "Expecting string: #{arg}" unless arg.is_a?(::String)
|
185
|
-
arg = arg.b if arg.encoding != "BINARY" ## was: name == 'UTF-8', wasm
|
186
|
-
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size > UINT_MAX
|
187
|
-
size = lpad_int(arg.size)
|
188
|
-
value = rpad(arg, ceil32(arg.size))
|
189
|
-
size + value
|
190
|
-
end
|
191
|
-
def encode_bytes(arg, length = nil)
|
192
|
-
raise EncodingError, "Expecting string: #{arg}" unless arg.is_a?(::String)
|
193
|
-
arg = arg.b if arg.encoding != Encoding::BINARY
|
194
|
-
if length # fixed length type
|
195
|
-
raise ValueOutOfBounds, "invalid bytes length #{arg.size}, should be #{length}" if arg.size > length
|
196
|
-
raise ValueOutOfBounds, "invalid bytes length #{length}" if length < 0 || length > 32
|
197
|
-
rpad(arg)
|
198
|
-
else # variable length type (if length is nil)
|
199
|
-
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size > UINT_MAX
|
200
|
-
size = lpad_int(arg.size)
|
201
|
-
value = rpad(arg, ceil32(arg.size))
|
202
|
-
size + value
|
203
|
-
end
|
204
|
-
end
|
205
|
-
def encode_address(arg)
|
206
|
-
if arg.is_a?(Integer)
|
207
|
-
lpad_int(arg)
|
208
|
-
elsif arg.size == 20
|
209
|
-
arg = arg.b if arg.encoding != Encoding::BINARY
|
210
|
-
lpad(arg)
|
211
|
-
elsif arg.size == 40
|
212
|
-
lpad_hex(arg)
|
213
|
-
elsif arg.size == 42 && arg[0, 2] == "0x" ## todo/fix: allow 0X too - why? why not?
|
214
|
-
lpad_hex(arg[2..-1])
|
215
|
-
else
|
216
|
-
raise EncodingError, "Could not parse address: #{arg}"
|
217
|
-
end
|
218
|
-
end
|
219
|
-
private
|
220
|
-
def int_to_eth_abi(value, bits)
|
221
|
-
value = 2**bits + value if value < 0
|
222
|
-
hex = (value % 2**bits).to_s(16)
|
223
|
-
hex = "0#{hex}" if hex.length.odd?
|
224
|
-
hex.rjust(bits / 4, "0")
|
225
|
-
end
|
226
|
-
def rpad(bin, l = 32) ## note: same as builtin String#ljust !!!
|
227
|
-
return bin if bin.size >= l
|
228
|
-
bin + BYTE_ZERO * (l - bin.size)
|
229
|
-
end
|
230
|
-
def lpad(bin) ## note: same as builtin String#rjust !!!
|
231
|
-
l = 32 # NOTE: default l word is 32 bytes
|
232
|
-
return bin if bin.size >= l
|
233
|
-
BYTE_ZERO * (l - bin.size) + bin
|
234
|
-
end
|
235
|
-
def lpad_int(n)
|
236
|
-
raise ArgumentError, "Integer invalid or out of range: #{n}" unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
|
237
|
-
hex = n.to_s(16)
|
238
|
-
hex = "0#{hex}" if hex.length.odd? # wasm, no .odd?
|
239
|
-
bin = hex_to_bin(hex)
|
240
|
-
lpad(bin)
|
241
|
-
end
|
242
|
-
def lpad_hex(hex)
|
243
|
-
raise TypeError, "Value must be a string" unless hex.is_a?(::String)
|
244
|
-
raise TypeError, "Non-hexadecimal digit found" unless hex =~ /\A[0-9a-fA-F]*\z/
|
245
|
-
bin = hex_to_bin(hex)
|
246
|
-
lpad(bin)
|
247
|
-
end
|
248
|
-
def ceil32(x)
|
249
|
-
x % 32 == 0 ? x : (x + 32 - x % 32)
|
250
|
-
end
|
251
|
-
end
|
252
|
-
module AbiCoderRb
|
253
|
-
def encode_tuple(tuple, args)
|
254
|
-
encode_types(tuple.types, args)
|
255
|
-
end
|
256
|
-
private
|
257
|
-
def encode_types(types, args)
|
258
|
-
raise ArgumentError, "args must be an array" unless args.is_a?(::Array)
|
259
|
-
unless args.size == types.size
|
260
|
-
raise ArgumentError,
|
261
|
-
"Wrong number of args: found #{args.size}, expecting #{types.size}"
|
262
|
-
end
|
263
|
-
head_size = types.map { |type| type.size || 32 }.sum
|
264
|
-
head = "".b
|
265
|
-
tail = "".b # 使用二进制字符串
|
266
|
-
types.each_with_index do |type, i|
|
267
|
-
if type.dynamic?
|
268
|
-
head += encode_uint256(head_size + tail.size)
|
269
|
-
tail += encode_type(type, args[i])
|
270
|
-
else
|
271
|
-
head += encode_type(type, args[i])
|
272
|
-
end
|
273
|
-
end
|
274
|
-
head + tail
|
275
|
-
end
|
276
|
-
end
|
277
|
-
module AbiCoderRb
|
278
|
-
def encode(type, value)
|
279
|
-
raise EncodingError, "Value can not be nil" if value.nil?
|
280
|
-
parsed = Type.parse(type)
|
281
|
-
encode_type(parsed, value)
|
282
|
-
end
|
283
|
-
private
|
284
|
-
def encode_type(type, value)
|
285
|
-
if type.is_a?(Tuple)
|
286
|
-
encode_tuple(type, value)
|
287
|
-
elsif type.is_a?(Array)
|
288
|
-
encode_array(type, value)
|
289
|
-
elsif type.is_a?(FixedArray)
|
290
|
-
encode_fixed_array(type, value)
|
291
|
-
else
|
292
|
-
encode_primitive_type(type, value)
|
293
|
-
end
|
294
|
-
end
|
295
|
-
end
|
296
|
-
module AbiCoderRb
|
297
|
-
class Type
|
298
|
-
class ParseError < StandardError; end
|
299
|
-
class Parser
|
300
|
-
TUPLE_TYPE_RX = /^\((.*)\)
|
301
|
-
((\[[0-9]*\])*)
|
302
|
-
/x
|
303
|
-
def self.parse(type)
|
304
|
-
type = type.strip
|
305
|
-
if type =~ TUPLE_TYPE_RX
|
306
|
-
types = _parse_tuple_type(::Regexp.last_match(1))
|
307
|
-
dims = _parse_dims(::Regexp.last_match(2))
|
308
|
-
parsed_types = types.map { |t| parse(t) }
|
309
|
-
return _parse_array_type(Tuple.new(parsed_types), dims)
|
310
|
-
end
|
311
|
-
base, sub, dims = _parse_base_type(type)
|
312
|
-
sub ||= 256 if type.start_with?("uint") || type.start_with?("int") # default to 256 if no sub given
|
313
|
-
_validate_base_type(base, sub)
|
314
|
-
subtype = case base
|
315
|
-
when "string" then String.new
|
316
|
-
when "bytes" then sub ? FixedBytes.new(sub) : Bytes.new
|
317
|
-
when "uint" then Uint.new(sub)
|
318
|
-
when "int" then Int.new(sub)
|
319
|
-
when "address" then Address.new
|
320
|
-
when "bool" then Bool.new
|
321
|
-
else
|
322
|
-
raise ParseError, "Unrecognized type base: #{base}"
|
323
|
-
end
|
324
|
-
_parse_array_type(subtype, dims)
|
325
|
-
end
|
326
|
-
BASE_TYPE_RX = /([a-z]*)
|
327
|
-
([0-9]*)
|
328
|
-
((\[[0-9]*\])*)
|
329
|
-
/x
|
330
|
-
def self._parse_base_type(str)
|
331
|
-
_, base, subscript, dimension = BASE_TYPE_RX.match(str).to_a
|
332
|
-
sub = subscript == "" ? nil : subscript.to_i
|
333
|
-
dims = _parse_dims(dimension)
|
334
|
-
[base, sub, dims]
|
335
|
-
end
|
336
|
-
def self._parse_dims(str)
|
337
|
-
dims = str.scan(/\[[0-9]*\]/)
|
338
|
-
dims.map do |dim|
|
339
|
-
size = dim[1...-1]
|
340
|
-
size == "" ? -1 : size.to_i
|
341
|
-
end
|
342
|
-
end
|
343
|
-
def self._parse_array_type(subtype, dims)
|
344
|
-
dims.each do |dim|
|
345
|
-
subtype = if dim == -1
|
346
|
-
Array.new(subtype)
|
347
|
-
else
|
348
|
-
FixedArray.new(subtype, dim)
|
349
|
-
end
|
350
|
-
end
|
351
|
-
subtype
|
352
|
-
end
|
353
|
-
def self._validate_base_type(base, sub)
|
354
|
-
case base
|
355
|
-
when "string"
|
356
|
-
raise ParseError, "String cannot have suffix" if sub
|
357
|
-
when "bytes"
|
358
|
-
raise ParseError, "Maximum 32 bytes for fixed-length bytes" if sub && sub > 32
|
359
|
-
when "uint", "int"
|
360
|
-
raise ParseError, "Integer type must have numerical suffix" unless sub
|
361
|
-
raise ParseError, "Integer size out of bounds" unless sub >= 8 && sub <= 256
|
362
|
-
raise ParseError, "Integer size must be multiple of 8" unless sub % 8 == 0
|
363
|
-
when "address"
|
364
|
-
raise ParseError, "Address cannot have suffix" if sub
|
365
|
-
when "bool"
|
366
|
-
raise ParseError, "Bool cannot have suffix" if sub
|
367
|
-
else
|
368
|
-
raise ParseError, "Unrecognized type base: #{base}"
|
369
|
-
end
|
370
|
-
end
|
371
|
-
def self._parse_tuple_type(str)
|
372
|
-
depth = 0
|
373
|
-
collected = []
|
374
|
-
current = ""
|
375
|
-
str.each_char do |c|
|
376
|
-
case c
|
377
|
-
when ","
|
378
|
-
if depth == 0
|
379
|
-
collected << current
|
380
|
-
current = ""
|
381
|
-
else
|
382
|
-
current += c
|
383
|
-
end
|
384
|
-
when "("
|
385
|
-
depth += 1
|
386
|
-
current += c
|
387
|
-
when ")"
|
388
|
-
depth -= 1
|
389
|
-
current += c
|
390
|
-
else
|
391
|
-
current += c
|
392
|
-
end
|
393
|
-
end
|
394
|
-
collected << current unless current.empty?
|
395
|
-
collected
|
396
|
-
end
|
397
|
-
end # class Parser
|
398
|
-
end # class Type
|
399
|
-
end # module ABI
|
400
|
-
module AbiCoderRb
|
401
|
-
class Type
|
402
|
-
def self.parse(type) ## convenience helper
|
403
|
-
Parser.parse(type)
|
404
|
-
end
|
405
|
-
def size
|
406
|
-
end
|
407
|
-
def dynamic?
|
408
|
-
size.nil?
|
409
|
-
end
|
410
|
-
def format
|
411
|
-
end
|
412
|
-
end
|
413
|
-
class Address < Type
|
414
|
-
def size
|
415
|
-
32
|
416
|
-
end
|
417
|
-
def format
|
418
|
-
"address"
|
419
|
-
end
|
420
|
-
def ==(other)
|
421
|
-
other.is_a?(Address)
|
422
|
-
end
|
423
|
-
end # class Address
|
424
|
-
class Bytes < Type
|
425
|
-
def size
|
426
|
-
nil
|
427
|
-
end
|
428
|
-
def format
|
429
|
-
"bytes"
|
430
|
-
end
|
431
|
-
def ==(other)
|
432
|
-
other.is_a?(Bytes)
|
433
|
-
end
|
434
|
-
end # class Bytes
|
435
|
-
class FixedBytes < Type
|
436
|
-
attr_reader :length
|
437
|
-
def initialize(length)
|
438
|
-
@length = length # in bytes (1,2,...32)
|
439
|
-
end
|
440
|
-
def size
|
441
|
-
32
|
442
|
-
end
|
443
|
-
def format
|
444
|
-
"bytes#{@length}"
|
445
|
-
end
|
446
|
-
def ==(other)
|
447
|
-
other.is_a?(FixedBytes) && @length == other.length
|
448
|
-
end
|
449
|
-
end # class FixedBytes
|
450
|
-
class Int < Type
|
451
|
-
attr_reader :bits
|
452
|
-
def initialize(bits = 256)
|
453
|
-
@bits = bits # in bits (8,16,...256)
|
454
|
-
end
|
455
|
-
def size
|
456
|
-
32
|
457
|
-
end
|
458
|
-
def format
|
459
|
-
"int#{@bits}"
|
460
|
-
end
|
461
|
-
def ==(other)
|
462
|
-
other.is_a?(Int) && @bits == other.bits
|
463
|
-
end
|
464
|
-
end # class Int
|
465
|
-
class Uint < Type
|
466
|
-
attr_reader :bits
|
467
|
-
def initialize(bits = 256)
|
468
|
-
@bits = bits # in bits (8,16,...256)
|
469
|
-
end
|
470
|
-
def size
|
471
|
-
32
|
472
|
-
end
|
473
|
-
def format
|
474
|
-
"uint#{@bits}"
|
475
|
-
end
|
476
|
-
def ==(other)
|
477
|
-
other.is_a?(Uint) && @bits == other.bits
|
478
|
-
end
|
479
|
-
end # class Uint
|
480
|
-
class Bool < Type
|
481
|
-
def size
|
482
|
-
32
|
483
|
-
end
|
484
|
-
def format
|
485
|
-
"bool"
|
486
|
-
end
|
487
|
-
def ==(other)
|
488
|
-
other.is_a?(Bool)
|
489
|
-
end
|
490
|
-
end # class Bool
|
491
|
-
class String < Type
|
492
|
-
def size
|
493
|
-
nil
|
494
|
-
end
|
495
|
-
def format
|
496
|
-
"string"
|
497
|
-
end
|
498
|
-
def ==(other)
|
499
|
-
other.is_a?(String)
|
500
|
-
end
|
501
|
-
end # class String
|
502
|
-
class Array < Type
|
503
|
-
attr_reader :subtype
|
504
|
-
def initialize(subtype)
|
505
|
-
@subtype = subtype
|
506
|
-
end
|
507
|
-
def size
|
508
|
-
nil
|
509
|
-
end
|
510
|
-
def format
|
511
|
-
"#{@subtype.format}[]"
|
512
|
-
end
|
513
|
-
def ==(other)
|
514
|
-
other.is_a?(Array) && @subtype == other.subtype
|
515
|
-
end
|
516
|
-
end # class Array
|
517
|
-
class FixedArray < Type
|
518
|
-
attr_reader :subtype, :dim
|
519
|
-
def initialize(subtype, dim)
|
520
|
-
@subtype = subtype
|
521
|
-
@dim = dim
|
522
|
-
end
|
523
|
-
def size
|
524
|
-
@subtype.dynamic? ? nil : @dim * subtype.size
|
525
|
-
end
|
526
|
-
def format
|
527
|
-
"#{@subtype.format}[#{@dim}]"
|
528
|
-
end
|
529
|
-
def ==(other)
|
530
|
-
other.is_a?(FixedArray) &&
|
531
|
-
@dim == other.dim &&
|
532
|
-
@subtype == other.subtype
|
533
|
-
end
|
534
|
-
end # class FixedArray
|
535
|
-
class Tuple < Type
|
536
|
-
attr_reader :types
|
537
|
-
def initialize(types)
|
538
|
-
@types = types
|
539
|
-
end
|
540
|
-
def size
|
541
|
-
s = 0
|
542
|
-
has_dynamic = false
|
543
|
-
@types.each do |type|
|
544
|
-
ts = type.size
|
545
|
-
if ts.nil?
|
546
|
-
has_dynamic = true
|
547
|
-
else
|
548
|
-
s += ts
|
549
|
-
end
|
550
|
-
end
|
551
|
-
return if has_dynamic
|
552
|
-
s
|
553
|
-
end
|
554
|
-
def format
|
555
|
-
"(#{@types.map { |t| t.format }.join(",")})" ## rebuild minimal string
|
556
|
-
end
|
557
|
-
def ==(other)
|
558
|
-
other.is_a?(Tuple) && @types == other.types
|
559
|
-
end
|
560
|
-
end # class Tuple
|
561
|
-
end # module ABI
|
562
|
-
module AbiCoderRb
|
563
|
-
module Utils
|
564
|
-
class << self
|
565
|
-
def hex_to_bin(hex)
|
566
|
-
hex = hex[2..] if %w[0x 0X].include?(hex[0, 2]) ## cut-of leading 0x or 0X if present
|
567
|
-
hex.scan(/../).map { |x| x.hex.chr }.join
|
568
|
-
end
|
569
|
-
def bin_to_hex(bin)
|
570
|
-
bin.each_byte.map { |byte| "%02x" % byte }.join
|
571
|
-
end
|
572
|
-
def hex?(str)
|
573
|
-
str.start_with?("0x") && str.length.even? && str[2..].match?(/\A\b[0-9a-fA-F]+\b\z/)
|
574
|
-
end
|
575
|
-
def lpad(str, sym, len)
|
576
|
-
return str if str.size >= len
|
577
|
-
sym * (len - str.size) + str
|
578
|
-
end
|
579
|
-
def zpad(str, len)
|
580
|
-
lpad str, BYTE_ZERO, len
|
581
|
-
end
|
582
|
-
def ffpad(str, len)
|
583
|
-
lpad str, BYTE_FF, len
|
584
|
-
end
|
585
|
-
def uint_to_big_endian(num, size)
|
586
|
-
raise "Can only serialize integers" unless num.is_a?(Integer)
|
587
|
-
raise "Cannot serialize negative integers" if num.negative?
|
588
|
-
raise "Integer too large (does not fit in #{size} bytes)" if size && num >= 256**size
|
589
|
-
s = if num.zero?
|
590
|
-
BYTE_EMPTY
|
591
|
-
else
|
592
|
-
hex = num.to_s(16)
|
593
|
-
hex = "0#{hex}" if hex.size.odd?
|
594
|
-
hex_to_bin hex
|
595
|
-
end
|
596
|
-
s = size ? "#{BYTE_ZERO * [0, size - s.size].max}#{s}" : s
|
597
|
-
zpad s, size
|
598
|
-
end
|
599
|
-
def int_to_abi_signed_256bit(value)
|
600
|
-
min = -2**255
|
601
|
-
max = 2**255 - 1
|
602
|
-
raise "Value out of range" if value < min || value > max
|
603
|
-
value = (1 << 256) + value if value < 0
|
604
|
-
hex_str = value.to_s(16)
|
605
|
-
hex_str.rjust(64, "0")
|
606
|
-
end
|
607
|
-
def int_to_abi_signed(value)
|
608
|
-
raise "Value out of range" if value < -2**31 || value > 2**31 - 1
|
609
|
-
hex_str = [value].pack("l>").unpack1("H*")
|
610
|
-
if value >= 0
|
611
|
-
hex_str.rjust(64, "0")
|
612
|
-
else
|
613
|
-
hex_str.rjust(64, "f")
|
614
|
-
end
|
615
|
-
end
|
616
|
-
def abi_to_int_signed(hex_str, bits)
|
617
|
-
hex_str = "0x#{hex_str}" if hex_str[0, 2] != "0x" || hex_str[0, 2] != "0X"
|
618
|
-
expected_length = bits / 4
|
619
|
-
extended_hex_str = if hex_str.length < expected_length
|
620
|
-
extend_char = hex_str[0] == "f" ? "f" : "0"
|
621
|
-
extend_char * (expected_length - hex_str.length) + hex_str
|
622
|
-
else
|
623
|
-
hex_str
|
624
|
-
end
|
625
|
-
binary_str = extended_hex_str.to_i(16).to_s(2).rjust(bits, extended_hex_str[0])
|
626
|
-
if binary_str[0] == "1" # 负数
|
627
|
-
-((binary_str.tr("01", "10").to_i(2) + 1) & ((1 << bits) - 1))
|
628
|
-
else # 正数
|
629
|
-
binary_str.to_i(2)
|
630
|
-
end
|
631
|
-
end
|
632
|
-
end
|
633
|
-
end
|
634
|
-
end
|
635
|
-
module AbiCoderRb
|
636
|
-
VERSION = "0.1.0"
|
637
|
-
end
|
638
|
-
module AbiCoderRb
|
639
|
-
class DecodingError < StandardError; end
|
640
|
-
class EncodingError < StandardError; end
|
641
|
-
class ValueError < StandardError; end
|
642
|
-
class ValueOutOfBounds < ValueError; end
|
643
|
-
BYTE_EMPTY = "".b.freeze
|
644
|
-
BYTE_ZERO = "\x00".b.freeze
|
645
|
-
BYTE_ONE = "\x01".b.freeze ## note: used for encoding bool for now
|
646
|
-
BYTE_FF = "\xff".b.freeze
|
647
|
-
UINT_MAX = 2**256 - 1 ## same as 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
648
|
-
UINT_MIN = 0
|
649
|
-
INT_MAX = 2**255 - 1 ## same as 57896044618658097711785492504343953926634992332820282019728792003956564819967
|
650
|
-
INT_MIN = -2**255 ## same as -57896044618658097711785492504343953926634992332820282019728792003956564819968
|
651
|
-
def hex_to_bin(hex) # convert hex(adecimal) string to binary string
|
652
|
-
hex = hex[2..] if %w[0x 0X].include?(hex[0, 2]) ## cut-of leading 0x or 0X if present
|
653
|
-
hex.scan(/../).map { |x| x.hex.chr }.join
|
654
|
-
end
|
655
|
-
alias hex hex_to_bin
|
656
|
-
def bin_to_hex(bin) # convert binary string to hex string
|
657
|
-
bin.each_byte.map { |byte| "%02x" % byte }.join
|
658
|
-
end
|
659
|
-
def hex?(str)
|
660
|
-
str.start_with?("0x") && str.length.even? && str[2..].match?(/\A\b[0-9a-fA-F]+\b\z/)
|
661
|
-
end
|
662
|
-
attr_accessor :before_encoding_action, :after_decoding_action
|
663
|
-
def before_encoding(action)
|
664
|
-
self.before_encoding_action = action
|
665
|
-
end
|
666
|
-
def after_decoding(action)
|
667
|
-
self.after_decoding_action = action
|
668
|
-
end
|
669
|
-
end
|