klay 0.0.1
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 +7 -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/.yardopts +1 -0
- data/AUTHORS.txt +20 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +202 -0
- data/README.md +262 -0
- data/Rakefile +6 -0
- data/bin/console +10 -0
- data/bin/setup +9 -0
- data/klay.gemspec +50 -0
- data/lib/klay/abi/type.rb +178 -0
- data/lib/klay/abi.rb +396 -0
- data/lib/klay/address.rb +106 -0
- data/lib/klay/api.rb +223 -0
- data/lib/klay/chain.rb +157 -0
- data/lib/klay/client/http.rb +63 -0
- data/lib/klay/client/ipc.rb +47 -0
- data/lib/klay/client.rb +232 -0
- data/lib/klay/constant.rb +71 -0
- data/lib/klay/eip712.rb +184 -0
- data/lib/klay/key/decrypter.rb +146 -0
- data/lib/klay/key/encrypter.rb +207 -0
- data/lib/klay/key.rb +167 -0
- data/lib/klay/rlp/decoder.rb +109 -0
- data/lib/klay/rlp/encoder.rb +78 -0
- data/lib/klay/rlp/sedes/big_endian_int.rb +66 -0
- data/lib/klay/rlp/sedes/binary.rb +97 -0
- data/lib/klay/rlp/sedes/list.rb +84 -0
- data/lib/klay/rlp/sedes.rb +74 -0
- data/lib/klay/rlp.rb +63 -0
- data/lib/klay/signature.rb +163 -0
- data/lib/klay/tx/eip1559.rb +336 -0
- data/lib/klay/tx/eip2930.rb +328 -0
- data/lib/klay/tx/legacy.rb +296 -0
- data/lib/klay/tx.rb +327 -0
- data/lib/klay/unit.rb +49 -0
- data/lib/klay/util.rb +235 -0
- data/lib/klay/version.rb +20 -0
- data/lib/klay.rb +35 -0
- metadata +164 -0
data/lib/klay/abi.rb
ADDED
@@ -0,0 +1,396 @@
|
|
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
|
+
require "konstructor"
|
18
|
+
|
19
|
+
require "klay/abi/type"
|
20
|
+
|
21
|
+
# Provides the {Eth} module.
|
22
|
+
module Klay
|
23
|
+
|
24
|
+
# Provides a Ruby implementation of the Ethereum Applicatoin Binary Interface (ABI).
|
25
|
+
# ref: https://docs.soliditylang.org/en/develop/abi-spec.html
|
26
|
+
module Abi
|
27
|
+
extend self
|
28
|
+
|
29
|
+
# Provides a special encoding error if anything fails to encode.
|
30
|
+
class EncodingError < StandardError; end
|
31
|
+
|
32
|
+
# Provides a special decoding error if anything fails to decode.
|
33
|
+
class DecodingError < StandardError; end
|
34
|
+
|
35
|
+
# Provides a special out-of-bounds error for values.
|
36
|
+
class ValueOutOfBounds < StandardError; end
|
37
|
+
|
38
|
+
# Encodes Application Binary Interface (ABI) data. It accepts multiple
|
39
|
+
# arguments and encodes using the head/tail mechanism.
|
40
|
+
#
|
41
|
+
# @param types [Array] types to be ABI-encoded.
|
42
|
+
# @param args [Array] values to be ABI-encoded.
|
43
|
+
# @return [String] the encoded ABI data.
|
44
|
+
def encode(types, args)
|
45
|
+
|
46
|
+
# prase all types
|
47
|
+
parsed_types = types.map { |t| Type.parse(t) }
|
48
|
+
|
49
|
+
# prepare the "head"
|
50
|
+
head_size = (0...args.size)
|
51
|
+
.map { |i| parsed_types[i].size or 32 }
|
52
|
+
.reduce(0, &:+)
|
53
|
+
head, tail = "", ""
|
54
|
+
|
55
|
+
# encode types and arguments
|
56
|
+
args.each_with_index do |arg, i|
|
57
|
+
if parsed_types[i].is_dynamic?
|
58
|
+
head += encode_type Type.size_type, head_size + tail.size
|
59
|
+
tail += encode_type parsed_types[i], arg
|
60
|
+
else
|
61
|
+
head += encode_type parsed_types[i], arg
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# return the encoded ABI blob
|
66
|
+
return "#{head}#{tail}"
|
67
|
+
end
|
68
|
+
|
69
|
+
# Encodes a specific value, either static or dynamic.
|
70
|
+
#
|
71
|
+
# @param type [Eth::Abi::Type] type to be encoded.
|
72
|
+
# @param arg [String|Number] value to be encoded.
|
73
|
+
# @return [String] the encoded type.
|
74
|
+
# @raise [EncodingError] if value does not match type.
|
75
|
+
def encode_type(type, arg)
|
76
|
+
if %w(string bytes).include? type.base_type and type.sub_type.empty?
|
77
|
+
raise EncodingError, "Argument must be a String" unless arg.instance_of? String
|
78
|
+
|
79
|
+
# encodes strings and bytes
|
80
|
+
size = encode_type Type.size_type, arg.size
|
81
|
+
padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
|
82
|
+
return "#{size}#{arg}#{padding}"
|
83
|
+
elsif type.is_dynamic?
|
84
|
+
raise EncodingError, "Argument must be an Array" unless arg.instance_of? Array
|
85
|
+
|
86
|
+
# encodes dynamic-sized arrays
|
87
|
+
head, tail = "", ""
|
88
|
+
head += encode_type Type.size_type, arg.size
|
89
|
+
nested_sub = type.nested_sub
|
90
|
+
nested_sub_size = type.nested_sub.size
|
91
|
+
arg.size.times do |i|
|
92
|
+
|
93
|
+
# ref https://github.com/ethereum/tests/issues/691
|
94
|
+
raise NotImplementedError, "Encoding dynamic arrays with nested dynamic sub-types is not implemented for ABI." if nested_sub.is_dynamic?
|
95
|
+
head += encode_type nested_sub, arg[i]
|
96
|
+
end
|
97
|
+
return "#{head}#{tail}"
|
98
|
+
else
|
99
|
+
if type.dimensions.empty?
|
100
|
+
|
101
|
+
# encode a primitive type
|
102
|
+
return encode_primitive_type type, arg
|
103
|
+
else
|
104
|
+
|
105
|
+
# encode static-size arrays
|
106
|
+
return arg.map { |x| encode_type(type.nested_sub, x) }.join
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Encodes primitive types.
|
112
|
+
#
|
113
|
+
# @param type [Eth::Abi::Type] type to be encoded.
|
114
|
+
# @param arg [String|Number] value to be encoded.
|
115
|
+
# @return [String] the encoded primitive type.
|
116
|
+
# @raise [EncodingError] if value does not match type.
|
117
|
+
# @raise [ValueOutOfBounds] if value is out of bounds for type.
|
118
|
+
# @raise [EncodingError] if encoding fails for type.
|
119
|
+
def encode_primitive_type(type, arg)
|
120
|
+
case type.base_type
|
121
|
+
when "uint"
|
122
|
+
return encode_uint arg, type
|
123
|
+
when "bool"
|
124
|
+
return encode_bool arg
|
125
|
+
when "int"
|
126
|
+
return encode_int arg, type
|
127
|
+
when "ureal", "ufixed"
|
128
|
+
return encode_ufixed arg, type
|
129
|
+
when "real", "fixed"
|
130
|
+
return encode_fixed arg, type
|
131
|
+
when "string", "bytes"
|
132
|
+
return encode_bytes arg, type
|
133
|
+
when "hash"
|
134
|
+
return encode_hash arg, type
|
135
|
+
when "address"
|
136
|
+
return encode_address arg
|
137
|
+
else
|
138
|
+
raise EncodingError, "Unhandled type: #{type.base_type} #{type.sub_type}"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Decodes Application Binary Interface (ABI) data. It accepts multiple
|
143
|
+
# arguments and decodes using the head/tail mechanism.
|
144
|
+
#
|
145
|
+
# @param types [Array] the ABI to be decoded.
|
146
|
+
# @param data [String] ABI data to be decoded.
|
147
|
+
# @return [Array] the decoded ABI data.
|
148
|
+
def decode(types, data)
|
149
|
+
|
150
|
+
# accept hex abi but decode it first
|
151
|
+
data = Util.hex_to_bin data if Util.is_hex? data
|
152
|
+
|
153
|
+
# parse all types
|
154
|
+
parsed_types = types.map { |t| Type.parse(t) }
|
155
|
+
|
156
|
+
# prepare output data
|
157
|
+
outputs = [nil] * types.size
|
158
|
+
start_positions = [nil] * types.size + [data.size]
|
159
|
+
pos = 0
|
160
|
+
parsed_types.each_with_index do |t, i|
|
161
|
+
if t.is_dynamic?
|
162
|
+
|
163
|
+
# record start position for dynamic type
|
164
|
+
start_positions[i] = Util.deserialize_big_endian_to_int(data[pos, 32])
|
165
|
+
j = i - 1
|
166
|
+
while j >= 0 and start_positions[j].nil?
|
167
|
+
start_positions[j] = start_positions[i]
|
168
|
+
j -= 1
|
169
|
+
end
|
170
|
+
pos += 32
|
171
|
+
else
|
172
|
+
|
173
|
+
# get data directly for static types
|
174
|
+
outputs[i] = data[pos, t.size]
|
175
|
+
pos += t.size
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# add start position equal the length of the entire data
|
180
|
+
j = types.size - 1
|
181
|
+
while j >= 0 and start_positions[j].nil?
|
182
|
+
start_positions[j] = start_positions[types.size]
|
183
|
+
j -= 1
|
184
|
+
end
|
185
|
+
raise DecodingError, "Not enough data for head" unless pos <= data.size
|
186
|
+
|
187
|
+
# add dynamic types
|
188
|
+
parsed_types.each_with_index do |t, i|
|
189
|
+
if t.is_dynamic?
|
190
|
+
offset, next_offset = start_positions[i, 2]
|
191
|
+
outputs[i] = data[offset...next_offset]
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# return the decoded ABI types and data
|
196
|
+
return parsed_types.zip(outputs).map { |(type, out)| decode_type(type, out) }
|
197
|
+
end
|
198
|
+
|
199
|
+
# Decodes a specific value, either static or dynamic.
|
200
|
+
#
|
201
|
+
# @param type [Eth::Abi::Type] type to be decoded.
|
202
|
+
# @param arg [String] encoded type data string.
|
203
|
+
# @return [String] the decoded data for the type.
|
204
|
+
# @raise [DecodingError] if decoding fails for type.
|
205
|
+
def decode_type(type, arg)
|
206
|
+
if %w(string bytes).include?(type.base_type) and type.sub_type.empty?
|
207
|
+
l = Util.deserialize_big_endian_to_int arg[0, 32]
|
208
|
+
data = arg[32..-1]
|
209
|
+
raise DecodingError, "Wrong data size for string/bytes object" unless data.size == Util.ceil32(l)
|
210
|
+
|
211
|
+
# decoded strings and bytes
|
212
|
+
return data[0, l]
|
213
|
+
elsif type.is_dynamic?
|
214
|
+
l = Util.deserialize_big_endian_to_int arg[0, 32]
|
215
|
+
nested_sub = type.nested_sub
|
216
|
+
|
217
|
+
# ref https://github.com/ethereum/tests/issues/691
|
218
|
+
raise NotImplementedError, "Decoding dynamic arrays with nested dynamic sub-types is not implemented for ABI." if nested_sub.is_dynamic?
|
219
|
+
|
220
|
+
# decoded dynamic-sized arrays
|
221
|
+
return (0...l).map { |i| decode_type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) }
|
222
|
+
elsif !type.dimensions.empty?
|
223
|
+
l = type.dimensions.last[0]
|
224
|
+
nested_sub = type.nested_sub
|
225
|
+
|
226
|
+
# decoded static-size arrays
|
227
|
+
return (0...l).map { |i| decode_type(nested_sub, arg[nested_sub.size * i, nested_sub.size]) }
|
228
|
+
else
|
229
|
+
|
230
|
+
# decoded primitive types
|
231
|
+
return decode_primitive_type type, arg
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Decodes primitive types.
|
236
|
+
#
|
237
|
+
# @param type [Eth::Abi::Type] type to be decoded.
|
238
|
+
# @param data [String] encoded primitive type data string.
|
239
|
+
# @return [String] the decoded data for the type.
|
240
|
+
# @raise [DecodingError] if decoding fails for type.
|
241
|
+
def decode_primitive_type(type, data)
|
242
|
+
case type.base_type
|
243
|
+
when "address"
|
244
|
+
|
245
|
+
# decoded address with 0x-prefix
|
246
|
+
return "0x#{Util.bin_to_hex data[12..-1]}"
|
247
|
+
when "string", "bytes"
|
248
|
+
if type.sub_type.empty?
|
249
|
+
size = Util.deserialize_big_endian_to_int data[0, 32]
|
250
|
+
|
251
|
+
# decoded dynamic-sized array
|
252
|
+
return data[32..-1][0, size]
|
253
|
+
else
|
254
|
+
|
255
|
+
# decoded static-sized array
|
256
|
+
return data[0, type.sub_type.to_i]
|
257
|
+
end
|
258
|
+
when "hash"
|
259
|
+
|
260
|
+
# decoded hash
|
261
|
+
return data[(32 - type.sub_type.to_i), type.sub_type.to_i]
|
262
|
+
when "uint"
|
263
|
+
|
264
|
+
# decoded unsigned integer
|
265
|
+
return Util.deserialize_big_endian_to_int data
|
266
|
+
when "int"
|
267
|
+
u = Util.deserialize_big_endian_to_int data
|
268
|
+
i = u >= 2 ** (type.sub_type.to_i - 1) ? (u - 2 ** type.sub_type.to_i) : u
|
269
|
+
|
270
|
+
# decoded integer
|
271
|
+
return i
|
272
|
+
when "ureal", "ufixed"
|
273
|
+
high, low = type.sub_type.split("x").map(&:to_i)
|
274
|
+
|
275
|
+
# decoded unsigned fixed point numeric
|
276
|
+
return Util.deserialize_big_endian_to_int(data) * 1.0 / 2 ** low
|
277
|
+
when "real", "fixed"
|
278
|
+
high, low = type.sub_type.split("x").map(&:to_i)
|
279
|
+
u = Util.deserialize_big_endian_to_int data
|
280
|
+
i = u >= 2 ** (high + low - 1) ? (u - 2 ** (high + low)) : u
|
281
|
+
|
282
|
+
# decoded fixed point numeric
|
283
|
+
return i * 1.0 / 2 ** low
|
284
|
+
when "bool"
|
285
|
+
|
286
|
+
# decoded boolean
|
287
|
+
return data[-1] == Constant::BYTE_ONE
|
288
|
+
else
|
289
|
+
raise DecodingError, "Unknown primitive type: #{type.base_type}"
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
private
|
294
|
+
|
295
|
+
# Properly encodes unsigned integers.
|
296
|
+
def encode_uint(arg, type)
|
297
|
+
raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::UINT_MAX or arg < Constant::UINT_MIN
|
298
|
+
real_size = type.sub_type.to_i
|
299
|
+
i = arg.to_i
|
300
|
+
raise ValueOutOfBounds, arg unless i >= 0 and i < 2 ** real_size
|
301
|
+
return Util.zpad_int i
|
302
|
+
end
|
303
|
+
|
304
|
+
# Properly encodes signed integers.
|
305
|
+
def encode_int(arg, type)
|
306
|
+
raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::INT_MAX or arg < Constant::INT_MIN
|
307
|
+
real_size = type.sub_type.to_i
|
308
|
+
i = arg.to_i
|
309
|
+
raise ValueOutOfBounds, arg unless i >= -2 ** (real_size - 1) and i < 2 ** (real_size - 1)
|
310
|
+
return Util.zpad_int(i % 2 ** type.sub_type.to_i)
|
311
|
+
end
|
312
|
+
|
313
|
+
# Properly encodes booleans.
|
314
|
+
def encode_bool(arg)
|
315
|
+
raise EncodingError, "Argument is not bool: #{arg}" unless arg.instance_of? TrueClass or arg.instance_of? FalseClass
|
316
|
+
return Util.zpad_int(arg ? 1 : 0)
|
317
|
+
end
|
318
|
+
|
319
|
+
# Properly encodes unsigned fixed-point numbers.
|
320
|
+
def encode_ufixed(arg, type)
|
321
|
+
high, low = type.sub_type.split("x").map(&:to_i)
|
322
|
+
raise ValueOutOfBounds, arg unless arg >= 0 and arg < 2 ** high
|
323
|
+
return Util.zpad_int((arg * 2 ** low).to_i)
|
324
|
+
end
|
325
|
+
|
326
|
+
# Properly encodes signed fixed-point numbers.
|
327
|
+
def encode_fixed(arg, type)
|
328
|
+
high, low = type.sub_type.split("x").map(&:to_i)
|
329
|
+
raise ValueOutOfBounds, arg unless arg >= -2 ** (high - 1) and arg < 2 ** (high - 1)
|
330
|
+
i = (arg * 2 ** low).to_i
|
331
|
+
return Util.zpad_int(i % 2 ** (high + low))
|
332
|
+
end
|
333
|
+
|
334
|
+
# Properly encodes byte-strings.
|
335
|
+
def encode_bytes(arg, type)
|
336
|
+
raise EncodingError, "Expecting String: #{arg}" unless arg.instance_of? String
|
337
|
+
if type.sub_type.empty?
|
338
|
+
size = Util.zpad_int arg.size
|
339
|
+
padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
|
340
|
+
|
341
|
+
# variable length string/bytes
|
342
|
+
return "#{size}#{arg}#{padding}"
|
343
|
+
else
|
344
|
+
raise ValueOutOfBounds, arg unless arg.size <= type.sub_type.to_i
|
345
|
+
padding = Constant::BYTE_ZERO * (32 - arg.size)
|
346
|
+
|
347
|
+
# fixed length string/bytes
|
348
|
+
return "#{arg}#{padding}"
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Properly encodes hash-strings.
|
353
|
+
def encode_hash(arg, type)
|
354
|
+
size = type.sub_type.to_i
|
355
|
+
raise EncodingError, "Argument too long: #{arg}" unless size > 0 and size <= 32
|
356
|
+
if arg.is_a? Integer
|
357
|
+
|
358
|
+
# hash from integer
|
359
|
+
return Util.zpad_int arg
|
360
|
+
elsif arg.size == size
|
361
|
+
|
362
|
+
# hash from encoded hash
|
363
|
+
return Util.zpad arg, 32
|
364
|
+
elsif arg.size == size * 2
|
365
|
+
|
366
|
+
# hash from hexa-decimal hash
|
367
|
+
return Util.zpad_hex arg
|
368
|
+
else
|
369
|
+
raise EncodingError, "Could not parse hash: #{arg}"
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# Properly encodes addresses.
|
374
|
+
def encode_address(arg)
|
375
|
+
if arg.is_a? Integer
|
376
|
+
|
377
|
+
# address from integer
|
378
|
+
return Util.zpad_int arg
|
379
|
+
elsif arg.size == 20
|
380
|
+
|
381
|
+
# address from encoded address
|
382
|
+
return Util.zpad arg, 32
|
383
|
+
elsif arg.size == 40
|
384
|
+
|
385
|
+
# address from hexa-decimal address with 0x prefix
|
386
|
+
return Util.zpad_hex arg
|
387
|
+
elsif arg.size == 42 and arg[0, 2] == "0x"
|
388
|
+
|
389
|
+
# address from hexa-decimal address
|
390
|
+
return Util.zpad_hex arg[2..-1]
|
391
|
+
else
|
392
|
+
raise EncodingError, "Could not parse address: #{arg}"
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
data/lib/klay/address.rb
ADDED
@@ -0,0 +1,106 @@
|
|
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
|
+
# Provides the {Eth} module.
|
16
|
+
module Klay
|
17
|
+
|
18
|
+
# The {Eth::Address} class to handle checksummed Ethereum addresses.
|
19
|
+
class Address
|
20
|
+
|
21
|
+
# Provides a special checksum error if EIP-55 is violated.
|
22
|
+
class CheckSumError < StandardError; end
|
23
|
+
|
24
|
+
# The prefixed and checksummed Ethereum address.
|
25
|
+
attr_reader :address
|
26
|
+
|
27
|
+
# Constructor of the {Eth::Address} class. Creates a new hex
|
28
|
+
# prefixed address.
|
29
|
+
#
|
30
|
+
# @param address [String] hex string representing an ethereum address.
|
31
|
+
def initialize(address)
|
32
|
+
unless Util.is_hex? address
|
33
|
+
raise CheckSumError, "Unknown address type #{address}!"
|
34
|
+
end
|
35
|
+
@address = Util.prefix_hex address
|
36
|
+
unless self.valid?
|
37
|
+
raise CheckSumError, "Invalid address provided #{address}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Checks that the address is valid.
|
42
|
+
#
|
43
|
+
# @return [Boolean] true if valid address.
|
44
|
+
def valid?
|
45
|
+
if !matches_any_format?
|
46
|
+
false
|
47
|
+
elsif not_checksummed?
|
48
|
+
true
|
49
|
+
else
|
50
|
+
checksum_matches?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Generate a checksummed address.
|
55
|
+
#
|
56
|
+
# @return [String] prefixed hexstring representing an checksummed address.
|
57
|
+
def checksummed
|
58
|
+
raise CheckSumError, "Invalid address: #{address}" unless matches_any_format?
|
59
|
+
|
60
|
+
cased = unprefixed.chars.zip(checksum.chars).map do |char, check|
|
61
|
+
check.match(/[0-7]/) ? char.downcase : char.upcase
|
62
|
+
end
|
63
|
+
|
64
|
+
Util.prefix_hex cased.join
|
65
|
+
end
|
66
|
+
|
67
|
+
alias :to_s :checksummed
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Checks whether the address checksum matches.
|
72
|
+
def checksum_matches?
|
73
|
+
address == checksummed
|
74
|
+
end
|
75
|
+
|
76
|
+
# Checks whether the address is not checksummed.
|
77
|
+
def not_checksummed?
|
78
|
+
all_uppercase? || all_lowercase?
|
79
|
+
end
|
80
|
+
|
81
|
+
# Checks whether the address is all upper-case.
|
82
|
+
def all_uppercase?
|
83
|
+
address.match /(?:0[xX])[A-F0-9]{40}/
|
84
|
+
end
|
85
|
+
|
86
|
+
# Checks whether the address is all lower-case.
|
87
|
+
def all_lowercase?
|
88
|
+
address.match /(?:0[xX])[a-f0-9]{40}/
|
89
|
+
end
|
90
|
+
|
91
|
+
# Checks whether the address matches any known format.
|
92
|
+
def matches_any_format?
|
93
|
+
address.match /\A(?:0[xX])[a-fA-F0-9]{40}\z/
|
94
|
+
end
|
95
|
+
|
96
|
+
# Computes the checksum of the address.
|
97
|
+
def checksum
|
98
|
+
Util.bin_to_hex Util.keccak256 unprefixed.downcase
|
99
|
+
end
|
100
|
+
|
101
|
+
# Removes the hex prefix.
|
102
|
+
def unprefixed
|
103
|
+
Util.remove_hex_prefix address
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|