abi_coder_rb 0.2.0 → 0.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1df03ba4a67d09da186cb3326798ef5f2bbbebb48dedb7fb3f30170eac01283e
4
- data.tar.gz: 92cada88c754227fb404dc6ae6198be216eb1db19c39d47e69e15b210fd58095
3
+ metadata.gz: 0f20aa23f421459866cdbd1cd485027ec5fec4b52af891adc9c4025f247b42a9
4
+ data.tar.gz: 439e3e33e16d8cd9b20267f6e35218adb3c542f7aa6e80482c8d9aafee220bd3
5
5
  SHA512:
6
- metadata.gz: 9d1621232bdabc3ee622786cafcef42260fac404df806b53a534791fed8a97884d3d0faca5ab153332b1b02665e95465667678e02d20098218178ef909f09f66
7
- data.tar.gz: 93f19691b3d14cfeaac06b001f24e5910b4160acb90bc06df5066616666b7148701c8d62d5511bacfd716143f49299fc039b46c1896c61f84505cf8179deae01
6
+ metadata.gz: 6c423bbce7507e4dc2d79d73355c65b84c8ceff0bc4c9e89045e23a461084898c9b97e35225e6b0b8e0173ddb05d1dfd13c3df5e7071cfe05960254037e2a3f9
7
+ data.tar.gz: d2977279469c49bc63d67c360d12b281bb2442856dddc166ef784b1311198afb0dabbe19604160c53edd5c54047b031113a6ae18e5ef60b93611b68fa9f8b657
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.2)
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.2"
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,179 @@
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
+ return with_names ? {} : [] if @data_type_str == "()"
59
+
60
+ data_values = decode(@data_type_str, hex_to_bin(data))
61
+
62
+ case flatten
63
+ when true
64
+ if with_names
65
+ combine(@data_field_names_flattened, data_values.flatten)
66
+ else
67
+ data_values.flatten
68
+ end
69
+ when false
70
+ if with_names
71
+ combine(@data_field_names, data_values)
72
+ else
73
+ data_values
74
+ end
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ # fields:
81
+ # [
82
+ # ["root", "bytes32"],
83
+ # ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
84
+ # ]
85
+ #
86
+ # returns:
87
+ # '(bytes32,(address,uint256,uint256,address,uint256,address,bytes))'
88
+ def fields_type_str(fields)
89
+ "(#{
90
+ fields.map do |_name, type|
91
+ if type.is_a?(::Array)
92
+ fields_type_str(type)
93
+ else
94
+ type
95
+ end
96
+ end.join(",")
97
+ })"
98
+ end
99
+
100
+ # fields:
101
+ # [
102
+ # ["root", "bytes32"],
103
+ # ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
104
+ # ]
105
+ #
106
+ # returns:
107
+ # ["root", {"message" => ["channel", "index", "fromChainId", "from", "toChainId", "to", "gasLimit", "encoded"]}
108
+ def fields_names(fields)
109
+ fields.map do |name, type|
110
+ name = name.underscore
111
+ if type.is_a?(::Array)
112
+ { name => fields_names(type) }
113
+ elsif type.is_a?(::String)
114
+ name
115
+ end
116
+ end
117
+ end
118
+
119
+ # fields:
120
+ # [
121
+ # ["root", "bytes32"],
122
+ # ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
123
+ # ]
124
+ #
125
+ # returns:
126
+ # ["root", "message_channel", "message_index", "message_fromChainId", "message_from", "message_toChainId", "message_to", "message_gasLimit", "message_encoded"]
127
+ def fields_names_flatten(fields, prefix = nil)
128
+ fields.map do |name, type|
129
+ name = name.underscore
130
+ if type.is_a?(::Array)
131
+ fields_names_flatten(
132
+ type,
133
+ prefix.nil? ? name : "#{prefix}.#{name}"
134
+ )
135
+ elsif type.is_a?(::String)
136
+ prefix.nil? ? name : "#{prefix}.#{name}"
137
+ end
138
+ end.flatten
139
+ end
140
+
141
+ # returns:
142
+ # [
143
+ # ["root", "bytes32"],
144
+ # ["message", [["channel", "address"], ["index", "uint256"], ["fromChainId", "uint256"], ["from", "address"], ["toChainId", "uint256"], ["to", "address"], ["encoded", "bytes"]]]
145
+ # ]
146
+ def fields_of(inputs)
147
+ inputs.map do |input|
148
+ if input["type"] == "tuple"
149
+ [input["name"], fields_of(input["components"])]
150
+ elsif input["type"] == "enum"
151
+ [input["name"], "uint8"]
152
+ else
153
+ [input["name"], input["type"]]
154
+ end
155
+ end
156
+ end
157
+
158
+ # params:
159
+ # keys = ['key1', 'key2' => ['key2_1', 'key2_2']]
160
+ # values = [1, [2.1, 2.2]]
161
+ #
162
+ # returns:
163
+ # {"key1"=>1, "key2"=>{"key2_1"=>2.1, "key2_2"=>2.2}}
164
+ def combine(keys, values)
165
+ result = {}
166
+
167
+ keys.each_with_index do |key, index|
168
+ if key.is_a?(Hash)
169
+ key.each do |k, v|
170
+ result[k] = combine(v, values[index])
171
+ end
172
+ else
173
+ result[key] = values[index]
174
+ end
175
+ end
176
+
177
+ result
178
+ end
179
+ 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.2
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-30 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