etheruby 0.0.2 → 0.0.3

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
  SHA1:
3
- metadata.gz: 200899891a140cea002a7e7d9c1ac9916908275b
4
- data.tar.gz: d01b91005637a2ee3543a0d1206a8a59bd265f93
3
+ metadata.gz: 0c566325afa370e6f3d4dc52964b205b26a53da7
4
+ data.tar.gz: 10cd9eac53229fe3a293fe0cefc879abf9d2cc3f
5
5
  SHA512:
6
- metadata.gz: 76545aa6b9a4f8bf92e18b35556a2b4361607e5628760d940c4badf57e9a564dde5785fd6b3694c409a4800bf2df4e0f5129abbf1f3f1b78d36334015cd3386f
7
- data.tar.gz: d7f1fcaf796b041096484d2ba695a5c9acbd4ee1c92cc22dea0822b3664dd807e2035b372751e2d8d968f13a483f1a2933578f5241fdc8ac8bc95c809a96f045
6
+ metadata.gz: 6dc714bdcc04e5778b508d809e5e29fb230c4a1640f3e377c55d123a12d9929eb879f6d73175274816b7491a0943c2e96a0b60216ce240b2630e3038ff5553e8
7
+ data.tar.gz: c3cae2decb6fb2c2f8e79c3d27ed2dcbf3cf2c2ece666382445892cba8f226a4946e1f598a4f2803dd358b0158f35fab32c9cf24b3c1270a83c2ab8a97ea708b
@@ -1,9 +1,12 @@
1
+ require 'bigdecimal'
2
+ require_relative 'type_matchers'
3
+
1
4
  module Etheruby
2
5
 
3
6
  class IncorrectTypeError < StandardError; end
4
7
  class ArgumentsCountError < StandardError; end
8
+ class InvalidFormatForDataError < StandardError; end
5
9
 
6
- # https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
7
10
  class ArgumentsGenerator
8
11
 
9
12
  attr_reader :params, :args
@@ -13,51 +16,33 @@ module Etheruby
13
16
  @args = args
14
17
  end
15
18
 
16
- def is_sized_type(param)
17
- param.to_s.match /^([a-z]+)(\d+)$/
18
- end
19
+ def treat_variable(param, arg)
20
+ if match = TypeMatchers.is_sized_type(param)
21
+ # Parameter is a sized type, e.g. uint256, byte32 ...
22
+ send("#{match[1]}_encode".to_sym, match[2].to_i, arg)
19
23
 
20
- def is_static_array_type(param)
21
- param.to_s.match(/^([a-z]+)\[(\d+)\]$/)
22
- end
24
+ elsif match = TypeMatchers.is_dualsized_type(param)
25
+ # Parameter is a dual sized array type, e.g. fixed16x16
26
+ send("#{match[1]}_encode".to_sym, match[2].to_i, match[3].to_i, arg)
27
+
28
+ elsif match = TypeMatchers.is_static_array_type(param)
29
+ # Parameter is a staticly sized array type, e.g. uint256[24]
30
+ static_array_encode(match[1], match[2].to_i, arg)
31
+
32
+ elsif match = TypeMatchers.is_dynamic_array_type(param)
33
+ # Parameter is a dynamicaly sized array type, e.g. uint256[]
34
+ dynamic_array_encode(match[1], arg)
23
35
 
24
- def is_dynamic_array_type(param)
25
- param.to_s.match(/^([a-z]+)\[\]$/)
36
+ else
37
+ # Parameter is a single-word type : string, bytes, address etc...
38
+ send("#{param}_encode".to_sym, arg)
39
+
40
+ end
26
41
  end
27
42
 
28
43
  def to_s
29
44
  raise ArgumentsCountError.new unless params.count == args.count
30
- arguments = ""
31
- # For each parameter of the method called, we
32
- # match the corresponding type to encode and we send the
33
- # parameter given to this encoder
34
- (0..params.count-1).each do |i|
35
- param, arg = params[i], args[i]
36
- if match = is_sized_type(param)
37
- # Parameter is a sized type, e.g. uint256, byte32 ...
38
- method_name = "#{match[1]}_encode".to_sym
39
- if respond_to?(method_name)
40
- arguments += send(method_name, match[2].to_i, arg)
41
- else
42
- raise IncorrectTypeError.new("Type #{param} cannot be encoded")
43
- end
44
- elsif match = is_static_array_type(param)
45
- # Parameter is a staticly sized array type, e.g. uint256[24]
46
- arguments += static_array(match[1], match[2].to_i, arg)
47
- elsif match = is_dynamic_array_type(param)
48
- # Parameter is a dynamicaly sized array type, e.g. uint256[]
49
- arguments += dynamic_array(match[1], arg)
50
- else
51
- # Parameter is a single-word type : string, bytes, address etc...
52
- method_name = "#{param}_encode".to_sym
53
- if respond_to?(method_name)
54
- arguments += send(method_name, arg)
55
- else
56
- raise IncorrectTypeError.new("Type #{param} cannot be encoded")
57
- end
58
- end
59
- end
60
- arguments
45
+ (0..params.count-1).map { |i| treat_variable(params[i], args[i]) }.join
61
46
  end
