abi_coder_rb 0.2.8 → 0.2.9
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/README.md +40 -10
- data/lib/abi_coder_rb/decode/decode_array.rb +4 -4
- data/lib/abi_coder_rb/decode/decode_fixed_array.rb +5 -5
- data/lib/abi_coder_rb/decode/decode_tuple.rb +1 -1
- data/lib/abi_coder_rb/encode/encode_array.rb +5 -6
- data/lib/abi_coder_rb/encode/encode_fixed_array.rb +2 -2
- data/lib/abi_coder_rb/encode/encode_primitive_type.rb +2 -5
- data/lib/abi_coder_rb/encode/encode_tuple.rb +4 -4
- data/lib/abi_coder_rb/encode.rb +6 -17
- data/lib/abi_coder_rb/parser/abi_parser.rb +96 -0
- data/lib/abi_coder_rb/parser/abi_tokenizer.rb +57 -0
- data/lib/abi_coder_rb/type/parse.rb +45 -0
- data/lib/abi_coder_rb/type/types.rb +199 -0
- data/lib/abi_coder_rb/version.rb +1 -1
- data/lib/abi_coder_rb.rb +8 -6
- data/sig/abi_coder_rb/abi_parser.rbs +37 -0
- data/sig/abi_coder_rb/abi_tokenizer.rbs +15 -0
- data/sig/abi_coder_rb/type.rbs +5 -0
- metadata +9 -8
- data/.DS_Store +0 -0
- data/Gemfile.lock +0 -87
- data/lib/.DS_Store +0 -0
- data/lib/abi_coder_rb/.DS_Store +0 -0
- data/lib/abi_coder_rb/parser.rb +0 -156
- data/lib/abi_coder_rb/types.rb +0 -237
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de0b1b60b90aa2361dc35e4511026c148913c1a1dffc976514c1f7b00eaa54ce
|
4
|
+
data.tar.gz: 95bfd4733c2e7590cecf11104462da65481c0c0168dea1d5c280e4167cfe4cdf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83c52580ddf1abfba83900fe006c28ec647870d2fcc7618abafb50a5b3cc9190280124c45bd5941fa962292f13e37e02cff3b7550c6643ba63a9365df75cf043
|
7
|
+
data.tar.gz: 15285c84c9dac789f4274cc12d9ea93dd43f4be6e265402ef2efa63c592ad04ae847f9880eddd9c4684fb385f76e2f8ebf79dacca64c58254292117d2fae7120
|
data/README.md
CHANGED
@@ -1,19 +1,22 @@
|
|
1
1
|
# AbiCoderRb
|
2
2
|
|
3
|
-
|
3
|
+
This is a ruby implementation of the Ethereum ABI codec. It passes all the codec tests in [web3.js].
|
4
4
|
|
5
|
-
|
5
|
+
## Why I write this gem
|
6
6
|
|
7
|
-
|
7
|
+
Originally, I just wanted to gain a deeper understanding of the Ethereum ABI codec by studying this [code](https://github.com/rubycocos/blockchain/blob/master/abicoder).
|
8
8
|
|
9
|
-
|
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).
|
9
|
+
When I read the code, I found that the code is not easy to read and understand. So I decided to rewrite it. This gem is the result.
|
15
10
|
|
16
|
-
|
11
|
+
## Features
|
12
|
+
|
13
|
+
1. Clear files and code structure.
|
14
|
+
2. Use string to describe any abi type. This is for compatibility with other abi libs.
|
15
|
+
3. 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)
|
16
|
+
4. Passed 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).
|
17
|
+
5. support packed encoding similar to `abi.encodePacked`. See [test](./spec/packed_encoding_spec.rb)
|
18
|
+
6. Clear and robust ABI parser which parses the ABI string to an AST tree. See [abi_parser](lib/abi_coder_rb/parser/abi_parser.rb)
|
19
|
+
7. Can be compiled to wasm. Try it online: https://wuminzhe.github.io/abi.html
|
17
20
|
|
18
21
|
## Installation
|
19
22
|
|
@@ -81,6 +84,33 @@ end
|
|
81
84
|
Hello.new.world # => ["Hello World"]
|
82
85
|
```
|
83
86
|
|
87
|
+
### Encode packed
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
# encodePacked
|
91
|
+
type = "int64"
|
92
|
+
value = 17
|
93
|
+
|
94
|
+
encode(type, value, true) # => "0x0000000000000011"
|
95
|
+
```
|
96
|
+
|
97
|
+
### Encode values one by one
|
98
|
+
|
99
|
+
It is similar to `abi.encode(v1, v2, ..)` or `abi.encodePacked(v1, v2, ..)` in solidity.
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
types = ["int32", "uint64"]
|
103
|
+
values = [17, 17]
|
104
|
+
|
105
|
+
# abi.encode(v1, v2)
|
106
|
+
encode(types, values) # => "0x00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000011"
|
107
|
+
|
108
|
+
# abi.encodePacked(v1, v2)
|
109
|
+
encode(types, values, true) # => "0x000000110000000000000011"
|
110
|
+
```
|
111
|
+
|
112
|
+
|
113
|
+
|
84
114
|
## Development
|
85
115
|
|
86
116
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -3,9 +3,9 @@ module AbiCoderRb
|
|
3
3
|
size = decode_uint256(data[0, 32])
|
4
4
|
raise DecodingError, "Too many elements: #{size}" if size > 100_000
|
5
5
|
|
6
|
-
|
6
|
+
inner_type = type.inner_type
|
7
7
|
|
8
|
-
if
|
8
|
+
if inner_type.dynamic?
|
9
9
|
raise DecodingError, "Not enough data for head" unless data.size >= 32 + 32 * size
|
10
10
|
|
11
11
|
start_positions = (1..size).map { |i| 32 + decode_uint256(data[32 * i, 32]) }
|
@@ -13,9 +13,9 @@ module AbiCoderRb
|
|
13
13
|
|
14
14
|
outputs = (0...size).map { |i| data[start_positions[i]...start_positions[i + 1]] }
|
15
15
|
|
16
|
-
outputs.map { |out| decode_type(
|
16
|
+
outputs.map { |out| decode_type(inner_type, out) }
|
17
17
|
else
|
18
|
-
(0...size).map { |i| decode_type(
|
18
|
+
(0...size).map { |i| decode_type(inner_type, data[(32 + inner_type.size * i)..]) }
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
module AbiCoderRb
|
2
2
|
def decode_fixed_array(type, data)
|
3
|
-
l = type.
|
4
|
-
|
5
|
-
if
|
3
|
+
l = type.length
|
4
|
+
inner_type = type.inner_type
|
5
|
+
if inner_type.dynamic?
|
6
6
|
start_positions = (0...l).map { |i| decode_uint256(data[32 * i, 32]) }
|
7
7
|
start_positions.push(data.size)
|
8
8
|
|
9
9
|
outputs = (0...l).map { |i| data[start_positions[i]...start_positions[i + 1]] }
|
10
10
|
|
11
|
-
outputs.map { |out| decode_type(
|
11
|
+
outputs.map { |out| decode_type(inner_type, out) }
|
12
12
|
else
|
13
|
-
(0...l).map { |i| decode_type(
|
13
|
+
(0...l).map { |i| decode_type(inner_type, data[inner_type.size * i, inner_type.size]) }
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module AbiCoderRb
|
2
2
|
def encode_array(type, args, packed = false)
|
3
|
-
raise
|
3
|
+
raise EncodingError, "args must be an array" unless args.is_a?(::Array)
|
4
4
|
|
5
5
|
_encode_array(type: type, args: args, packed: packed)
|
6
6
|
end
|
@@ -14,15 +14,14 @@ module AbiCoderRb
|
|
14
14
|
# 数组长度
|
15
15
|
head += encode_uint256(args.size) if type.is_a?(Array) && !packed
|
16
16
|
|
17
|
-
subtype = type.subtype
|
18
17
|
args.each do |arg|
|
19
|
-
if
|
20
|
-
raise "#{type.class} with dynamic inner type is not supported in packed mode" if packed
|
18
|
+
if type.inner_type.dynamic?
|
19
|
+
raise EncodingError, "#{type.class} with dynamic inner type is not supported in packed mode" if packed
|
21
20
|
|
22
21
|
head += encode_uint256(32 * args.size + tail.size) # 当前数据的位置指针
|
23
|
-
tail += encode_type(
|
22
|
+
tail += encode_type(type.inner_type, arg)
|
24
23
|
else
|
25
|
-
head += encode_type(
|
24
|
+
head += encode_type(type.inner_type, arg)
|
26
25
|
end
|
27
26
|
end
|
28
27
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module AbiCoderRb
|
2
2
|
def encode_fixed_array(type, args, packed = false)
|
3
|
-
raise
|
4
|
-
raise
|
3
|
+
raise EncodingError, "args must be an array" unless args.is_a?(::Array)
|
4
|
+
raise EncodingError, "Wrong array size: found #{args.size}, expecting #{type.length}" unless args.size == type.length
|
5
5
|
|
6
6
|
# fixed_array,是没有元素数量的编码de
|
7
7
|
# 如果内部类型是静态的,就是一个一个元素编码后加起来。
|
@@ -25,7 +25,7 @@ module AbiCoderRb
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def encode_uint(arg, bits, packed = false)
|
28
|
-
raise
|
28
|
+
raise EncodingError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
|
29
29
|
raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**bits
|
30
30
|
|
31
31
|
if packed
|
@@ -51,8 +51,7 @@ module AbiCoderRb
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def encode_bool(arg, packed = false)
|
54
|
-
|
55
|
-
raise ArgumentError, "arg is not bool: #{arg}" unless arg.is_a?(TrueClass) || arg.is_a?(FalseClass)
|
54
|
+
raise EncodingError, "arg is not bool: #{arg}" unless arg.is_a?(TrueClass) || arg.is_a?(FalseClass)
|
56
55
|
|
57
56
|
if packed
|
58
57
|
arg ? BYTE_ONE : BYTE_ZERO
|
@@ -62,7 +61,6 @@ module AbiCoderRb
|
|
62
61
|
end
|
63
62
|
|
64
63
|
def encode_string(arg, packed = false)
|
65
|
-
## raise EncodingError or ArgumentError - why? why not?
|
66
64
|
raise EncodingError, "Expecting string: #{arg}" unless arg.is_a?(::String)
|
67
65
|
|
68
66
|
arg = arg.b if arg.encoding != "BINARY" ## was: name == 'UTF-8', wasm
|
@@ -79,7 +77,6 @@ module AbiCoderRb
|
|
79
77
|
end
|
80
78
|
|
81
79
|
def encode_bytes(arg, length: nil, packed: false)
|
82
|
-
## raise EncodingError or ArgumentError - why? why not?
|
83
80
|
raise EncodingError, "Expecting string: #{arg}" unless arg.is_a?(::String)
|
84
81
|
|
85
82
|
arg = arg.b if arg.encoding != Encoding::BINARY
|
@@ -1,17 +1,17 @@
|
|
1
1
|
module AbiCoderRb
|
2
2
|
def encode_tuple(tuple, args, packed = false)
|
3
|
-
raise "#{tuple.class} with multi inner
|
3
|
+
raise "#{tuple.class} with multi inner type is not supported in packed mode" if packed && tuple.inner_types.size > 1
|
4
4
|
|
5
|
-
encode_types(tuple.
|
5
|
+
encode_types(tuple.inner_types, args, packed)
|
6
6
|
end
|
7
7
|
|
8
8
|
private
|
9
9
|
|
10
10
|
def encode_types(types, args, packed = false)
|
11
|
-
raise
|
11
|
+
raise EncodingError, "args must be an array" unless args.is_a?(::Array)
|
12
12
|
|
13
13
|
unless args.size == types.size
|
14
|
-
raise
|
14
|
+
raise EncodingError,
|
15
15
|
"Wrong number of args: found #{args.size}, expecting #{types.size}"
|
16
16
|
end
|
17
17
|
|
data/lib/abi_coder_rb/encode.rb
CHANGED
@@ -5,25 +5,14 @@ require_relative "encode/encode_primitive_type"
|
|
5
5
|
|
6
6
|
module AbiCoderRb
|
7
7
|
# returns byte array
|
8
|
-
def encode(
|
9
|
-
if
|
10
|
-
raise EncodingError, "values should be an array" unless value_or_values.is_a?(::Array)
|
8
|
+
def encode(str, value, packed = false)
|
9
|
+
return encode_type(Type.parse(str), value, packed) if str.is_a?(::String)
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
value = values[i]
|
16
|
-
encode(typestr, value, packed)
|
17
|
-
end.join
|
18
|
-
else
|
19
|
-
typestr = typestr_or_typestrs
|
20
|
-
value = value_or_values
|
21
|
-
# TODO: more checks?
|
22
|
-
raise EncodingError, "Value can not be nil" if value.nil?
|
11
|
+
return str.map.with_index do |type, i|
|
12
|
+
encode(type, value[i], packed)
|
13
|
+
end.join("") if str.is_a?(::Array) && value.is_a?(::Array) && str.size == value.size
|
23
14
|
|
24
|
-
|
25
|
-
encode_type(parsed, value, packed)
|
26
|
-
end
|
15
|
+
raise EncodingError, "There is something wrong with #{str.inspect}, #{value.inspect}"
|
27
16
|
end
|
28
17
|
|
29
18
|
private
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative 'abi_tokenizer'
|
2
|
+
|
3
|
+
module AbiCoderRb
|
4
|
+
class AbiParser
|
5
|
+
def initialize(abi)
|
6
|
+
@tokenizer = AbiTokenizer.new(abi)
|
7
|
+
@current_token = @tokenizer.next_token
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse
|
11
|
+
element =
|
12
|
+
case @current_token
|
13
|
+
when "string", "address", "bool" then parse_simple_type
|
14
|
+
when "uint", "int" then parse_numeric_type
|
15
|
+
when "bytes" then parse_bytes
|
16
|
+
when "(" then parse_tuple
|
17
|
+
else
|
18
|
+
raise ParseError, "Unexpected token: #{@current_token}"
|
19
|
+
end
|
20
|
+
|
21
|
+
@current_token == "[" ? parse_array(element) : element
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def parse_bytes
|
27
|
+
result =
|
28
|
+
if @tokenizer.peek_token =~ /^\d+$/
|
29
|
+
@current_token = @tokenizer.next_token
|
30
|
+
{ type: 'bytes', length: @current_token.to_i }
|
31
|
+
else
|
32
|
+
{ type: 'bytes' }
|
33
|
+
end
|
34
|
+
@current_token = @tokenizer.next_token
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_numeric_type
|
39
|
+
type = @current_token
|
40
|
+
result =
|
41
|
+
if @tokenizer.peek_token =~ /^\d+$/
|
42
|
+
@current_token = @tokenizer.next_token
|
43
|
+
{ type: type, bits: @current_token.to_i }
|
44
|
+
else
|
45
|
+
{ type: type, bits: 256 }
|
46
|
+
end
|
47
|
+
@current_token = @tokenizer.next_token
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_simple_type
|
52
|
+
type = @current_token
|
53
|
+
@current_token = @tokenizer.next_token
|
54
|
+
{ type: type }
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_tuple
|
58
|
+
inner_types = []
|
59
|
+
|
60
|
+
expect("(")
|
61
|
+
until @current_token == ")"
|
62
|
+
inner_types << parse
|
63
|
+
expect(",") if @current_token != ")"
|
64
|
+
end
|
65
|
+
expect(")")
|
66
|
+
|
67
|
+
{ type: "tuple", inner_types: inner_types }
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_array(element)
|
71
|
+
arr = { type: 'array', inner_type: element, length: parse_array_length }
|
72
|
+
@current_token == "[" ? parse_array(arr) : arr
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_array_length
|
76
|
+
expect("[")
|
77
|
+
if @current_token == "]"
|
78
|
+
length = nil
|
79
|
+
elsif @current_token =~ /^\d+$/
|
80
|
+
length = @current_token.to_i
|
81
|
+
@current_token = @tokenizer.next_token
|
82
|
+
else
|
83
|
+
raise ParseError, "Expected array length or closing ']'"
|
84
|
+
end
|
85
|
+
expect("]")
|
86
|
+
|
87
|
+
length
|
88
|
+
end
|
89
|
+
|
90
|
+
def expect(token)
|
91
|
+
raise "Expected #{token}, got #{@current_token}" unless @current_token == token
|
92
|
+
|
93
|
+
@current_token = @tokenizer.next_token
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module AbiCoderRb
|
2
|
+
class AbiTokenizer
|
3
|
+
def initialize(abi)
|
4
|
+
@abi = abi
|
5
|
+
@index = 0
|
6
|
+
end
|
7
|
+
|
8
|
+
def next_token
|
9
|
+
skip_whitespace
|
10
|
+
|
11
|
+
return nil if @index >= @abi.length
|
12
|
+
|
13
|
+
char = @abi[@index]
|
14
|
+
|
15
|
+
case char
|
16
|
+
when '(', ')', ',', '[', ']'
|
17
|
+
@index += 1
|
18
|
+
char
|
19
|
+
else
|
20
|
+
read_identifier
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def peek_token
|
25
|
+
original_index = @index
|
26
|
+
token = next_token
|
27
|
+
@index = original_index
|
28
|
+
token
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def skip_whitespace
|
34
|
+
while @index < @abi.length && @abi[@index].match?(/\s/)
|
35
|
+
@index += 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def read_identifier
|
40
|
+
start_index = @index
|
41
|
+
|
42
|
+
# if the first character is a digit, read the whole number
|
43
|
+
if @abi[@index].match?(/\d/)
|
44
|
+
while @index < @abi.length && @abi[@index].match?(/\d/)
|
45
|
+
@index += 1
|
46
|
+
end
|
47
|
+
else
|
48
|
+
# Move the index until a non-identifier character or digit is found
|
49
|
+
while @index < @abi.length && !@abi[@index].match?(/[\(\),\[\]\s\d]/)
|
50
|
+
@index += 1
|
51
|
+
end
|
52
|
+
end
|
53
|
+
@abi[start_index...@index]
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative '../parser/abi_parser'
|
2
|
+
|
3
|
+
module AbiCoderRb
|
4
|
+
class Type
|
5
|
+
class << self
|
6
|
+
def parse(str)
|
7
|
+
abi_type_element = AbiCoderRb::AbiParser.new(str).parse
|
8
|
+
create_type(abi_type_element)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def create_type(element)
|
14
|
+
case element[:type]
|
15
|
+
when 'string' then String.new
|
16
|
+
when 'address' then Address.new
|
17
|
+
when 'bool' then Bool.new
|
18
|
+
when 'uint', 'int' then create_numeric_type(element)
|
19
|
+
when 'bytes' then create_bytes_type(element)
|
20
|
+
when 'tuple' then create_tuple_type(element)
|
21
|
+
when 'array' then create_array_type(element)
|
22
|
+
else
|
23
|
+
raise ParseError, "Unknown type: #{element}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_tuple_type(abi_type)
|
28
|
+
Tuple.new(abi_type[:inner_types].map { |t| create_type(t) })
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_numeric_type(abi_type)
|
32
|
+
abi_type[:type] == 'uint' ? Uint.new(abi_type[:bits]) : Int.new(abi_type[:bits])
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_bytes_type(abi_type)
|
36
|
+
abi_type[:length] ? FixedBytes.new(abi_type[:length]) : Bytes.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_array_type(abi_type)
|
40
|
+
inner_type = create_type(abi_type[:inner_type])
|
41
|
+
abi_type[:length] ? FixedArray.new(inner_type, abi_type[:length]) : Array.new(inner_type)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
module AbiCoderRb
|
2
|
+
class Type
|
3
|
+
def size
|
4
|
+
raise NotImplementedError, "size method not defined for #{self.class.name}"
|
5
|
+
end
|
6
|
+
|
7
|
+
def dynamic?
|
8
|
+
size.nil?
|
9
|
+
end
|
10
|
+
|
11
|
+
def format
|
12
|
+
raise NotImplementedError, "format method not defined for #{self.class.name}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Address < Type
|
17
|
+
def size
|
18
|
+
32
|
19
|
+
end
|
20
|
+
|
21
|
+
def format
|
22
|
+
"address"
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other)
|
26
|
+
other.is_a?(Address)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Bytes < Type
|
31
|
+
def size
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def format
|
36
|
+
"bytes"
|
37
|
+
end
|
38
|
+
|
39
|
+
def ==(other)
|
40
|
+
other.is_a?(Bytes)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class FixedBytes < Type
|
45
|
+
attr_reader :length
|
46
|
+
|
47
|
+
def initialize(length)
|
48
|
+
@length = length
|
49
|
+
end
|
50
|
+
|
51
|
+
def size
|
52
|
+
32
|
53
|
+
end
|
54
|
+
|
55
|
+
def format
|
56
|
+
"bytes#{@length}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def ==(other)
|
60
|
+
other.is_a?(FixedBytes) && @length == other.length
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Int < Type
|
65
|
+
attr_reader :bits
|
66
|
+
|
67
|
+
def initialize(bits = 256)
|
68
|
+
@bits = bits
|
69
|
+
end
|
70
|
+
|
71
|
+
def size
|
72
|
+
32
|
73
|
+
end
|
74
|
+
|
75
|
+
def format
|
76
|
+
"int#{@bits}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def ==(other)
|
80
|
+
other.is_a?(Int) && @bits == other.bits
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class Uint < Type
|
85
|
+
attr_reader :bits
|
86
|
+
|
87
|
+
def initialize(bits = 256)
|
88
|
+
@bits = bits
|
89
|
+
end
|
90
|
+
|
91
|
+
def size
|
92
|
+
32
|
93
|
+
end
|
94
|
+
|
95
|
+
def format
|
96
|
+
"uint#{@bits}"
|
97
|
+
end
|
98
|
+
|
99
|
+
def ==(other)
|
100
|
+
other.is_a?(Uint) && @bits == other.bits
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class Bool < Type
|
105
|
+
def size
|
106
|
+
32
|
107
|
+
end
|
108
|
+
|
109
|
+
def format
|
110
|
+
"bool"
|
111
|
+
end
|
112
|
+
|
113
|
+
def ==(other)
|
114
|
+
other.is_a?(Bool)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class String < Type
|
119
|
+
def size
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def format
|
124
|
+
"string"
|
125
|
+
end
|
126
|
+
|
127
|
+
def ==(other)
|
128
|
+
other.is_a?(String)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class Array < Type
|
133
|
+
attr_reader :inner_type
|
134
|
+
|
135
|
+
def initialize(inner_type)
|
136
|
+
@inner_type = inner_type
|
137
|
+
end
|
138
|
+
|
139
|
+
def size
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
|
143
|
+
def format
|
144
|
+
"#{@inner_type.format}[]"
|
145
|
+
end
|
146
|
+
|
147
|
+
def ==(other)
|
148
|
+
other.is_a?(Array) && @inner_type == other.inner_type
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class FixedArray < Type
|
153
|
+
attr_reader :inner_type, :length
|
154
|
+
|
155
|
+
def initialize(inner_type, length)
|
156
|
+
@inner_type = inner_type
|
157
|
+
@length = length
|
158
|
+
end
|
159
|
+
|
160
|
+
def size
|
161
|
+
@inner_type.dynamic? ? nil : @length * inner_type.size
|
162
|
+
end
|
163
|
+
|
164
|
+
def format
|
165
|
+
"#{@inner_type.format}[#{@length}]"
|
166
|
+
end
|
167
|
+
|
168
|
+
def ==(other)
|
169
|
+
other.is_a?(FixedArray) && @length == other.length && @inner_type == other.inner_type
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class Tuple < Type
|
174
|
+
attr_reader :inner_types
|
175
|
+
|
176
|
+
def initialize(inner_types)
|
177
|
+
@inner_types = inner_types
|
178
|
+
end
|
179
|
+
|
180
|
+
def size
|
181
|
+
s = 0
|
182
|
+
has_dynamic = false
|
183
|
+
@inner_types.each do |type|
|
184
|
+
ts = type.size
|
185
|
+
has_dynamic ||= ts.nil?
|
186
|
+
s += ts unless ts.nil?
|
187
|
+
end
|
188
|
+
has_dynamic ? nil : s
|
189
|
+
end
|
190
|
+
|
191
|
+
def format
|
192
|
+
"(#{@inner_types.map(&:format).join(",")})"
|
193
|
+
end
|
194
|
+
|
195
|
+
def ==(other)
|
196
|
+
other.is_a?(Tuple) && @inner_types == other.inner_types
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
data/lib/abi_coder_rb/version.rb
CHANGED
data/lib/abi_coder_rb.rb
CHANGED
@@ -4,18 +4,20 @@ require_relative "abi_coder_rb/version"
|
|
4
4
|
|
5
5
|
require_relative "abi_coder_rb/utils"
|
6
6
|
|
7
|
-
require_relative "abi_coder_rb/
|
8
|
-
require_relative "abi_coder_rb/
|
7
|
+
require_relative "abi_coder_rb/type/types"
|
8
|
+
require_relative "abi_coder_rb/type/parse"
|
9
9
|
require_relative "abi_coder_rb/decode"
|
10
10
|
require_relative "abi_coder_rb/encode"
|
11
11
|
|
12
12
|
require_relative "periphery/event_decoder"
|
13
13
|
|
14
14
|
module AbiCoderRb
|
15
|
-
class
|
16
|
-
class
|
17
|
-
class
|
18
|
-
class
|
15
|
+
class Error < StandardError; end
|
16
|
+
class DecodingError < Error; end
|
17
|
+
class EncodingError < Error; end
|
18
|
+
class ValueError < Error; end
|
19
|
+
class ValueOutOfBounds < Error; end
|
20
|
+
class ParseError < Error; end
|
19
21
|
|
20
22
|
BYTE_EMPTY = "".b.freeze
|
21
23
|
BYTE_ZERO = "\x00".b.freeze
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module AbiCoderRb
|
2
|
+
class AbiParser
|
3
|
+
@tokenizer: AbiTokenizer
|
4
|
+
@current_token: String
|
5
|
+
|
6
|
+
def initialize: (AbiTokenizer) -> void
|
7
|
+
def parse: -> abi_type
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def parse_bytes: -> bytes
|
12
|
+
def parse_numeric_type: -> numeric_type
|
13
|
+
def parse_simple_type: -> simple_type
|
14
|
+
def parse_tuple: -> tuple_type
|
15
|
+
def parse_array: -> array_type
|
16
|
+
def parse_array_length: -> Integer
|
17
|
+
def expect: -> void
|
18
|
+
end
|
19
|
+
|
20
|
+
type abi_type = bytes | numeric_type | simple_type | array_type | tuple_type
|
21
|
+
|
22
|
+
type bytes =
|
23
|
+
{ type: "bytes", length?: 1 | 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 }
|
24
|
+
|
25
|
+
type numeric_type =
|
26
|
+
{ type: "uint", bits: 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 88 | 96 | 104 | 112 | 120 | 128 | 136 | 144 | 152 | 160 | 168 | 176 | 184 | 192 | 200 | 208 | 216 | 224 | 232 | 240 | 248 | 256 } # uint will be parsed to uint256.
|
27
|
+
| { type: "int", bits: 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 88 | 96 | 104 | 112 | 120 | 128 | 136 | 144 | 152 | 160 | 168 | 176 | 184 | 192 | 200 | 208 | 216 | 224 | 232 | 240 | 248 | 256 } # int will be parsed to int256
|
28
|
+
|
29
|
+
type simple_type =
|
30
|
+
{ type: "string" }
|
31
|
+
| { type: "address" }
|
32
|
+
| { type: "bool" }
|
33
|
+
|
34
|
+
type array_type = { type: "array", inner_type: abi_type, length: Integer? } # length is nil for dynamic array.
|
35
|
+
|
36
|
+
type tuple_type = { type: "tuple", inner_types: Array[abi_type] }
|
37
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module AbiCoderRb
|
2
|
+
class AbiTokenizer
|
3
|
+
@abi: String
|
4
|
+
@index: Integer
|
5
|
+
|
6
|
+
def initialize: (String) -> void
|
7
|
+
def next_token: -> String
|
8
|
+
def peek_token: -> String
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def read_identifier: -> String
|
13
|
+
def skip_whitespace: -> void
|
14
|
+
end
|
15
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aki Wu
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -45,19 +45,15 @@ executables: []
|
|
45
45
|
extensions: []
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
|
-
- ".DS_Store"
|
49
48
|
- ".rspec"
|
50
49
|
- ".rubocop.yml"
|
51
50
|
- CODE_OF_CONDUCT.md
|
52
51
|
- Gemfile
|
53
|
-
- Gemfile.lock
|
54
52
|
- LICENSE.txt
|
55
53
|
- README.md
|
56
54
|
- Rakefile
|
57
55
|
- flatten.rb
|
58
|
-
- lib/.DS_Store
|
59
56
|
- lib/abi_coder_rb.rb
|
60
|
-
- lib/abi_coder_rb/.DS_Store
|
61
57
|
- lib/abi_coder_rb/decode.rb
|
62
58
|
- lib/abi_coder_rb/decode/decode_array.rb
|
63
59
|
- lib/abi_coder_rb/decode/decode_fixed_array.rb
|
@@ -68,12 +64,17 @@ files:
|
|
68
64
|
- lib/abi_coder_rb/encode/encode_fixed_array.rb
|
69
65
|
- lib/abi_coder_rb/encode/encode_primitive_type.rb
|
70
66
|
- lib/abi_coder_rb/encode/encode_tuple.rb
|
71
|
-
- lib/abi_coder_rb/parser.rb
|
72
|
-
- lib/abi_coder_rb/
|
67
|
+
- lib/abi_coder_rb/parser/abi_parser.rb
|
68
|
+
- lib/abi_coder_rb/parser/abi_tokenizer.rb
|
69
|
+
- lib/abi_coder_rb/type/parse.rb
|
70
|
+
- lib/abi_coder_rb/type/types.rb
|
73
71
|
- lib/abi_coder_rb/utils.rb
|
74
72
|
- lib/abi_coder_rb/version.rb
|
75
73
|
- lib/periphery/event_decoder.rb
|
76
74
|
- sig/abi_coder_rb.rbs
|
75
|
+
- sig/abi_coder_rb/abi_parser.rbs
|
76
|
+
- sig/abi_coder_rb/abi_tokenizer.rbs
|
77
|
+
- sig/abi_coder_rb/type.rbs
|
77
78
|
- tea.yaml
|
78
79
|
homepage: https://github.com/wuminzhe/abi_coder_rb
|
79
80
|
licenses:
|
data/.DS_Store
DELETED
Binary file
|
data/Gemfile.lock
DELETED
@@ -1,87 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
abi_coder_rb (0.2.8)
|
5
|
-
activesupport
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
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)
|
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)
|
25
|
-
diff-lcs (1.5.0)
|
26
|
-
drb (2.1.1)
|
27
|
-
ruby2_keywords
|
28
|
-
i18n (1.14.1)
|
29
|
-
concurrent-ruby (~> 1.0)
|
30
|
-
json (2.6.3)
|
31
|
-
keccak (1.3.1)
|
32
|
-
language_server-protocol (3.17.0.3)
|
33
|
-
minitest (5.20.0)
|
34
|
-
mutex_m (0.1.2)
|
35
|
-
parallel (1.23.0)
|
36
|
-
parser (3.2.2.4)
|
37
|
-
ast (~> 2.4.1)
|
38
|
-
racc
|
39
|
-
racc (1.7.3)
|
40
|
-
rainbow (3.1.1)
|
41
|
-
rake (13.1.0)
|
42
|
-
regexp_parser (2.8.2)
|
43
|
-
rexml (3.2.6)
|
44
|
-
rspec (3.12.0)
|
45
|
-
rspec-core (~> 3.12.0)
|
46
|
-
rspec-expectations (~> 3.12.0)
|
47
|
-
rspec-mocks (~> 3.12.0)
|
48
|
-
rspec-core (3.12.2)
|
49
|
-
rspec-support (~> 3.12.0)
|
50
|
-
rspec-expectations (3.12.3)
|
51
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
52
|
-
rspec-support (~> 3.12.0)
|
53
|
-
rspec-mocks (3.12.6)
|
54
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
55
|
-
rspec-support (~> 3.12.0)
|
56
|
-
rspec-support (3.12.1)
|
57
|
-
rubocop (1.57.2)
|
58
|
-
json (~> 2.3)
|
59
|
-
language_server-protocol (>= 3.17.0)
|
60
|
-
parallel (~> 1.10)
|
61
|
-
parser (>= 3.2.2.4)
|
62
|
-
rainbow (>= 2.2.2, < 4.0)
|
63
|
-
regexp_parser (>= 1.8, < 3.0)
|
64
|
-
rexml (>= 3.2.5, < 4.0)
|
65
|
-
rubocop-ast (>= 1.28.1, < 2.0)
|
66
|
-
ruby-progressbar (~> 1.7)
|
67
|
-
unicode-display_width (>= 2.4.0, < 3.0)
|
68
|
-
rubocop-ast (1.30.0)
|
69
|
-
parser (>= 3.2.1.0)
|
70
|
-
ruby-progressbar (1.13.0)
|
71
|
-
ruby2_keywords (0.0.5)
|
72
|
-
tzinfo (2.0.6)
|
73
|
-
concurrent-ruby (~> 1.0)
|
74
|
-
unicode-display_width (2.5.0)
|
75
|
-
|
76
|
-
PLATFORMS
|
77
|
-
arm64-darwin-21
|
78
|
-
|
79
|
-
DEPENDENCIES
|
80
|
-
abi_coder_rb!
|
81
|
-
keccak (~> 1.3)
|
82
|
-
rake (~> 13.0)
|
83
|
-
rspec (~> 3.0)
|
84
|
-
rubocop (~> 1.21)
|
85
|
-
|
86
|
-
BUNDLED WITH
|
87
|
-
2.4.13
|
data/lib/.DS_Store
DELETED
Binary file
|
data/lib/abi_coder_rb/.DS_Store
DELETED
Binary file
|
data/lib/abi_coder_rb/parser.rb
DELETED
@@ -1,156 +0,0 @@
|
|
1
|
-
module AbiCoderRb
|
2
|
-
class Type
|
3
|
-
class ParseError < StandardError; end
|
4
|
-
|
5
|
-
## nested inside Type - why? why not?
|
6
|
-
class Parser
|
7
|
-
TUPLE_TYPE_RX = /^\((.*)\)
|
8
|
-
((\[[0-9]*\])*)
|
9
|
-
/x
|
10
|
-
|
11
|
-
def self.parse(type)
|
12
|
-
type = type.strip
|
13
|
-
|
14
|
-
if type =~ TUPLE_TYPE_RX
|
15
|
-
types = _parse_tuple_type(::Regexp.last_match(1))
|
16
|
-
dims = _parse_dims(::Regexp.last_match(2))
|
17
|
-
|
18
|
-
parsed_types = types.map { |t| parse(t) }
|
19
|
-
|
20
|
-
return _parse_array_type(Tuple.new(parsed_types), dims)
|
21
|
-
end
|
22
|
-
|
23
|
-
# uint256 => uint, 256, nil
|
24
|
-
# uint256[2] => uint, 256, [2]
|
25
|
-
# uint256[] => uint, 256, [-1]
|
26
|
-
# bytes => bytes, nil, []
|
27
|
-
base, sub, dims = _parse_base_type(type)
|
28
|
-
|
29
|
-
sub ||= 256 if type.start_with?("uint") || type.start_with?("int") # default to 256 if no sub given
|
30
|
-
_validate_base_type(base, sub)
|
31
|
-
|
32
|
-
subtype = case base
|
33
|
-
when "string" then String.new
|
34
|
-
when "bytes" then sub ? FixedBytes.new(sub) : Bytes.new
|
35
|
-
when "uint" then Uint.new(sub)
|
36
|
-
when "int" then Int.new(sub)
|
37
|
-
when "address" then Address.new
|
38
|
-
when "bool" then Bool.new
|
39
|
-
else
|
40
|
-
## puts " type: >#{type}<"
|
41
|
-
raise ParseError, "Unrecognized type base: #{base}"
|
42
|
-
end
|
43
|
-
|
44
|
-
_parse_array_type(subtype, dims)
|
45
|
-
end
|
46
|
-
|
47
|
-
##
|
48
|
-
# Crazy regexp to seperate out base type component (eg. uint), size (eg.
|
49
|
-
# 256, 128, nil), array component (eg. [], [45], nil)
|
50
|
-
#
|
51
|
-
BASE_TYPE_RX = /([a-z]*)
|
52
|
-
([0-9]*)
|
53
|
-
((\[[0-9]*\])*)
|
54
|
-
/x
|
55
|
-
|
56
|
-
def self._parse_base_type(str)
|
57
|
-
_, base, subscript, dimension = BASE_TYPE_RX.match(str).to_a
|
58
|
-
|
59
|
-
## note: use [Integer,Integer] array in the future for sub
|
60
|
-
## for fixed (e.g. 128x128 => [128,128]) or such
|
61
|
-
## for now always assume single integer (as string)
|
62
|
-
sub = subscript == "" ? nil : subscript.to_i
|
63
|
-
|
64
|
-
## e.g. turn "[][1][2]" into [-1,1,2]
|
65
|
-
## or "" into [] -- that is, empty array
|
66
|
-
dims = _parse_dims(dimension)
|
67
|
-
|
68
|
-
[base, sub, dims]
|
69
|
-
end
|
70
|
-
|
71
|
-
def self._parse_dims(str)
|
72
|
-
dims = str.scan(/\[[0-9]*\]/)
|
73
|
-
|
74
|
-
## note: return -1 for dynamic array size e.g. []
|
75
|
-
## e.g. "[]"[1...-1] => ""
|
76
|
-
## "[0]"[1...-1] => "0"
|
77
|
-
## "[1]"[1...-1] => "1"
|
78
|
-
dims.map do |dim|
|
79
|
-
size = dim[1...-1]
|
80
|
-
size == "" ? -1 : size.to_i
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def self._parse_array_type(subtype, dims)
|
85
|
-
##
|
86
|
-
## todo/check - double check if the order in reverse
|
87
|
-
## in solidity / abi encoding / decoding?
|
88
|
-
##
|
89
|
-
dims.each do |dim|
|
90
|
-
subtype = if dim == -1
|
91
|
-
Array.new(subtype)
|
92
|
-
else
|
93
|
-
FixedArray.new(subtype, dim)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
subtype
|
98
|
-
end
|
99
|
-
|
100
|
-
def self._validate_base_type(base, sub)
|
101
|
-
case base
|
102
|
-
when "string"
|
103
|
-
# NOTE: string can not have any suffix
|
104
|
-
raise ParseError, "String cannot have suffix" if sub
|
105
|
-
when "bytes"
|
106
|
-
raise ParseError, "Maximum 32 bytes for fixed-length bytes" if sub && sub > 32
|
107
|
-
when "uint", "int"
|
108
|
-
raise ParseError, "Integer type must have numerical suffix" unless sub
|
109
|
-
raise ParseError, "Integer size out of bounds" unless sub >= 8 && sub <= 256
|
110
|
-
raise ParseError, "Integer size must be multiple of 8" unless sub % 8 == 0
|
111
|
-
when "address"
|
112
|
-
raise ParseError, "Address cannot have suffix" if sub
|
113
|
-
when "bool"
|
114
|
-
raise ParseError, "Bool cannot have suffix" if sub
|
115
|
-
else
|
116
|
-
## puts " type: >#{type}<"
|
117
|
-
raise ParseError, "Unrecognized type base: #{base}"
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def self._parse_tuple_type(str)
|
122
|
-
## note: types assumes string WITHOUT enclosing () e.g.
|
123
|
-
## tuple(string,string,bool) => expected as "string,string,bool"
|
124
|
-
|
125
|
-
depth = 0
|
126
|
-
collected = []
|
127
|
-
current = ""
|
128
|
-
|
129
|
-
### todo/fix: replace with a simple parser!!!
|
130
|
-
## allow () and move verbose tuple() too!!!
|
131
|
-
str.each_char do |c|
|
132
|
-
case c
|
133
|
-
when ","
|
134
|
-
if depth == 0
|
135
|
-
collected << current
|
136
|
-
current = ""
|
137
|
-
else
|
138
|
-
current += c
|
139
|
-
end
|
140
|
-
when "("
|
141
|
-
depth += 1
|
142
|
-
current += c
|
143
|
-
when ")"
|
144
|
-
depth -= 1
|
145
|
-
current += c
|
146
|
-
else
|
147
|
-
current += c
|
148
|
-
end
|
149
|
-
end
|
150
|
-
collected << current unless current.empty?
|
151
|
-
|
152
|
-
collected
|
153
|
-
end
|
154
|
-
end # class Parser
|
155
|
-
end # class Type
|
156
|
-
end # module ABI
|
data/lib/abi_coder_rb/types.rb
DELETED
@@ -1,237 +0,0 @@
|
|
1
|
-
module AbiCoderRb
|
2
|
-
#######
|
3
|
-
## for now use (get inspired)
|
4
|
-
## by the type names used by abi coder in rust
|
5
|
-
## see https://github.com/rust-ethereum/ethabi/blob/master/ethabi/src/param_type/param_type.rs
|
6
|
-
|
7
|
-
class Type
|
8
|
-
def self.parse(type) ## convenience helper
|
9
|
-
Parser.parse(type)
|
10
|
-
end
|
11
|
-
|
12
|
-
##
|
13
|
-
# Get the static size of a type, or nil if dynamic.
|
14
|
-
#
|
15
|
-
# @return [Integer, NilClass] size of static type, or nil for dynamic type
|
16
|
-
#
|
17
|
-
def size
|
18
|
-
## check/todo: what error to raise for not implemented / method not defined???
|
19
|
-
raise ArgumentError, "no required size method defined for Type subclass #{self.class.name}; sorry"
|
20
|
-
end
|
21
|
-
|
22
|
-
def dynamic?
|
23
|
-
size.nil?
|
24
|
-
end
|
25
|
-
|
26
|
-
def format
|
27
|
-
## check/todo: what error to raise for not implemented / method not defined???
|
28
|
-
raise ArgumentError, "no required format method defined for Type subclass #{self.class.name}; sorry"
|
29
|
-
end
|
30
|
-
|
31
|
-
####
|
32
|
-
## default implementation
|
33
|
-
## assume equal if class match (e.g. Address == Address)
|
34
|
-
## - use format string for generic compare - why? why not?
|
35
|
-
end
|
36
|
-
|
37
|
-
class Address < Type
|
38
|
-
## note: address is always 20 bytes; BUT uses 32 bytes (with padding)
|
39
|
-
def size
|
40
|
-
32
|
41
|
-
end
|
42
|
-
|
43
|
-
def format
|
44
|
-
"address"
|
45
|
-
end
|
46
|
-
|
47
|
-
def ==(other)
|
48
|
-
other.is_a?(Address)
|
49
|
-
end
|
50
|
-
end # class Address
|
51
|
-
|
52
|
-
class Bytes < Type
|
53
|
-
## note: dynamic (not known at compile-time)
|
54
|
-
def size
|
55
|
-
nil
|
56
|
-
end
|
57
|
-
|
58
|
-
def format
|
59
|
-
"bytes"
|
60
|
-
end
|
61
|
-
|
62
|
-
def ==(other)
|
63
|
-
other.is_a?(Bytes)
|
64
|
-
end
|
65
|
-
end # class Bytes
|
66
|
-
|
67
|
-
class FixedBytes < Type
|
68
|
-
attr_reader :length
|
69
|
-
|
70
|
-
def initialize(length)
|
71
|
-
@length = length # in bytes (1,2,...32)
|
72
|
-
end
|
73
|
-
|
74
|
-
## note: always uses 32 bytes (with padding)
|
75
|
-
def size
|
76
|
-
32
|
77
|
-
end
|
78
|
-
|
79
|
-
def format
|
80
|
-
"bytes#{@length}"
|
81
|
-
end
|
82
|
-
|
83
|
-
def ==(other)
|
84
|
-
other.is_a?(FixedBytes) && @length == other.length
|
85
|
-
end
|
86
|
-
end # class FixedBytes
|
87
|
-
|
88
|
-
class Int < Type
|
89
|
-
attr_reader :bits
|
90
|
-
|
91
|
-
def initialize(bits = 256)
|
92
|
-
@bits = bits # in bits (8,16,...256)
|
93
|
-
end
|
94
|
-
|
95
|
-
## note: always uses 32 bytes (with padding)
|
96
|
-
def size
|
97
|
-
32
|
98
|
-
end
|
99
|
-
|
100
|
-
def format
|
101
|
-
"int#{@bits}"
|
102
|
-
end
|
103
|
-
|
104
|
-
def ==(other)
|
105
|
-
other.is_a?(Int) && @bits == other.bits
|
106
|
-
end
|
107
|
-
end # class Int
|
108
|
-
|
109
|
-
class Uint < Type
|
110
|
-
attr_reader :bits
|
111
|
-
|
112
|
-
def initialize(bits = 256)
|
113
|
-
@bits = bits # in bits (8,16,...256)
|
114
|
-
end
|
115
|
-
|
116
|
-
## note: always uses 32 bytes (with padding)
|
117
|
-
def size
|
118
|
-
32
|
119
|
-
end
|
120
|
-
|
121
|
-
def format
|
122
|
-
"uint#{@bits}"
|
123
|
-
end
|
124
|
-
|
125
|
-
def ==(other)
|
126
|
-
other.is_a?(Uint) && @bits == other.bits
|
127
|
-
end
|
128
|
-
end # class Uint
|
129
|
-
|
130
|
-
class Bool < Type
|
131
|
-
## note: always uses 32 bytes (with padding)
|
132
|
-
def size
|
133
|
-
32
|
134
|
-
end
|
135
|
-
|
136
|
-
def format
|
137
|
-
"bool"
|
138
|
-
end
|
139
|
-
|
140
|
-
def ==(other)
|
141
|
-
other.is_a?(Bool)
|
142
|
-
end
|
143
|
-
end # class Bool
|
144
|
-
|
145
|
-
class String < Type
|
146
|
-
## note: dynamic (not known at compile-time)
|
147
|
-
def size
|
148
|
-
nil
|
149
|
-
end
|
150
|
-
|
151
|
-
def format
|
152
|
-
"string"
|
153
|
-
end
|
154
|
-
|
155
|
-
def ==(other)
|
156
|
-
other.is_a?(String)
|
157
|
-
end
|
158
|
-
end # class String
|
159
|
-
|
160
|
-
class Array < Type
|
161
|
-
attr_reader :subtype
|
162
|
-
|
163
|
-
def initialize(subtype)
|
164
|
-
@subtype = subtype
|
165
|
-
end
|
166
|
-
|
167
|
-
## note: dynamic (not known at compile-time)
|
168
|
-
def size
|
169
|
-
nil
|
170
|
-
end
|
171
|
-
|
172
|
-
def format
|
173
|
-
"#{@subtype.format}[]"
|
174
|
-
end
|
175
|
-
|
176
|
-
def ==(other)
|
177
|
-
other.is_a?(Array) && @subtype == other.subtype
|
178
|
-
end
|
179
|
-
end # class Array
|
180
|
-
|
181
|
-
class FixedArray < Type
|
182
|
-
attr_reader :subtype, :dim
|
183
|
-
|
184
|
-
def initialize(subtype, dim)
|
185
|
-
@subtype = subtype
|
186
|
-
@dim = dim
|
187
|
-
end
|
188
|
-
|
189
|
-
def size
|
190
|
-
@subtype.dynamic? ? nil : @dim * subtype.size
|
191
|
-
end
|
192
|
-
|
193
|
-
def format
|
194
|
-
"#{@subtype.format}[#{@dim}]"
|
195
|
-
end
|
196
|
-
|
197
|
-
def ==(other)
|
198
|
-
other.is_a?(FixedArray) &&
|
199
|
-
@dim == other.dim &&
|
200
|
-
@subtype == other.subtype
|
201
|
-
end
|
202
|
-
end # class FixedArray
|
203
|
-
|
204
|
-
class Tuple < Type
|
205
|
-
attr_reader :types
|
206
|
-
|
207
|
-
def initialize(types)
|
208
|
-
@types = types
|
209
|
-
end
|
210
|
-
|
211
|
-
def size
|
212
|
-
s = 0
|
213
|
-
has_dynamic = false
|
214
|
-
@types.each do |type|
|
215
|
-
ts = type.size
|
216
|
-
if ts.nil?
|
217
|
-
# can not return nil here? if wasm
|
218
|
-
has_dynamic = true
|
219
|
-
else
|
220
|
-
s += ts
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
return if has_dynamic
|
225
|
-
|
226
|
-
s
|
227
|
-
end
|
228
|
-
|
229
|
-
def format
|
230
|
-
"(#{@types.map { |t| t.format }.join(",")})" ## rebuild minimal string
|
231
|
-
end
|
232
|
-
|
233
|
-
def ==(other)
|
234
|
-
other.is_a?(Tuple) && @types == other.types
|
235
|
-
end
|
236
|
-
end # class Tuple
|
237
|
-
end # module ABI
|