abicoder 0.1.0 → 1.0.0
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/Manifest.txt +4 -4
- data/README.md +88 -19
- data/Rakefile +1 -1
- data/lib/abicoder/decoder.rb +207 -0
- data/lib/abicoder/encoder.rb +278 -0
- data/lib/abicoder/parser.rb +158 -0
- data/lib/abicoder/types.rb +155 -0
- data/lib/abicoder/version.rb +2 -2
- data/lib/abicoder.rb +37 -15
- metadata +10 -10
- data/lib/abicoder/codec.rb +0 -413
- data/lib/abicoder/type.rb +0 -141
- data/lib/abicoder/type_tuple.rb +0 -106
- data/lib/abicoder/utils.rb +0 -81
@@ -0,0 +1,158 @@
|
|
1
|
+
module ABI
|
2
|
+
class Type
|
3
|
+
|
4
|
+
class ParseError < StandardError; end
|
5
|
+
|
6
|
+
|
7
|
+
## nested inside Type - why? why not?
|
8
|
+
class Parser
|
9
|
+
|
10
|
+
TUPLE_TYPE_RX = /^\((.*)\)
|
11
|
+
((\[[0-9]*\])*)
|
12
|
+
/x
|
13
|
+
|
14
|
+
def self.parse( type )
|
15
|
+
if type =~ TUPLE_TYPE_RX
|
16
|
+
types = _parse_tuple_type( $1 )
|
17
|
+
dims = _parse_dims( $2 )
|
18
|
+
|
19
|
+
parsed_types = types.map{ |t| parse( t ) }
|
20
|
+
|
21
|
+
return _parse_array_type( Tuple.new( parsed_types ), dims )
|
22
|
+
end
|
23
|
+
|
24
|
+
base, sub, dims = _parse_base_type( type )
|
25
|
+
|
26
|
+
_validate_base_type( base, sub )
|
27
|
+
|
28
|
+
subtype = case base
|
29
|
+
when 'string' then String.new
|
30
|
+
when 'bytes' then sub ? FixedBytes.new( sub ) : Bytes.new
|
31
|
+
when 'uint' then Uint.new( sub )
|
32
|
+
when 'int' then Int.new( sub )
|
33
|
+
when 'address' then Address.new
|
34
|
+
when 'bool' then Bool.new
|
35
|
+
else
|
36
|
+
## puts " type: >#{type}<"
|
37
|
+
raise ParseError, "Unrecognized type base: #{base}"
|
38
|
+
end
|
39
|
+
|
40
|
+
_parse_array_type( subtype, dims )
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Crazy regexp to seperate out base type component (eg. uint), size (eg.
|
45
|
+
# 256, 128, nil), array component (eg. [], [45], nil)
|
46
|
+
#
|
47
|
+
BASE_TYPE_RX = /([a-z]*)
|
48
|
+
([0-9]*)
|
49
|
+
((\[[0-9]*\])*)
|
50
|
+
/x
|
51
|
+
|
52
|
+
def self._parse_base_type( str )
|
53
|
+
_, base, subscript, dimension = BASE_TYPE_RX.match( str ).to_a
|
54
|
+
|
55
|
+
## note: use [Integer,Integer] array in the future for sub
|
56
|
+
## for fixed (e.g. 128x128 => [128,128]) or such
|
57
|
+
## for now always assume single integer (as string)
|
58
|
+
sub = subscript == '' ? nil : subscript.to_i
|
59
|
+
|
60
|
+
## e.g. turn "[][1][2]" into [-1,1,2]
|
61
|
+
## or "" into [] -- that is, empty array
|
62
|
+
dims = _parse_dims( dimension )
|
63
|
+
|
64
|
+
[base, sub, dims]
|
65
|
+
end
|
66
|
+
|
67
|
+
def self._parse_dims( str )
|
68
|
+
dims = str.scan( /\[[0-9]*\]/ )
|
69
|
+
|
70
|
+
## note: return -1 for dynamic array size e.g. []
|
71
|
+
## e.g. "[]"[1...-1] => ""
|
72
|
+
## "[0]"[1...-1] => "0"
|
73
|
+
## "[1]"[1...-1] => "1"
|
74
|
+
dims = dims.map do |dim|
|
75
|
+
size = dim[1...-1]
|
76
|
+
size == '' ? -1 : size.to_i
|
77
|
+
end
|
78
|
+
dims
|
79
|
+
end
|
80
|
+
|
81
|
+
def self._parse_array_type( subtype, dims )
|
82
|
+
##
|
83
|
+
## todo/check - double check if the order in reverse
|
84
|
+
## in solidity / abi encoding / decoding?
|
85
|
+
##
|
86
|
+
dims.each do |dim|
|
87
|
+
subtype = if dim == -1
|
88
|
+
Array.new( subtype )
|
89
|
+
else
|
90
|
+
FixedArray.new( subtype, dim )
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
subtype
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def self._validate_base_type( base, sub )
|
99
|
+
case base
|
100
|
+
when 'string'
|
101
|
+
# note: string can not have any suffix
|
102
|
+
raise ParseError, "String cannot have suffix" if sub
|
103
|
+
when 'bytes'
|
104
|
+
raise ParseError, "Maximum 32 bytes for fixed-length bytes" if sub && sub > 32
|
105
|
+
when 'uint', 'int'
|
106
|
+
raise ParseError, "Integer type must have numerical suffix" unless sub
|
107
|
+
raise ParseError, "Integer size out of bounds" unless sub >= 8 && sub <= 256
|
108
|
+
raise ParseError, "Integer size must be multiple of 8" unless sub % 8 == 0
|
109
|
+
when 'address'
|
110
|
+
raise ParseError, "Address cannot have suffix" if sub
|
111
|
+
when 'bool'
|
112
|
+
raise ParseError, "Bool cannot have suffix" if sub
|
113
|
+
else
|
114
|
+
## puts " type: >#{type}<"
|
115
|
+
raise ParseError, "Unrecognized type base: #{base}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
|
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 ',' then
|
134
|
+
if depth == 0
|
135
|
+
collected << current
|
136
|
+
current = ''
|
137
|
+
else
|
138
|
+
current += c
|
139
|
+
end
|
140
|
+
when '(' then
|
141
|
+
depth += 1
|
142
|
+
current += c
|
143
|
+
when ')' then
|
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
|
+
|
155
|
+
end # class Parser
|
156
|
+
end # class Type
|
157
|
+
end # module ABI
|
158
|
+
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module ABI
|
2
|
+
|
3
|
+
|
4
|
+
#######
|
5
|
+
## for now use (get inspired)
|
6
|
+
## by the type names used by abi coder in rust
|
7
|
+
## see https://github.com/rust-ethereum/ethabi/blob/master/ethabi/src/param_type/param_type.rs
|
8
|
+
|
9
|
+
|
10
|
+
class Type
|
11
|
+
|
12
|
+
def self.parse( type ) ## convenience helper
|
13
|
+
Parser.parse( type )
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Get the static size of a type, or nil if dynamic.
|
18
|
+
#
|
19
|
+
# @return [Integer, NilClass] size of static type, or nil for dynamic type
|
20
|
+
#
|
21
|
+
def size
|
22
|
+
## check/todo: what error to raise for not implemented / method not defined???
|
23
|
+
raise ArgumentError, "no required size method defined for Type subclass #{self.class.name}; sorry"
|
24
|
+
end
|
25
|
+
def dynamic?() size.nil?; end
|
26
|
+
|
27
|
+
def format
|
28
|
+
## check/todo: what error to raise for not implemented / method not defined???
|
29
|
+
raise ArgumentError, "no required format method defined for Type subclass #{self.class.name}; sorry"
|
30
|
+
end
|
31
|
+
|
32
|
+
####
|
33
|
+
## default implementation
|
34
|
+
## assume equal if class match (e.g. Address == Address)
|
35
|
+
## - use format string for generic compare - why? why not?
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
class Address < Type
|
41
|
+
def size() 32; end ## note: address is always 20 bytes; BUT uses 32 bytes (with padding)
|
42
|
+
def format() 'address'; end
|
43
|
+
def ==(another_type) another_type.kind_of?( Address ); end
|
44
|
+
end # class Address
|
45
|
+
|
46
|
+
class Bytes < Type
|
47
|
+
def size() nil; end ## note: dynamic (not known at compile-time)
|
48
|
+
def format() 'bytes'; end
|
49
|
+
def ==(another_type) another_type.kind_of?( Bytes ); end
|
50
|
+
end # class Bytes
|
51
|
+
|
52
|
+
class FixedBytes < Type
|
53
|
+
attr_reader :length
|
54
|
+
def initialize( length )
|
55
|
+
@length = length # in bytes (1,2,...32)
|
56
|
+
end
|
57
|
+
def size() 32; end ## note: always uses 32 bytes (with padding)
|
58
|
+
def format() "bytes#{@length}"; end
|
59
|
+
def ==(another_type)
|
60
|
+
another_type.kind_of?( FixedBytes ) && @length == another_type.length
|
61
|
+
end
|
62
|
+
end # class FixedBytes
|
63
|
+
|
64
|
+
class Int < Type
|
65
|
+
attr_reader :bits
|
66
|
+
def initialize( bits=256 )
|
67
|
+
@bits = bits # in bits (8,16,...256)
|
68
|
+
end
|
69
|
+
def size() 32; end ## note: always uses 32 bytes (with padding)
|
70
|
+
def format() "int#{@bits}"; end
|
71
|
+
def ==(another_type)
|
72
|
+
another_type.kind_of?( Int ) && @bits == another_type.bits
|
73
|
+
end
|
74
|
+
end # class Int
|
75
|
+
|
76
|
+
class Uint < Type
|
77
|
+
attr_reader :bits
|
78
|
+
def initialize( bits=256 )
|
79
|
+
@bits = bits # in bits (8,16,...256)
|
80
|
+
end
|
81
|
+
def size() 32; end ## note: always uses 32 bytes (with padding)
|
82
|
+
def format() "uint#{@bits}"; end
|
83
|
+
def ==(another_type)
|
84
|
+
another_type.kind_of?( Uint ) && @bits == another_type.bits
|
85
|
+
end
|
86
|
+
end # class Uint
|
87
|
+
|
88
|
+
class Bool < Type
|
89
|
+
def size() 32; end ## note: always uses 32 bytes (with padding)
|
90
|
+
def format() 'bool'; end
|
91
|
+
def ==(another_type) another_type.kind_of?( Bool ); end
|
92
|
+
end # class Bool
|
93
|
+
|
94
|
+
class String < Type
|
95
|
+
def size() nil; end ## note: dynamic (not known at compile-time)
|
96
|
+
def format() 'string'; end
|
97
|
+
def ==(another_type) another_type.kind_of?( String ); end
|
98
|
+
end # class String
|
99
|
+
|
100
|
+
class Array < Type
|
101
|
+
attr_reader :subtype
|
102
|
+
def initialize( subtype )
|
103
|
+
@subtype = subtype
|
104
|
+
end
|
105
|
+
def size() nil; end ## note: dynamic (not known at compile-time)
|
106
|
+
def format() "#{@subtype.format}[]"; end
|
107
|
+
def ==(another_type)
|
108
|
+
another_type.kind_of?( Array ) && @subtype == another_type.subtype
|
109
|
+
end
|
110
|
+
end # class Array
|
111
|
+
|
112
|
+
class FixedArray < Type
|
113
|
+
attr_reader :subtype
|
114
|
+
attr_reader :dim
|
115
|
+
def initialize( subtype, dim )
|
116
|
+
@subtype = subtype
|
117
|
+
@dim = dim
|
118
|
+
end
|
119
|
+
|
120
|
+
def size
|
121
|
+
@subtype.dynamic? ? nil : @dim * subtype.size
|
122
|
+
end
|
123
|
+
def format() "#{@subtype.format}[#{@dim}]"; end
|
124
|
+
def ==(another_type)
|
125
|
+
another_type.kind_of?( FixedArray ) &&
|
126
|
+
@dim == another_type.dim &&
|
127
|
+
@subtype == another_type.subtype
|
128
|
+
end
|
129
|
+
end # class FixedArray
|
130
|
+
|
131
|
+
class Tuple < Type
|
132
|
+
attr_reader :types
|
133
|
+
|
134
|
+
def initialize( types )
|
135
|
+
@types = types
|
136
|
+
end
|
137
|
+
|
138
|
+
def size
|
139
|
+
s = 0
|
140
|
+
@types.each do |type|
|
141
|
+
ts = type.size
|
142
|
+
return nil if ts.nil?
|
143
|
+
s += ts
|
144
|
+
end
|
145
|
+
s
|
146
|
+
end
|
147
|
+
def format
|
148
|
+
"(#{@types.map {|t| t.format }.join(',')})" ## rebuild minimal string
|
149
|
+
end
|
150
|
+
def ==(another_type)
|
151
|
+
another_type.kind_of?( Tuple ) && @types == another_type.types
|
152
|
+
end
|
153
|
+
end # class Tuple
|
154
|
+
end # module ABI
|
155
|
+
|
data/lib/abicoder/version.rb
CHANGED
data/lib/abicoder.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
## our own code
|
4
4
|
require_relative 'abicoder/version' # note: let version always go first
|
5
5
|
|
6
|
+
|
6
7
|
module ABI
|
7
8
|
###################
|
8
9
|
### some (shared) constants (move to constants.rb or such - why? why not?)
|
@@ -12,34 +13,55 @@ module ABI
|
|
12
13
|
## todo/fix: move BYTE_EMPTY, BYTE_ZERO, BYTE_ONE to upstream to bytes gem
|
13
14
|
## and make "global" constants - why? why not?
|
14
15
|
|
15
|
-
BYTE_EMPTY = "".b.freeze
|
16
|
+
## BYTE_EMPTY = "".b.freeze
|
16
17
|
BYTE_ZERO = "\x00".b.freeze
|
17
18
|
BYTE_ONE = "\x01".b.freeze ## note: used for encoding bool for now
|
18
19
|
|
19
20
|
|
20
|
-
UINT_MAX = 2**256 - 1
|
21
|
+
UINT_MAX = 2**256 - 1 ## same as 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
21
22
|
UINT_MIN = 0
|
22
|
-
INT_MAX = 2**255 - 1
|
23
|
-
INT_MIN = -2**255
|
24
|
-
|
23
|
+
INT_MAX = 2**255 - 1 ## same as 57896044618658097711785492504343953926634992332820282019728792003956564819967
|
24
|
+
INT_MIN = -2**255 ## same as -57896044618658097711785492504343953926634992332820282019728792003956564819968
|
25
25
|
|
26
|
-
### todo/check - what is TT the abbrevation for ???
|
27
|
-
TT32 = 2**32
|
28
|
-
TT40 = 2**40 ## todo/check - used where and why???
|
29
|
-
TT160 = 2**160 ## todo/check - used where and why???
|
30
|
-
TT256 = 2**256 ## todo/check - used where and why???
|
31
|
-
TT64M1 = 2**64 - 1 ## todo/check - used where and why???
|
32
26
|
end # module ABI
|
33
27
|
|
34
|
-
require_relative 'abicoder/utils'
|
35
28
|
|
29
|
+
require_relative 'abicoder/types'
|
30
|
+
require_relative 'abicoder/parser'
|
31
|
+
|
32
|
+
|
33
|
+
require_relative 'abicoder/encoder'
|
34
|
+
require_relative 'abicoder/decoder'
|
35
|
+
|
36
|
+
|
37
|
+
module ABI
|
38
|
+
def self.encoder
|
39
|
+
@encoder ||= Encoder.new
|
40
|
+
end
|
41
|
+
def self.decoder
|
42
|
+
@decoder ||= Decoder.new
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def self.encode( types, args )
|
47
|
+
encoder.encode( types, args )
|
48
|
+
end
|
36
49
|
|
37
|
-
|
38
|
-
|
50
|
+
def self.decode( types, data, raise_errors = false )
|
51
|
+
decoder.decode( types, data, raise_errors )
|
52
|
+
end
|
39
53
|
|
40
|
-
|
54
|
+
## add alternate _abi names - why? why not?
|
55
|
+
class << self
|
56
|
+
alias_method :encode_abi, :encode
|
57
|
+
alias_method :decode_abi, :decode
|
58
|
+
end
|
59
|
+
end ## module ABI
|
41
60
|
|
42
61
|
|
62
|
+
################
|
63
|
+
## add convenience alternate spellings - why? why not?
|
64
|
+
Abi = ABI
|
43
65
|
|
44
66
|
|
45
67
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abicoder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdoc
|
@@ -45,8 +45,8 @@ dependencies:
|
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '3.23'
|
47
47
|
description: abicoder - 'lite' application binary interface (abi) encoding / decoding
|
48
|
-
machinery / helper for Ethereum & Co. (blockchain)
|
49
|
-
for easy (re)use
|
48
|
+
machinery / helper (incl. nested arrays and/or tuples) for Ethereum & Co. (blockchain)
|
49
|
+
contracts with zero-dependencies for easy (re)use
|
50
50
|
email: wwwmake@googlegroups.com
|
51
51
|
executables: []
|
52
52
|
extensions: []
|
@@ -60,10 +60,10 @@ files:
|
|
60
60
|
- README.md
|
61
61
|
- Rakefile
|
62
62
|
- lib/abicoder.rb
|
63
|
-
- lib/abicoder/
|
64
|
-
- lib/abicoder/
|
65
|
-
- lib/abicoder/
|
66
|
-
- lib/abicoder/
|
63
|
+
- lib/abicoder/decoder.rb
|
64
|
+
- lib/abicoder/encoder.rb
|
65
|
+
- lib/abicoder/parser.rb
|
66
|
+
- lib/abicoder/types.rb
|
67
67
|
- lib/abicoder/version.rb
|
68
68
|
homepage: https://github.com/rubycocos/blockchain
|
69
69
|
licenses:
|
@@ -90,6 +90,6 @@ rubygems_version: 3.3.7
|
|
90
90
|
signing_key:
|
91
91
|
specification_version: 4
|
92
92
|
summary: abicoder - 'lite' application binary interface (abi) encoding / decoding
|
93
|
-
machinery / helper for Ethereum & Co. (blockchain)
|
94
|
-
for easy (re)use
|
93
|
+
machinery / helper (incl. nested arrays and/or tuples) for Ethereum & Co. (blockchain)
|
94
|
+
contracts with zero-dependencies for easy (re)use
|
95
95
|
test_files: []
|