abi_coder_rb 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1df03ba4a67d09da186cb3326798ef5f2bbbebb48dedb7fb3f30170eac01283e
4
- data.tar.gz: 92cada88c754227fb404dc6ae6198be216eb1db19c39d47e69e15b210fd58095
3
+ metadata.gz: 603c8a3cab98c321861f9480f1f668ffdf3559553974d09bdcd46858d97a44ea
4
+ data.tar.gz: 036b109f1f9f53088d592aeac51a66a9147707ae74a93a431a9c2be65617e37a
5
5
  SHA512:
6
- metadata.gz: 9d1621232bdabc3ee622786cafcef42260fac404df806b53a534791fed8a97884d3d0faca5ab153332b1b02665e95465667678e02d20098218178ef909f09f66
7
- data.tar.gz: 93f19691b3d14cfeaac06b001f24e5910b4160acb90bc06df5066616666b7148701c8d62d5511bacfd716143f49299fc039b46c1896c61f84505cf8179deae01
6
+ metadata.gz: 1d6358050ac16002065cb2b379bafa1ed85f3cacdcec82998c69408cbd22d46ce47e554a73f2ca1bae9edc9279505cd18d62412a87271503dcf1488ca97f3fe5
7
+ data.tar.gz: a4b083f13af344d38aea6acbc5b267f8c0763217ec98041cb8b12d88e2896c5e92df66b8911c6a7524c83f777af05932ebe10ac56bb20523dbacd9a86de52d91
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.0)
4
+ abi_coder_rb (0.2.1)
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
- 1. The most significant difference from the original code is that '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.
8
-
9
- 2. Added type encoding pre- and type decoding post- callbacks to facilitate transforming before encoding and after decoding. See [test](./spec/web3_js_abitests_spec.rb)
10
-
11
- 3. 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).
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
 
@@ -5,7 +5,7 @@ module AbiCoderRb
5
5
  when Uint
6
6
  decode_uint256(data[0, 32])
7
7
  when Int
8
- Utils.abi_to_int_signed(bin_to_hex(data[0, 32]), type.bits)
8
+ abi_to_int_signed(bin_to_hex(data[0, 32]), type.bits)
9
9
  when Bool
10
10
  data[31] == BYTE_ONE
11
11
  when String
@@ -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(Utils.int_to_abi_signed_256bit(arg))
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
@@ -1,111 +1,105 @@
1
1
  module AbiCoderRb