62
47
 
63
48
  ##
@@ -74,37 +59,60 @@ module Etheruby
74
59
  ##
75
60
  # uint<X> encoding
76
61
  def uint_encode(size, arg)
77
- arg.to_s(16).rjust(size / 4,'0')
62
+ raise InvalidFormatForDataError.new("unsigned integer #{arg} < 0") if arg < 0
63
+ int_encode(size, arg)
64
+ end
65
+
66
+ ##
67
+ # ufixed<X> encoding
68
+ def ufixed_encode(size_i, size_d, arg)
69
+ raise InvalidFormatForDataError.new("unsigned fixed #{arg} < 0") if arg < 0
70
+ fixed_encode(size_i, size_d, arg)
78
71
  end
79
72
 
80
73
  ##
81
74
  # fixed<X> encoding
82
- def fixed_encode(size, arg)
83
- #Todo
75
+ def fixed_encode(size_i, size_d, arg)
76
+ raise InvalidFormatForDataError.new("Please use BigDecimal !") unless arg.is_a? BigDecimal
77
+ if arg >= 0
78
+ int_part, dec_part = arg.to_i, arg - arg.to_i
79
+ else
80
+ int_part, dec_part = arg.to_i, (arg + arg.to_i.abs).abs
81
+ end
82
+ int_encode(size_i, int_part) + \
83
+ decimal_representation(size_d, dec_part)
84
84
  end
85
85
 
86
86
  ##
87
- # ufixed<X> encoding
88
- def ufixed_encode(size, arg)
89
- #Todo
87
+ # Represent the decimal part in hexadimal according to the precision
88
+ def decimal_representation(precision, value)
89
+ (0..precision-1).map {
90
+ int_part, value = (value*2).to_i, (value*2) - (value*2).to_i
91
+ int_part
92
+ }.each_slice(8).map { |slice|
93
+ slice.join.to_i(2).to_s(16).rjust(2,'0')
94
+ }.join
90
95
  end
91
96
 
92
97
  ##
93
98
  # Encode a static array
94
- def static_array(type, size, arg)
95
- #Todo
99
+ def static_array_encode(type, size, arg)
100
+ raise InvalidFormatForDataError.new(
101
+ "Array have #{arg.count} items for #{size} sized variable"
102
+ ) unless arg.count == size
103
+ arg.map { |item| treat_variable(type, item) }.join
96
104
  end
97
105
 
98
106
  ##
99
107
  # Creates a dynamic array
100
- def dynamic_array(type, arg)
101
- #Todo
108
+ def dynamic_array_encode(type, arg)
109
+ uint_encode(256, arg.count) + static_array_encode(type, arg.count, arg)
102
110
  end
103
111
 
104
112
  ##
105
113
  # byte<X> encodeing
106
114
  def byte_encode(size, arg)
107
- arg.map{ |b| b.to_s(16) }.join.rjust(size,'0')
115
+ arg.map{ |b| b.to_s(16).rjust(2,'0') }.join.rjust(size,'0')
108
116
  end
109
117
 
110
118
  ##
@@ -122,7 +130,7 @@ module Etheruby
122
130
  ##
123
131
  # bytes (dynamic size) encoding
124
132
  def bytes_encode(arg)
125
- uint_encode(256, arg.count) + arg.map{ |b| b.to_s(16) }.join
133
+ uint_encode(256, arg.count) + arg.map{ |b| b.to_s(16).rjust(2,'0') }.join
126
134
  end
127
135
 
128
136
  ##
@@ -20,7 +20,7 @@ module Etheruby
20
20
  @sym = sym
21
21
  end
22
22
  def method_missing(sym, *data)
23
- body = { 'id' => 'jsonrpc', 'method' => "#{@sym}_#{sym}", 'params' => data }
23
+ body = { 'id' => '1', 'method' => "#{@sym}_#{sym}", 'params' => data }
24
24
  text_response = http_post_request(::MultiJson.dump(body))
25
25
  ::MultiJson.load(text_response)
26
26
  end
