evm_client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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