2
- module Utils
3
- class << self
4
- def hex_to_bin(hex)
5
- hex = hex[2..] if %w[0x 0X].include?(hex[0, 2]) ## cut-of leading 0x or 0X if present
6
- hex.scan(/../).map { |x| x.hex.chr }.join
7
- end
8
-
9
- def bin_to_hex(bin)
10
- bin.each_byte.map { |byte| "%02x" % byte }.join
11
- end
12
-
13
- def hex?(str)
14
- str.start_with?("0x") && str.length.even? && str[2..].match?(/\A\b[0-9a-fA-F]+\b\z/)
15
- end
16
-
17
- # example:
18
- # lpad("hello", 'x', 10) => "xxxxxxhello"
19
- def lpad(str, sym, len)
20
- return str if str.size >= len
21
-
22
- sym * (len - str.size) + str
23
- end
24
-
25
- def zpad(str, len)
26
- lpad str, BYTE_ZERO, len
27
- end
28
-
29
- def ffpad(str, len)
30
- lpad str, BYTE_FF, len
31
- end
32
-
33
- def uint_to_big_endian(num, size)
34
- raise "Can only serialize integers" unless num.is_a?(Integer)
35
- raise "Cannot serialize negative integers" if num.negative?
36
- raise "Integer too large (does not fit in #{size} bytes)" if size && num >= 256**size
37
-
38
- # Convert num into a binary string
39
- s = if num.zero?
40
- BYTE_EMPTY
41
- else
42
- hex = num.to_s(16)
43
- hex = "0#{hex}" if hex.size.odd?
44
- hex_to_bin hex
45
- end
46
-
47
- # Adjust the size of the binary string to match the specified `size` in bytes, if `size` is given.
48
- s = size ? "#{BYTE_ZERO * [0, size - s.size].max}#{s}" : s
49
-
50
- zpad s, size
51
- end
52
-
53
- def int_to_abi_signed_256bit(value)
54
- # 确保值在256位有符号整数范围内
55
- min = -2**255
56
- max = 2**255 - 1
57
- raise "Value out of range" if value < min || value > max
58
-
59
- # 为负数计算补码
60
- value = (1 << 256) + value if value < 0
61
-
62
- # 转换为十六进制字符串
63
- hex_str = value.to_s(16)
64
-
65
- # 确保字符串长度为64字符(256位)
66
- hex_str.rjust(64, "0")
67
- end
68
-
69
- def int_to_abi_signed(value)
70
- # 确保值在32位有符号整数范围内
71
- raise "Value out of range" if value < -2**31 || value > 2**31 - 1
72
-
73
- # 转换为32位有符号整数的二进制表示,然后转换为十六进制
74
- hex_str = [value].pack("l>").unpack1("H*")
75
-
76
- # 如果是正数,补齐前导零以达到64位长度
77
- # 如果是负数,pack方法会产生正确的补码形式,但需要确保长度为64
78
- if value >= 0
79
- hex_str.rjust(64, "0")
80
- else
81
- hex_str.rjust(64, "f")
82
- end
83
- end
84
-
85
- def abi_to_int_signed(hex_str, bits)
86
- hex_str = "0x#{hex_str}" if hex_str[0, 2] != "0x" || hex_str[0, 2] != "0X"
87
-
88
- # 计算预期的十六进制字符串长度
89
- expected_length = bits / 4
90
- extended_hex_str = if hex_str.length < expected_length
91
- # 如果输入长度小于预期,根据首位字符扩展字符串
92
- extend_char = hex_str[0] == "f" ? "f" : "0"
93
- extend_char * (expected_length - hex_str.length) + hex_str
94
- else
95
- hex_str
96
- end
97
-
98
- # 将十六进制字符串转换为二进制字符串
99
- binary_str = extended_hex_str.to_i(16).to_s(2).rjust(bits, extended_hex_str[0])
100
-
101
- # 检查符号位并转换为整数
102
- if binary_str[0] == "1" # 负数
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AbiCoderRb
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
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,177 @@
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
+ data_values = decode(@data_type_str, hex_to_bin(data))
59
+
60
+ case flatten
61
+ when true
62
+ if with_names
63
+ combine(@data_field_names_flattened, data_values.flatten)
64
+ else
65
+ data_values.flatten
66
+ end
67
+ when false
68
+ if with_names
69
+ combine(@data_field_names, data_values)
70
+ else
71
+ data_values
72
+ end
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ # fields:
79
+ # [
80
+ # ["root", "bytes32"],
81
+ # ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
82
+ # ]
83
+ #
84
+ # returns:
85
+ # '(bytes32,(address,uint256,uint256,address,uint256,address,bytes))'
86
+ def fields_type_str(fields)
87
+ "(#{
88
+ fields.map do |_name, type|
89
+ if type.is_a?(::Array)
90
+ fields_type_str(type)
91
+ else
92
+ type
93
+ end
94
+ end.join(",")
95
+ })"
96
+ end
97
+
98
+ # fields:
99
+ # [
100
+ # ["root", "bytes32"],
101
+ # ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
102
+ # ]
103
+ #
104
+ # returns:
105
+ # ["root", {"message" => ["channel", "index", "fromChainId", "from", "toChainId", "to", "gasLimit", "encoded"]}
106
+ def fields_names(fields)
107
+ fields.map do |name, type|
108
+ name = name.underscore
109
+ if type.is_a?(::Array)
110
+ { name => fields_names(type) }
111
+ elsif type.is_a?(::String)
112
+ name
113
+ end
114
+ end
115
+ end
116
+
117
+ # fields:
118
+ # [
119
+ # ["root", "bytes32"],
120
+ # ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
121
+ # ]
122
+ #
123
+ # returns:
124
+ # ["root", "message_channel", "message_index", "message_fromChainId", "message_from", "message_toChainId", "message_to", "message_gasLimit", "message_encoded"]
125
+ def fields_names_flatten(fields, prefix = nil)
126
+ fields.map do |name, type|
127
+ name = name.underscore
128
+ if type.is_a?(::Array)
129
+ fields_names_flatten(
130
+ type,
131
+ prefix.nil? ? name : "#{prefix}.#{name}"
132
+ )
133
+ elsif type.is_a?(::String)
134
+ prefix.nil? ? name : "#{prefix}.#{name}"
135
+ end
136
+ end.flatten
137
+ end
138
+
139
+ # returns:
140
+ # [
141
+ # ["root", "bytes32"],
142
+ # ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
143
+ # ]
144
+ def fields_of(inputs)
145
+ inputs.map do |input|
146
+ if input["type"] == "tuple"
147
+ [input["name"], fields_of(input["components"])]
148
+ elsif input["type"] == "enum"
149
+ [input["name"], "uint8"]
150
+ else
151
+ [input["name"], input["type"]]
152
+ end
153
+ end
154
+ end
155
+
156
+ # params:
157
+ # keys = ['key1', 'key2' => ['key2_1', 'key2_2']]
158
+ # values = [1, [2.1, 2.2]]
159
+ #
160
+ # returns:
161
+ # {"key1"=>1, "key2"=>{"key2_1"=>2.1, "key2_2"=>2.2}}
162
+ def combine(keys, values)
163
+ result = {}
164
+
165
+ keys.each_with_index do |key, index|
166
+ if key.is_a?(Hash)
167
+ key.each do |k, v|
168
+ result[k] = combine(v, values[index])
169
+ end
170
+ else
171
+ result[key] = values[index]
172
+ end
173
+ end
174
+
175
+ result
176
+ end
177
+ 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.0
4
+ version: 0.2.1
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-25 00:00:00.000000000 Z
11
+ date: 2023-11-29 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:
@@ -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