@@ -0,0 +1,110 @@
1
+ require_relative 'type_matchers'
2
+
3
+ module Etheruby
4
+
5
+ class ResponseParser
6
+
7
+ def initialize(_returns, _response)
8
+ @returns = _returns
9
+ @response = _response
10
+ end
11
+
12
+ def treat_variable(param)
13
+ if match = TypeMatchers.is_sized_type(param)
14
+ # Parameter is a sized type, e.g. uint256, byte32 ...
15
+ send("#{match[1]}_decode".to_sym, match[2].to_i)
16
+
17
+ elsif match = TypeMatchers.is_dualsized_type(param)
18
+ # Parameter is a dual sized array type, e.g. fixed16x16
19
+ send("#{match[1]}_decode".to_sym, match[2].to_i, match[3].to_i)
20
+
21
+ elsif match = TypeMatchers.is_static_array_type(param)
22
+ # Parameter is a staticly sized array type, e.g. uint256[24]
23
+ static_array_decode(match[1], match[2].to_i)
24
+
25
+ elsif match = TypeMatchers.is_dynamic_array_type(param)
26
+ # Parameter is a dynamicaly sized array type, e.g. uint256[]
27
+ dynamic_array_decode(match[1])
28
+
29
+ else
30
+ # Parameter is a single-word type : string, bytes, address etc...
31
+ send("#{param}_decode".to_sym)
32
+
33
+ end
34
+ end
35
+
36
+ def parse
37
+ if @returns.count == 1
38
+ treat_variable(@returns[0])
39
+ else
40
+ @returns.map { |type| treat_variable(type) }
41
+ end
42
+ end
43
+
44
+ # Each decode method will receive in parameters the response string remaining
45
+ # to parse. It will extract the type of the response considering it as the
46
+ # first thing in the string and returns the string without it. Doing this,
47
+ # method calls will be chainable easily.
48
+
49
+ ##
50
+ # int<X> decoding
51
+ def int_decode(size)
52
+ end
53
+
54
+ ##
55
+ # uint<X> decoding
56
+ def uint_decode(size)
57
+ v, @response = @response[0..(size/4)].to_i(16),
58
+ @response[(size/4)..@response.length]
59
+ v
60
+ end
61
+
62
+ ##
63
+ # ufixed<X> decoding
64
+ def ufixed_decode(size_i, size_d)
65
+ end
66
+
67
+ ##
68
+ # fixed<X> decoding
69
+ def fixed_decode(size_i, size_d)
70
+ end
71
+
72
+ ##
73
+ # Decodes a static array
74
+ def static_array_decode(type, size)
75
+ end
76
+
77
+ ##
78
+ # Decodes a dynamic array
79
+ def dynamic_array_decode(type)
80
+ end
81
+
82
+ ##
83
+ # byte<X> decoding
84
+ def byte_decode(size)
85
+ end
86
+
87
+ ##
88
+ # address<X> decoding
89
+ def address_decode
90
+ end
91
+
92
+ ##
93
+ # string<x> decoding (as bytes)
94
+ def string_decode
95
+ end
96
+
97
+ ##
98
+ # bytes (dynamic size) decoding
99
+ def bytes_decode
100
+ end
101
+
102
+ ##
103
+ # boolean decoding (as uint8)
104
+ def bool_decode
105
+ uint_decode(8) == 1
106
+ end
107
+
108
+ end
109
+
110
+ end
@@ -0,0 +1,25 @@
1
+ module Etheruby
2
+
3
+ module TypeMatchers
4
+
5
+ def is_sized_type(param)
6
+ param.to_s.match /^([a-z]+)(\d+)$/
7
+ end
8
+
9
+ def is_dualsized_type(param)
10
+ param.to_s.match /^(.+)(\d+)x(\d+)$/
11
+ end
12
+
13
+ def is_static_array_type(param)
14
+ param.to_s.match(/^(.+)\[(\d+)\]$/)
15
+ end
16
+
17
+ def is_dynamic_array_type(param)
18
+ param.to_s.match(/^(.+)\[\]$/)
19
+ end
20
+
21
+ module_function :is_sized_type, :is_dualsized_type,
22
+ :is_static_array_type, :is_dynamic_array_type
23
+ end
24
+
25
+ end
data/lib/etheruby.rb CHANGED
@@ -3,6 +3,7 @@ require 'logger'
3
3
  require_relative 'etheruby/client'
4
4
  require_relative 'etheruby/contract_method_dsl'
5
5
  require_relative 'etheruby/arguments_generator'
6
+ require_relative 'etheruby/response_parser'
6
7
 
7
8
  module Etheruby
8
9
  class NoContractMethodError < StandardError; end
@@ -52,7 +53,11 @@ module Etheruby
52
53
  else
53
54
  @@logger.debug("Response from API for #{method_info[:name]} : #{response.inspect}")
54
55
  end
55
- response['result']
56
+ if method_info.has_key? :returns
57
+ ResponseParser.new(method_info[:returns], response['result']).parse
58
+ else
59
+ response['result']
60
+ end
56
61
  end
57
62
 
58
63
  def self.address
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: etheruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jérémy SEBAN
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-16 00:00:00.000000000 Z
11
+ date: 2017-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: digest-sha3
@@ -80,7 +80,8 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '3.5'
83
- description: Describe ethereum smart-contract and execute them with ease.
83
+ description: Etheruby is a library including a client for the JSON-RPC API and a Object-Contract
84
+ Mapper to interact with smart-contracts.
84
85
  email: jeremy@seban.eu
85
86
  executables: []
86
87
  extensions: []
@@ -90,6 +91,8 @@ files:
90
91
  - lib/etheruby/arguments_generator.rb
91
92
  - lib/etheruby/client.rb
92
93
  - lib/etheruby/contract_method_dsl.rb
94
+ - lib/etheruby/response_parser.rb
95
+ - lib/etheruby/type_matchers.rb
93
96
  homepage: https://github.com/MechanicalSloth/etheruby
94
97
  licenses:
95
98
  - MIT