evm_client 0.1.0

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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.travis.yml +32 -0
  7. data/CODE_OF_CONDUCT.md +13 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +22 -0
  10. data/LICENSE.txt +21 -0
  11. data/PREREQUISITES.md +75 -0
  12. data/README.md +665 -0
  13. data/Rakefile +11 -0
  14. data/bin/console +14 -0
  15. data/bin/install_parity +22 -0
  16. data/bin/setup +7 -0
  17. data/contracts/AccountingLib.sol +112 -0
  18. data/contracts/AuditorInterface.sol +4 -0
  19. data/contracts/AuditorRegistry.sol +14 -0
  20. data/contracts/CustodianInterface.sol +27 -0
  21. data/contracts/CustodianRegistry.sol +40 -0
  22. data/contracts/DigixConfiguration.sol +68 -0
  23. data/contracts/Directory.sol +67 -0
  24. data/contracts/DoublyLinked.sol +54 -0
  25. data/contracts/GenericInterface.sol +56 -0
  26. data/contracts/GenericRegistry.sol +15 -0
  27. data/contracts/Gold.sol +105 -0
  28. data/contracts/GoldRegistry.sol +82 -0
  29. data/contracts/GoldTokenLedger.sol +3 -0
  30. data/contracts/Interface.sol +27 -0
  31. data/contracts/Minter.sol +3 -0
  32. data/contracts/Recaster.sol +3 -0
  33. data/contracts/Testing.sol +59 -0
  34. data/contracts/VendorInterface.sol +82 -0
  35. data/contracts/VendorRegistry.sol +39 -0
  36. data/contracts/classic/Digixbot.sol +106 -0
  37. data/contracts/classic/DigixbotConfiguration.sol +62 -0
  38. data/contracts/classic/DigixbotEthereum.sol +86 -0
  39. data/contracts/classic/DigixbotUsers.sol +103 -0
  40. data/contracts/classic/Gold.sol +497 -0
  41. data/contracts/classic/GoldRegistry.sol +503 -0
  42. data/contracts/classic/GoldTokenLedger.sol +560 -0
  43. data/contracts/classic/GoldTokenMinter.sol +607 -0
  44. data/contracts/classic/ParticipantRegistry.sol +94 -0
  45. data/contracts/classic/QueueSample.sol +54 -0
  46. data/evm_client.gemspec +36 -0
  47. data/lib/evm_client.rb +15 -0
  48. data/lib/evm_client/abi.rb +32 -0
  49. data/lib/evm_client/client.rb +146 -0
  50. data/lib/evm_client/contract.rb +341 -0
  51. data/lib/evm_client/contract_event.rb +32 -0
  52. data/lib/evm_client/contract_initializer.rb +54 -0
  53. data/lib/evm_client/decoder.rb +99 -0
  54. data/lib/evm_client/deployment.rb +49 -0
  55. data/lib/evm_client/encoder.rb +118 -0
  56. data/lib/evm_client/event_log.rb +88 -0
  57. data/lib/evm_client/explorer_url_helper.rb +25 -0
  58. data/lib/evm_client/formatter.rb +146 -0
  59. data/lib/evm_client/function.rb +40 -0
  60. data/lib/evm_client/function_input.rb +14 -0
  61. data/lib/evm_client/function_output.rb +14 -0
  62. data/lib/evm_client/http_client.rb +44 -0
  63. data/lib/evm_client/initializer.rb +27 -0
  64. data/lib/evm_client/ipc_client.rb +57 -0
  65. data/lib/evm_client/project_initializer.rb +28 -0
  66. data/lib/evm_client/railtie.rb +12 -0
  67. data/lib/evm_client/singleton.rb +39 -0
  68. data/lib/evm_client/solidity.rb +40 -0
  69. data/lib/evm_client/transaction.rb +41 -0
  70. data/lib/evm_client/version.rb +3 -0
  71. data/lib/tasks/ethereum_contract.rake +27 -0
  72. data/lib/tasks/ethereum_node.rake +52 -0
  73. data/lib/tasks/ethereum_test.rake +32 -0
  74. data/lib/tasks/ethereum_transaction.rake +24 -0
  75. metadata +219 -0
