eth-custom 0.5.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/dependabot.yml +18 -0
- data/.github/workflows/codeql.yml +48 -0
- data/.github/workflows/docs.yml +26 -0
- data/.github/workflows/spec.yml +52 -0
- data/.gitignore +43 -0
- data/.gitmodules +3 -0
- data/.rspec +4 -0
- data/.yardopts +1 -0
- data/AUTHORS.txt +29 -0
- data/CHANGELOG.md +218 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +202 -0
- data/README.md +347 -0
- data/Rakefile +6 -0
- data/bin/console +10 -0
- data/bin/setup +9 -0
- data/codecov.yml +6 -0
- data/eth.gemspec +51 -0
- data/lib/eth/abi/event.rb +137 -0
- data/lib/eth/abi/type.rb +178 -0
- data/lib/eth/abi.rb +446 -0
- data/lib/eth/address.rb +106 -0
- data/lib/eth/api.rb +223 -0
- data/lib/eth/chain.rb +157 -0
- data/lib/eth/client/http.rb +63 -0
- data/lib/eth/client/ipc.rb +50 -0
- data/lib/eth/client.rb +499 -0
- data/lib/eth/constant.rb +71 -0
- data/lib/eth/contract/event.rb +42 -0
- data/lib/eth/contract/function.rb +57 -0
- data/lib/eth/contract/function_input.rb +38 -0
- data/lib/eth/contract/function_output.rb +37 -0
- data/lib/eth/contract/initializer.rb +47 -0
- data/lib/eth/contract.rb +143 -0
- data/lib/eth/eip712.rb +184 -0
- data/lib/eth/key/decrypter.rb +146 -0
- data/lib/eth/key/encrypter.rb +207 -0
- data/lib/eth/key.rb +167 -0
- data/lib/eth/rlp/decoder.rb +114 -0
- data/lib/eth/rlp/encoder.rb +78 -0
- data/lib/eth/rlp/sedes/big_endian_int.rb +66 -0
- data/lib/eth/rlp/sedes/binary.rb +97 -0
- data/lib/eth/rlp/sedes/list.rb +84 -0
- data/lib/eth/rlp/sedes.rb +74 -0
- data/lib/eth/rlp.rb +63 -0
- data/lib/eth/signature.rb +163 -0
- data/lib/eth/solidity.rb +75 -0
- data/lib/eth/tx/eip1559.rb +337 -0
- data/lib/eth/tx/eip2930.rb +329 -0
- data/lib/eth/tx/legacy.rb +297 -0
- data/lib/eth/tx.rb +322 -0
- data/lib/eth/unit.rb +49 -0
- data/lib/eth/util.rb +235 -0
- data/lib/eth/version.rb +20 -0
- data/lib/eth.rb +35 -0
- metadata +184 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
# Copyright (c) 2016-2022 The Ruby-Eth Contributors
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# -*- encoding : ascii-8bit -*-
|
16
|
+
|
17
|
+
# Provides the {Eth} module.
|
18
|
+
module Eth
|
19
|
+
|
20
|
+
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
|
21
|
+
module Abi
|
22
|
+
|
23
|
+
# Provides a module to decode transaction log events.
|
24
|
+
module Event
|
25
|
+
extend self
|
26
|
+
|
27
|
+
# Compute topic for ABI event interface.
|
28
|
+
#
|
29
|
+
# @param interface [Hash] ABI event interface.
|
30
|
+
# @return [String] a hex-string topic.
|
31
|
+
def compute_topic(interface)
|
32
|
+
sig = Abi.signature(interface)
|
33
|
+
Util.prefix_hex(Util.bin_to_hex(Util.keccak256(sig)))
|
34
|
+
end
|
35
|
+
|
36
|
+
# A decoded event log.
|
37
|
+
class LogDescription
|
38
|
+
# The event ABI interface used to decode the log.
|
39
|
+
attr_accessor :event_interface
|
40
|
+
|
41
|
+
# The the input argument of the event.
|
42
|
+
attr_accessor :args
|
43
|
+
|
44
|
+
# The named input argument of the event.
|
45
|
+
attr_accessor :kwargs
|
46
|
+
|
47
|
+
# The topic hash.
|
48
|
+
attr_accessor :topic
|
49
|
+
|
50
|
+
# Decodes event log argument values.
|
51
|
+
#
|
52
|
+
# @param event_interface [Hash] event ABI type.
|
53
|
+
# @param log [Hash] transaction receipt log
|
54
|
+
def initialize(event_interface, log)
|
55
|
+
@event_interface = event_interface
|
56
|
+
|
57
|
+
inputs = event_interface.fetch("inputs")
|
58
|
+
data = log.fetch("data")
|
59
|
+
topics = log.fetch("topics", [])
|
60
|
+
anonymous = event_interface.fetch("anonymous", false)
|
61
|
+
|
62
|
+
@topic = topics[0] if !anonymous
|
63
|
+
@args, @kwargs = Event.decode_log(inputs, data, topics, anonymous)
|
64
|
+
end
|
65
|
+
|
66
|
+
# The event name. (e.g. Transfer)
|
67
|
+
def name
|
68
|
+
@name ||= event_interface.fetch("name")
|
69
|
+
end
|
70
|
+
|
71
|
+
# The event signature. (e.g. Transfer(address,address,uint256))
|
72
|
+
def signature
|
73
|
+
@signature ||= Abi.signature(event_interface)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Decodes a stream of receipt logs with a set of ABI interfaces.
|
78
|
+
#
|
79
|
+
# @param interfaces [Array] event ABI types.
|
80
|
+
# @param logs [Array] transaction receipt logs
|
81
|
+
# @return [Hash] an enumerator of LogDescription objects.
|
82
|
+
def decode_logs(interfaces, logs)
|
83
|
+
Enumerator.new do |y|
|
84
|
+
topic_to_interfaces = Hash[interfaces.map { |i| [compute_topic(i), i] }]
|
85
|
+
|
86
|
+
logs.each do |log|
|
87
|
+
topic = log.fetch("topics", [])[0]
|
88
|
+
if topic && interface = topic_to_interfaces[topic]
|
89
|
+
y << [log, LogDescription.new(interface, log)]
|
90
|
+
else
|
91
|
+
y << [log, nil]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Decodes event log argument values.
|
98
|
+
#
|
99
|
+
# @param inputs [Array] event ABI types.
|
100
|
+
# @param data [String] ABI event data to be decoded.
|
101
|
+
# @param topics [Array] ABI event topics to be decoded.
|
102
|
+
# @param anonymous [Boolean] If event signature is excluded from topics.
|
103
|
+
# @return [[Array, Hash]] decoded positional arguments and decoded keyword arguments.
|
104
|
+
# @raise [DecodingError] if decoding fails for type.
|
105
|
+
def decode_log(inputs, data, topics, anonymous = false)
|
106
|
+
topic_inputs, data_inputs = inputs.partition { |i| i["indexed"] }
|
107
|
+
|
108
|
+
topic_types = topic_inputs.map { |i| i["type"] }
|
109
|
+
data_types = data_inputs.map { |i| i["type"] }
|
110
|
+
|
111
|
+
# If event is anonymous, all topics are arguments. Otherwise, the first
|
112
|
+
# topic will be the event signature.
|
113
|
+
if anonymous == false
|
114
|
+
topics = topics[1..-1]
|
115
|
+
end
|
116
|
+
|
117
|
+
decoded_topics = topics.map.with_index { |t, i| Abi.decode([topic_types[i]], t)[0] }
|
118
|
+
decoded_data = Abi.decode(data_types, data)
|
119
|
+
|
120
|
+
args = []
|
121
|
+
kwargs = {}
|
122
|
+
|
123
|
+
inputs.each_with_index do |input, index|
|
124
|
+
if input["indexed"]
|
125
|
+
value = decoded_topics[topic_inputs.index(input)]
|
126
|
+
else
|
127
|
+
value = decoded_data[data_inputs.index(input)]
|
128
|
+
end
|
129
|
+
args[index] = value
|
130
|
+
kwargs[input["name"].to_sym] = value
|
131
|
+
end
|
132
|
+
|
133
|
+
return args, kwargs
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/lib/eth/abi/type.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
# Copyright (c) 2016-2022 The Ruby-Eth Contributors
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# -*- encoding : ascii-8bit -*-
|
16
|
+
|
17
|
+
# Provides the {Eth} module.
|
18
|
+
module Eth
|
19
|
+
|
20
|
+
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
|
21
|
+
module Abi
|
22
|
+
|
23
|
+
# Provides a class to handle and parse common ABI types.
|
24
|
+
class Type
|
25
|
+
|
26
|
+
# Provides a specific parser error if type cannot be determined.
|
27
|
+
class ParseError < StandardError; end
|
28
|
+
|
29
|
+
# The base attribute, e.g., `string` or `bytes`.
|
30
|
+
attr :base_type
|
31
|
+
|
32
|
+
# The sub-type attribute, e.g., `256` as size of an uint256.
|
33
|
+
attr :sub_type
|
34
|
+
|
35
|
+
# The dimension attribute, e.g., `[10]` for an array of size 10.
|
36
|
+
attr :dimensions
|
37
|
+
|
38
|
+
# Create a new Type object for base types, sub types, and dimensions.
|
39
|
+
# Should not be used; use {Type.parse} instead.
|
40
|
+
#
|
41
|
+
# @param base_type [String] the base-type attribute.
|
42
|
+
# @param sub_type [String] the sub-type attribute.
|
43
|
+
# @param dimensions [Array] the dimension attribute.
|
44
|
+
# @return [Eth::Abi::Type] an ABI type object.
|
45
|
+
def initialize(base_type, sub_type, dimensions)
|
46
|
+
sub_type = sub_type.to_s
|
47
|
+
@base_type = base_type
|
48
|
+
@sub_type = sub_type
|
49
|
+
@dimensions = dimensions
|
50
|
+
end
|
51
|
+
|
52
|
+
# Converts the self.parse method into a constructor.
|
53
|
+
konstructor :parse
|
54
|
+
|
55
|
+
# Attempts to parse a string containing a common Solidity type.
|
56
|
+
# Creates a new Type upon success (using konstructor).
|
57
|
+
#
|
58
|
+
# @param type [String] a common Solidity type.
|
59
|
+
# @return [Eth::Abi::Type] a parsed Type object.
|
60
|
+
# @raise [ParseError] if it fails to parse the type.
|
61
|
+
def parse(type)
|
62
|
+
_, base_type, sub_type, dimension = /([a-z]*)([0-9]*x?[0-9]*)((\[[0-9]*\])*)/.match(type).to_a
|
63
|
+
|
64
|
+
# type dimension can only be numeric
|
65
|
+
dims = dimension.scan(/\[[0-9]*\]/)
|
66
|
+
raise ParseError, "Unknown characters found in array declaration" if dims.join != dimension
|
67
|
+
|
68
|
+
# enforce base types
|
69
|
+
validate_base_type base_type, sub_type
|
70
|
+
|
71
|
+
# return a new Type (using konstructor)
|
72
|
+
sub_type = sub_type.to_s
|
73
|
+
@base_type = base_type
|
74
|
+
@sub_type = sub_type
|
75
|
+
@dimensions = dims.map { |x| x[1...-1].to_i }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Creates a new uint256 type used for size.
|
79
|
+
#
|
80
|
+
# @return [Eth::Abi::Type] a uint256 size type.
|
81
|
+
def self.size_type
|
82
|
+
@size_type ||= new("uint", 256, [])
|
83
|
+
end
|
84
|
+
|
85
|
+
# Compares two types for their attributes.
|
86
|
+
#
|
87
|
+
# @param another_type [Eth::Abi::Type] another type to be compared.
|
88
|
+
# @return [Boolean] true if all attributes match.
|
89
|
+
def ==(another_type)
|
90
|
+
base_type == another_type.base_type and
|
91
|
+
sub_type == another_type.sub_type and
|
92
|
+
dimensions == another_type.dimensions
|
93
|
+
end
|
94
|
+
|
95
|
+
# Computes the size of a type if possible.
|
96
|
+
#
|
97
|
+
# @return [Integer] the size of the type; or nil if not available.
|
98
|
+
def size
|
99
|
+
s = nil
|
100
|
+
if dimensions.empty?
|
101
|
+
unless ["string", "bytes"].include?(base_type) and sub_type.empty?
|
102
|
+
s = 32
|
103
|
+
end
|
104
|
+
else
|
105
|
+
unless dimensions.last == 0
|
106
|
+
unless nested_sub.is_dynamic?
|
107
|
+
s = dimensions.last * nested_sub.size
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
@size ||= s
|
112
|
+
end
|
113
|
+
|
114
|
+
# Helpes to determine whether array is of dynamic size.
|
115
|
+
#
|
116
|
+
# @return [Boolean] true if array is of dynamic size.
|
117
|
+
def is_dynamic?
|
118
|
+
size.nil?
|
119
|
+
end
|
120
|
+
|
121
|
+
# Types can have nested sub-types in arrays.
|
122
|
+
#
|
123
|
+
# @return [Eth::Abi::Type] nested sub-type.
|
124
|
+
def nested_sub
|
125
|
+
@nested_sub ||= self.class.new(base_type, sub_type, dimensions[0...-1])
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
# Validates all known base types and raises if an issue occurs.
|
131
|
+
def validate_base_type(base_type, sub_type)
|
132
|
+
case base_type
|
133
|
+
when "string"
|
134
|
+
|
135
|
+
# string can not have any suffix
|
136
|
+
raise ParseError, "String type must have no suffix or numerical suffix" unless sub_type.empty?
|
137
|
+
when "bytes"
|
138
|
+
|
139
|
+
# bytes can be no longer than 32 bytes
|
140
|
+
raise ParseError, "Maximum 32 bytes for fixed-length string or bytes" unless sub_type.empty? || sub_type.to_i <= 32
|
141
|
+
when "uint", "int"
|
142
|
+
|
143
|
+
# integers must have a numerical suffix
|
144
|
+
raise ParseError, "Integer type must have numerical suffix" unless sub_type =~ /\A[0-9]+\z/
|
145
|
+
|
146
|
+
# integer size must be valid
|
147
|
+
size = sub_type.to_i
|
148
|
+
raise ParseError, "Integer size out of bounds" unless size >= 8 && size <= 256
|
149
|
+
raise ParseError, "Integer size must be multiple of 8" unless size % 8 == 0
|
150
|
+
when "ureal", "real", "fixed", "ufixed"
|
151
|
+
|
152
|
+
# floats must have valid dimensional suffix
|
153
|
+
raise ParseError, "Real type must have suffix of form <high>x<low>, e.g. 128x128" unless sub_type =~ /\A[0-9]+x[0-9]+\z/
|
154
|
+
high, low = sub_type.split("x").map(&:to_i)
|
155
|
+
total = high + low
|
156
|
+
raise ParseError, "Real size out of bounds (max 32 bytes)" unless total >= 8 && total <= 256
|
157
|
+
raise ParseError, "Real high/low sizes must be multiples of 8" unless high % 8 == 0 && low % 8 == 0
|
158
|
+
when "hash"
|
159
|
+
|
160
|
+
# hashs must have numerical suffix
|
161
|
+
raise ParseError, "Hash type must have numerical suffix" unless sub_type =~ /\A[0-9]+\z/
|
162
|
+
when "address"
|
163
|
+
|
164
|
+
# addresses cannot have any suffix
|
165
|
+
raise ParseError, "Address cannot have suffix" unless sub_type.empty?
|
166
|
+
when "bool"
|
167
|
+
|
168
|
+
# booleans cannot have any suffix
|
169
|
+
raise ParseError, "Bool cannot have suffix" unless sub_type.empty?
|
170
|
+
else
|
171
|
+
|
172
|
+
# we cannot parse arbitrary types such as 'decimal' or 'hex'
|
173
|
+
raise ParseError, "Unknown base type"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|