@@ -0,0 +1,32 @@
1
+ module EvmClient
2
+ class ContractEvent
3
+
4
+ attr_accessor :name, :signature, :input_types, :inputs,
5
+ :event_string, :address, :client,
6
+ :indexed_inputs, :indexed_outputs,
7
+ :non_indexed_inputs, :non_indexed_outputs
8
+
9
+ def initialize(data)
10
+ @name = data['name']
11
+ @input_types = data['inputs'].map {|x| x['type']}
12
+ @inputs = data['inputs'].map {|x| x['name']}
13
+ @event_string = "#{@name}(#{@input_types.join(",")})"
14
+ @signature = Digest::SHA3.hexdigest(@event_string, 256)
15
+
16
+ @indexed_inputs = Array(data['inputs']).select { |input| input['indexed'] == true }
17
+ @non_indexed_inputs = Array(data['inputs']).select { |input| input['indexed'] == false }
18
+
19
+ @indexed_outputs = Array(data['outputs']).select { |output| output['indexed'] == true }
20
+ @non_indexed_outputs = Array(data['outputs']).select { |output| output['indexed'] == false }
21
+ end
22
+
23
+ def set_address(address)
24
+ @address = address
25
+ end
26
+
27
+ def set_client(client)
28
+ @client = client
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,54 @@
1
+ module EvmClient
2
+
3
+ class ContractInitializer
4
+
5
+ attr_accessor :abi, :binary, :name, :libraries, :needs_linking, :project_initializer, :contract
6
+
7
+ def initialize(contract_name, contract, project_initializer)
8
+ @abi = JSON.parse(contract["abi"]) unless contract.nil?
9
+ @binary = contract["bin"] unless contract.nil?
10
+ @name = contract_name
11
+ @project_initializer = project_initializer
12
+ matchdata = @binary.scan(/_+[a-zA-Z]+_+/).uniq
13
+ @needs_linking = matchdata.present?
14
+ if @needs_linking
15
+ @libraries = matchdata.collect do |libname|
16
+ {name: libname.gsub(/_+/,''), sigil: libname}
17
+ end
18
+ end
19
+ end
20
+
21
+ def link_libraries
22
+ if @needs_linking
23
+ @libraries.each do |library|
24
+ name = library[:name]
25
+ if @project_initializer.libraries[name].nil?
26
+ ENV['ETHEREUM_DEPLOYER_WAIT_TIME'] ||= "120"
27
+ wait_time = ENV['ETHEREUM_DEPLOYER_WAIT_TIME'].to_i
28
+ library_instance = library[:name].constantize.new
29
+ puts "Deploying library #{name}"
30
+ library_instance.deploy_and_wait(wait_time)
31
+ puts "Library deployed at #{library_instance.address}"
32
+ @project_initializer.libraries[name] = library_instance.address
33
+ @binary.gsub!(library[:sigil], library_instance.address.gsub(/^0x/,''))
34
+ else
35
+ address = @project_initializer.libraries[name]
36
+ @binary.gsub!(library[:sigil], address.gsub(/^0x/,''))
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def build(connection)
43
+ @contract = EvmClient::Contract.new(@name, @binary, @abi, client)
44
+ @contract.build(connection)
45
+ end
46
+
47
+ def generate_javascripts(path)
48
+ data = {name: @name, abi: @abi, binary: @binary}
49
+ File.open(File.join(path, "#{@name}.json"), 'w') {|f| f.puts data.to_json}
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,99 @@
1
+ module EvmClient
2
+ class Decoder
3
+
4
+ def decode(type, value, start = 0)
5
+ is_array, arity, array_subtype = Abi::parse_array_type(type)
6
+ if is_array && arity
7
+ decode_static_array(arity, array_subtype, value, start)
8
+ elsif is_array
9
+ decode_dynamic_array(array_subtype, value, start)
10
+ else
11
+ value = value.gsub(/^0x/,'')
12
+ core, subtype = Abi::parse_type(type)
13
+ method_name = "decode_#{core}".to_sym
14
+ self.send(method_name, value, subtype, start)
15
+ end
16
+ end
17
+
18
+ def decode_static_array(arity, array_subtype, value, start)
19
+ (0..arity-1).map { |i| decode(array_subtype, value, start + i * 64) }
20
+ end
21
+
22
+ def decode_dynamic_array(array_subtype, value, start)
23
+ location = decode_uint(value[start..(start+63)]) * 2
24
+ size = decode_uint(value[location..location+63])
25
+ (0..size-1).map { |i| decode(array_subtype, value, location + (i+1) * 64) }
26
+ end
27
+
28
+ def decode_fixed(value, subtype = "128x128", start = 0)
29
+ decode_int(trim(value, start, fixed_bitsize(subtype))).to_f / 2**exponent(subtype)
30
+ end
31
+
32
+ def decode_uint(value, subtype = "256", start = 0)
33
+ trim(value, start, bitsize(subtype)).hex
34
+ end
35
+
36
+ def decode_int(value, subtype = "256", start = 0)
37
+ raise ArgumentError if value.nil?
38
+ size = bitsize(subtype)
39
+ value = trim(value, start, size)
40
+ (value[0..1] == "ff") ? (value.hex - (2 ** size)) : value.hex
41
+ end
42
+
43
+ def decode_bool(value, _, start)
44
+ value = trim(value, start, 4)
45
+ return true if value == "1"
46
+ return false if value == "0"
47
+ raise ArgumentError
48
+ end
49
+
50
+ def decode_address(value, _ = nil, start)
51
+ raise ArgumentError if value.size-start < 64
52
+ value[start+24..start+63]
53
+ end
54
+
55
+ def decode_bytes(value, subtype, start)
56
+ subtype.present? ? decode_static_bytes(value, subtype, start) : decode_dynamic_bytes(value, start)
57
+ end
58
+
59
+ def decode_static_bytes(value, subtype = nil, start = 0)
60
+ trim(value, start, subtype.to_i*8).scan(/.{2}/).collect {|x| x.hex}.pack('C*').strip
61
+ end
62
+
63
+ def decode_dynamic_bytes(value, start = 0)
64
+ location = decode_uint(value[start..(start+63)]) * 2
65
+ size = decode_uint(value[location..location+63]) * 2
66
+ value[location+64..location+63+size].scan(/.{2}/).collect {|x| x.hex}.pack('C*')
67
+ end
68
+
69
+ def decode_string(value, _ = nil, start = 0)
70
+ decode_dynamic_bytes(value, start).force_encoding('utf-8')
71
+ end
72
+
73
+ def decode_arguments(arguments, data)
74
+ data = data.gsub(/^0x/,'')
75
+ types = arguments.map { |o| o.type }
76
+ types.each.with_index.map { |t , i| decode(t, data, i*64) }
77
+ end
78
+
79
+ private
80
+ def trim(value, start, bitsize = 256)
81
+ value[start+63-(bitsize/4-1)..start+63]
82
+ end
83
+
84
+ def bitsize(subtype, default = 256)
85
+ subtype.present? ? subtype.to_i : default
86
+ end
87
+
88
+ def fixed_bitsize(subtype = nil)
89
+ subtype ||= "128x128"
90
+ _, x, n = /(\d+)x(\d+)/.match(subtype).to_a
91
+ x.to_i + n.to_i
92
+ end
93
+
94
+ def exponent(subtype, default = 128)
95
+ subtype.nil? ? default : /(\d+)x(\d+)/.match(subtype)[2].to_i
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,49 @@
1
+ module EvmClient
2
+
3
+ class Deployment
4
+
5
+ DEFAULT_TIMEOUT = 300.seconds
6
+ DEFAULT_STEP = 5.seconds
7
+
8
+ attr_accessor :id, :contract_address, :connection, :deployed, :mined
9
+ attr_reader :valid_deployment
10
+
11
+ def initialize(txid, connection)
12
+ @id = txid
13
+ @connection = connection
14
+ @deployed = false
15
+ @contract_address = nil
16
+ @valid_deployment = false
17
+ end
18
+
19
+ def mined?
20
+ return true if @mined
21
+ @mined = @connection.eth_get_transaction_by_hash(@id)["result"]["blockNumber"].present?
22
+ @mined ||= false
23
+ end
24
+
25
+ def check_deployed
26
+ return false unless @id
27
+ contract_receipt = @connection.eth_get_transaction_receipt(@id)
28
+ result = contract_receipt["result"]
29
+ has_contract_address = result && result["contractAddress"]
30
+ @contract_address ||= result["contractAddress"] if has_contract_address
31
+ has_contract_address && result["blockNumber"]
32
+ end
33
+
34
+ def deployed?
35
+ @valid_deployment ||= check_deployed
36
+ end
37
+
38
+ def wait_for_deployment(timeout: DEFAULT_TIMEOUT, step: DEFAULT_STEP)
39
+ start_time = Time.now
40
+ loop do
41
+ raise "Transaction #{@id} timed out." if ((Time.now - start_time) > timeout)
42
+ yield if block_given?
43
+ return true if deployed?
44
+ sleep step
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,118 @@
1
+ module EvmClient
2
+
3
+ class Encoder
4
+
5
+ def encode(type, value)
6
+ is_array, arity, array_subtype = Abi::parse_array_type(type)
7
+ if is_array && arity
8
+ encode_static_array(arity, array_subtype, value)
9
+ elsif is_array
10
+ encode_dynamic_array(array_subtype, value)
11
+ else
12
+ core, subtype = Abi::parse_type(type)
13
+ method_name = "encode_#{core}".to_sym
14
+ self.send(method_name, value, subtype)
15
+ end
16
+ end
17
+
18
+ def encode_static_array(arity, array_subtype, array)
19
+ raise "Wrong number of arguments" if arity != array.size
20
+ array.inject("") { |a, e| a << encode(array_subtype, e) }
21
+ end
22
+
23
+ def encode_dynamic_array(array_subtype, array)
24
+ location = encode_uint(@inputs ? size_of_inputs(@inputs) + @tail.size/2 : 32)
25
+ size = encode_uint(array.size)
26
+ data = array.inject("") { |a, e| a << encode(array_subtype, e) }
27
+ [location, size + data]
28
+ end
29
+
30
+ def encode_int(value, _ = nil)
31
+ to_twos_complement(value).to_s(16).rjust(64, '0')
32
+ end
33
+
34
+ def encode_uint(value, _ = nil)
35
+ raise ArgumentError if value < 0
36
+ encode_int(value)
37
+ end
38
+
39
+ def encode_bool(value, _)
40
+ (value ? "1" : "0").rjust(64, '0')
41
+ end
42
+
43
+ def encode_fixed(value, subtype)
44
+ n = subtype.nil? ? 128 : /(\d+)x(\d+)/.match(subtype)[2].to_i
45
+ do_encode_fixed(value, n)
46
+ end
47
+
48
+ def do_encode_fixed(value, n)
49
+ encode_uint((value * 2**n).to_i)
50
+ end
51
+
52
+ def encode_ufixed(_value, _)
53
+ raise NotImplementedError
54
+ end
55
+
56
+ def encode_bytes(value, subtype)
57
+ subtype.nil? ? encode_dynamic_bytes(value) : encode_static_bytes(value)
58
+ end
59
+
60
+ def encode_static_bytes(value)
61
+ value.bytes.map {|x| x.to_s(16).rjust(2, '0')}.join("").ljust(64, '0')
62
+ end
63
+
64
+ def encode_dynamic_bytes(value)
65
+ location = encode_uint(@inputs ? size_of_inputs(@inputs) + @tail.size/2 : 32)
66
+ size = encode_uint(value.size)
67
+ content = encode_static_bytes(value)
68
+ [location, size + content]
69
+ end
70
+
71
+ def encode_string(value, _)
72
+ location = encode_uint(@inputs ? size_of_inputs(@inputs) + @tail.size/2 : 32)
73
+ size = encode_uint(value.bytes.size)
74
+ content = value.bytes.map {|x| x.to_s(16).rjust(2, '0')}.join("").ljust(64, '0')
75
+ [location, size + content]
76
+ end
77
+
78
+ def encode_address(value, _)
79
+ value = "0" * 24 + value.gsub(/^0x/,'')
80
+ raise ArgumentError if value.size != 64
81
+ value
82
+ end
83
+
84
+ def ensure_prefix(value)
85
+ value.start_with?("0x") ? value : ("0x" + value)
86
+ end
87
+
88
+ def encode_arguments(inputs, args)
89
+ raise "Wrong number of arguments" if inputs.length != args.length
90
+ @head = ""
91
+ @tail = ""
92
+ @inputs = inputs
93
+ inputs.each.with_index do |input, index|
94
+ encoded = encode(input.type, args[index])
95
+ if encoded.is_a? Array
96
+ @head << encoded[0]
97
+ @tail << encoded[1]
98
+ else
99
+ @head << encoded
100
+ end
101
+ end
102
+ @head + @tail
103
+ end
104
+
105
+ private
106
+ def to_twos_complement(number)
107
+ (number & ((1 << 256) - 1))
108
+ end
109
+
110
+ def size_of_inputs(inputs)
111
+ inputs.map do |input|
112
+ _, arity, _ = Abi::parse_array_type(input.type)
113
+ arity.nil? ? 32 : arity * 32
114
+ end.inject(:+)
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,88 @@
1
+ module EvmClient
2
+ class EventLog
3
+ attr_reader :address, :block_hash, :block_number, :data, :log_index, :removed,
4
+ :topics, :transaction_hash, :transaction_index, :signature, :contract, :event
5
+
6
+ def self.build(raw_response:, contract:)
7
+ signature = raw_response['topics'][0]
8
+ event = contract.events.find { |event| signature.match(event.signature) }
9
+
10
+ new(
11
+ address: raw_response['address'],
12
+ block_hash: raw_response['blockHash'],
13
+ block_number: raw_response['blockNumber'].to_i(16),
14
+ data: raw_response['data'],
15
+ log_index: raw_response['logIndex'],
16
+ removed: raw_response['removed'],
17
+ topics: raw_response['topics'],
18
+ transaction_hash: raw_response['transactionHash'],
19
+ transaction_index: raw_response['transactionIndex'],
20
+ signature: signature,
21
+ contract: contract,
22
+ event: event
23
+ )
24
+ end
25
+
26
+ def initialize( address:, block_hash:, block_number:, data:, log_index:, removed:,
27
+ topics:, transaction_hash:, transaction_index:, signature:, contract:, event:)
28
+ @address = address
29
+ @block_hash = block_hash
30
+ @block_number = block_number
31
+ @data = data
32
+ @log_index = log_index
33
+ @removed = removed
34
+ @topics = topics
35
+ @transaction_hash = transaction_hash
36
+ @transaction_index = transaction_index
37
+ @signature = signature
38
+ @contract = contract
39
+ @event = event
40
+ end
41
+
42
+ def name
43
+ event.name
44
+ end
45
+
46
+ def return_values
47
+ return_values = {}
48
+
49
+ indexed_params.each_with_index do |param, index|
50
+ return_values[param.name] = topics[index+1]
51
+ end
52
+
53
+ non_indexed_params.each_with_index do |param, index|
54
+ return_values[param.name] = Decoder.new.decode_arguments(non_indexed_params, data)[index]
55
+ end
56
+
57
+ return_values
58
+ end
59
+
60
+ def to_h
61
+ {
62
+ address: address,
63
+ block_hash: block_hash,
64
+ block_number: block_number,
65
+ data: data,
66
+ log_index: log_index,
67
+ removed: removed,
68
+ topics: topics,
69
+ transaction_hash: transaction_hash,
70
+ transaction_index: transaction_index,
71
+ signature: signature,
72
+ name: name,
73
+ contract_name: contract.name,
74
+ return_values: return_values
75
+ }
76
+ end
77
+
78
+ private
79
+
80
+ def indexed_params
81
+ [event.indexed_inputs, event.indexed_outputs].flatten.map { |input| OpenStruct.new(input) }
82
+ end
83
+
84
+ def non_indexed_params
85
+ [event.non_indexed_inputs, event.non_indexed_outputs].flatten.map { |output| OpenStruct.new(output) }
86
+ end
87
+ end
88
+ end