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,25 @@
1
+ module EvmClient
2
+ module ExplorerUrlHelper
3
+
4
+ CHAIN_PREFIX = {
5
+ 17 => "no-explorer-for-devmode.",
6
+ 42 => "kovan.",
7
+ 3 => "ropsten.",
8
+ 4 => "rinkeby.",
9
+ 5 => "goerli."
10
+ }
11
+
12
+ def link_to_tx(label, txid, **opts)
13
+ link_to label, explorer_path("tx/#{txid}"), {target: "_blank"}.merge(opts)
14
+ end
15
+
16
+ def link_to_address(label, address, **opts)
17
+ link_to label, explorer_path("address/#{address}"), {target: "_blank"}.merge(opts)
18
+ end
19
+
20
+ def explorer_path(suffix, version = EvmClient::Singleton.instance.get_chain)
21
+ prefix = CHAIN_PREFIX.fetch(version, "")
22
+ "https://#{prefix}etherscan.io/#{suffix}"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,146 @@
1
+ module EvmClient
2
+ class Formatter
3
+
4
+ UNITS = {
5
+ wei: 1,
6
+ kwei: 1000,
7
+ ada: 1000,
8
+ femtoether: 1000,
9
+ mwei: 1000000,
10
+ babbage: 1000000,
11
+ picoether: 1000000,
12
+ gwei: 1000000000,
13
+ shannon: 1000000000,
14
+ nanoether: 1000000000,
15
+ nano: 1000000000,
16
+ szabo: 1000000000000,
17
+ microether: 1000000000000,
18
+ micro: 1000000000000,
19
+ finney: 1000000000000000,
20
+ milliether: 1000000000000000,
21
+ milli: 1000000000000000,
22
+ ether: 1000000000000000000,
23
+ eth: 1000000000000000000,
24
+ kether: 1000000000000000000000,
25
+ grand: 1000000000000000000000,
26
+ einstein: 1000000000000000000000,
27
+ mether: 1000000000000000000000000,
28
+ gether: 1000000000000000000000000000,
29
+ tether: 1000000000000000000000000000000
30
+ }
31
+
32
+ def valid_address?(address_string)
33
+ address = address_string.gsub(/^0x/,'')
34
+ return false if address == "0000000000000000000000000000000000000000"
35
+ return false if address.length != 40
36
+ return !(address.match(/[0-9a-fA-F]+/).nil?)
37
+ end
38
+
39
+ def from_bool(boolval)
40
+ return nil if boolval.nil?
41
+ boolval ? "1" : "0"
42
+ end
43
+
44
+ def to_bool(hexstring)
45
+ return nil if hexstring.nil?
46
+ (hexstring == "0000000000000000000000000000000000000000000000000000000000000001")
47
+ end
48
+
49
+ def to_ascii(hexstring)
50
+ return nil if hexstring.nil?
51
+ hexstring.gsub(/^0x/,'').scan(/.{2}/).collect {|x| x.hex}.pack("c*")
52
+ end
53
+
54
+ def to_utf8(hexstring)
55
+ return nil if hexstring.nil?
56
+ hexstring.gsub(/^0x/,'').scan(/.{2}/).collect {|x| x.hex}.pack("U*").delete("\u0000")
57
+ end
58
+
59
+ def from_ascii(ascii_string)
60
+ return nil if ascii_string.nil?
61
+ ascii_string.unpack('H*')[0]
62
+ end
63
+
64
+ def from_utf8(utf8_string)
65
+ return nil if utf8_string.nil?
66
+ utf8_string.force_encoding('UTF-8').split("").collect {|x| x.ord.to_s(16).rjust(2, '0')}.join("")
67
+ end
68
+
69
+ def to_address(hexstring)
70
+ return "0x0000000000000000000000000000000000000000" if hexstring.nil?
71
+ "0x" + hexstring[-40..-1]
72
+ end
73
+
74
+ def to_wei(amount, unit = "ether")
75
+ return nil if amount.nil?
76
+ BigDecimal(UNITS[unit.to_sym] * amount, 16).to_s.to_i rescue nil
77
+ end
78
+
79
+ def from_wei(amount, unit = "ether")
80
+ return nil if amount.nil?
81
+ (BigDecimal(amount, 16) / BigDecimal(UNITS[unit.to_sym], 16)).to_s rescue nil
82
+ end
83
+
84
+ def from_address(address)
85
+ return "0x0000000000000000000000000000000000000000" if address.nil?
86
+ address.gsub(/^0x/,'').rjust(64, "0")
87
+ end
88
+
89
+ def to_param(string)
90
+ string.ljust(64, '0')
91
+ end
92
+
93
+ def from_input(string)
94
+ string[10..-1].scan(/.{64}/)
95
+ end
96
+
97
+ def to_twos_complement(number)
98
+ (number & ((1 << 256) - 1)).to_s(16)
99
+ end
100
+
101
+ def to_int(hexstring)
102
+ return nil if hexstring.nil?
103
+ (hexstring.gsub(/^0x/,'')[0..1] == "ff") ? (hexstring.hex - (2 ** 256)) : hexstring.hex
104
+ end
105
+
106
+ def get_base_type(typename)
107
+ typename.gsub(/\d+/,'')
108
+ end
109
+
110
+ def from_payload(args)
111
+ converter = "output_to_#{self.get_base_type(args[0])}".to_sym
112
+ self.send(converter, args[1])
113
+ end
114
+
115
+ def output_to_address(bytes)
116
+ self.to_address(bytes)
117
+ end
118
+
119
+ def output_to_bytes(bytes)
120
+ self.to_utf8(bytes)
121
+ end
122
+
123
+ def output_to_string(bytes)
124
+ self.to_utf8(bytes)
125
+ end
126
+
127
+ def output_to_uint(bytes)
128
+ self.to_int(bytes)
129
+ end
130
+
131
+ def output_to_int(bytes)
132
+ self.to_int(bytes)
133
+ end
134
+
135
+ def output_to_bool(bytes)
136
+ self.to_bool(bytes.gsub(/^0x/,''))
137
+ end
138
+
139
+ def to_output(args)
140
+ converter = "output_to_#{self.get_base_type(args[0])}".to_sym
141
+ self.send(converter, args[1])
142
+ end
143
+
144
+ end
145
+
146
+ end
@@ -0,0 +1,40 @@
1
+ module EvmClient
2
+ class Function
3
+
4
+ attr_accessor :name, :inputs, :outputs, :signature, :minified_signature, :constant, :function_string
5
+
6
+ def initialize(data)
7
+ @name = data["name"]
8
+ @constant = data["constant"]
9
+
10
+ @inputs = data["inputs"].map do |input|
11
+ EvmClient::FunctionInput.new(input)
12
+ end
13
+
14
+ @outputs = data["outputs"].collect do |output|
15
+ EvmClient::FunctionOutput.new(output)
16
+ end
17
+
18
+ @function_string = self.class.calc_signature(@name, @inputs)
19
+ @signature = self.class.calc_id(@function_string)
20
+ @minified_signature = signature[0..7]
21
+ end
22
+
23
+ def self.to_canonical_type(type)
24
+ type
25
+ .gsub(/(int)(\z|\D)/, '\1256\2')
26
+ .gsub(/(uint)(\z|\D)/, '\1256\2')
27
+ .gsub(/(fixed)(\z|\D)/, '\1128x128\2')
28
+ .gsub(/(ufixed)(\z|\D)/, '\1128x128\2')
29
+ end
30
+
31
+ def self.calc_signature(name, inputs)
32
+ "#{name}(#{inputs.collect {|x| self.to_canonical_type(x.type) }.join(",")})"
33
+ end
34
+
35
+ def self.calc_id(signature)
36
+ Digest::SHA3.hexdigest(signature, 256)
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ module EvmClient
2
+ class FunctionInput
3
+
4
+ attr_accessor :type, :name, :indexed
5
+
6
+ def initialize(data)
7
+ @type = data["type"]
8
+ @name = data["name"]
9
+ @indexed = data["index"]
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,14 @@
1
+ module EvmClient
2
+
3
+ class FunctionOutput
4
+
5
+ attr_accessor :type, :name
6
+
7
+ def initialize(data)
8
+ @type = data["type"]
9
+ @name = data["name"]
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,44 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ module EvmClient
4
+ class HttpClient < Client
5
+ attr_accessor :host, :port, :uri, :ssl, :proxy
6
+
7
+ def initialize(host, proxy = nil, log = false)
8
+ super(log)
9
+ uri = URI.parse(host)
10
+ raise ArgumentError unless ['http', 'https'].include? uri.scheme
11
+ @host = uri.host
12
+ @port = uri.port
13
+ @proxy = proxy
14
+
15
+ @ssl = uri.scheme == 'https'
16
+ @uri = URI("#{uri.scheme}://#{@host}:#{@port}#{uri.path}")
17
+ end
18
+
19
+ def send_single(payload)
20
+ if @proxy.present?
21
+ _, p_username, p_password, p_host, p_port = @proxy.gsub(/(:|\/|@)/,' ').squeeze(' ').split
22
+ http = ::Net::HTTP.new(@host, @port, p_host, p_port, p_username, p_password)
23
+ else
24
+ http = ::Net::HTTP.new(@host, @port)
25
+ end
26
+
27
+ if @ssl
28
+ http.use_ssl = true
29
+ end
30
+ header = {'Content-Type' => 'application/json'}
31
+ request = ::Net::HTTP::Post.new(uri, header)
32
+ request.body = payload
33
+ response = http.request(request)
34
+ response.body
35
+ end
36
+
37
+ def send_batch(batch)
38
+ result = send_single(batch.to_json)
39
+ result = JSON.parse(result)
40
+ result.sort_by! { |c| c['id'] }
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,27 @@
1
+ module EvmClient
2
+
3
+ class Initializer
4
+ attr_accessor :contracts, :file, :client
5
+
6
+ def initialize(file, client = EvmClient::Singleton.instance)
7
+ @client = client
8
+ sol_output = Solidity.new.compile(file)
9
+ contracts = sol_output.keys
10
+
11
+ @contracts = []
12
+ contracts.each do |contract|
13
+ abi = JSON.parse(sol_output[contract]["abi"] )
14
+ name = contract
15
+ code = sol_output[contract]["bin"]
16
+ @contracts << Contract.new(name, code, abi, @client)
17
+ end
18
+ end
19
+
20
+ def build_all
21
+ @contracts.each do |contract|
22
+ contract.build
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,57 @@
1
+ require 'socket'
2
+ module EvmClient
3
+ class IpcClient < Client
4
+ attr_accessor :ipcpath
5
+
6
+ IPC_PATHS = [
7
+ "#{ENV['HOME']}/.parity/jsonrpc.ipc",
8
+ "#{ENV['HOME']}/.openethereum/jsonrpc.ipc",
9
+ "#{ENV['HOME']}/Library/Ethereum/geth.ipc",
10
+ "#{ENV['HOME']}/Library/Ethereum/testnet/geth.ipc",
11
+ "#{ENV['HOME']}/Library/Application\ Support/io.parity.ethereum/jsonrpc.ipc",
12
+ "#{ENV['HOME']}/Library/Application\ Support/io.openethereum.ethereum/jsonrpc.ipc",
13
+ "#{ENV['HOME']}/.local/share/parity/jsonrpc.ipc",
14
+ "#{ENV['HOME']}/.local/share/io.parity.ethereum/jsonrpc.ipc",
15
+ "#{ENV['HOME']}/AppData/Roaming/Parity/Ethereum/jsonrpc.ipc",
16
+ "#{ENV['HOME']}/.local/share/openethereum/jsonrpc.ipc",
17
+ "#{ENV['HOME']}/.local/share/io.openethereum.ethereum/jsonrpc.ipc",
18
+ "#{ENV['HOME']}/AppData/Roaming/openethereum/Ethereum/jsonrpc.ipc",
19
+ "#{ENV['HOME']}/.ethereum/geth.ipc",
20
+ "#{ENV['HOME']}/.ethereum/testnet/geth.ipc"
21
+ ]
22
+
23
+ def initialize(ipcpath = nil, log = true)
24
+ super(log)
25
+ ipcpath ||= IpcClient.default_path
26
+ @ipcpath = ipcpath
27
+ end
28
+
29
+ def self.default_path(paths = IPC_PATHS)
30
+ path = paths.find { |path| File.exist?(path) }
31
+ path || raise("Ipc file not found. Please pass in the file path explicitly to IpcClient initializer")
32
+ end
33
+
34
+ def send_single(payload)
35
+ socket = UNIXSocket.new(@ipcpath)
36
+ socket.puts(payload)
37
+ read = socket.recvmsg(nil)[0]
38
+ socket.close
39
+ return read
40
+ end
41
+
42
+ # Note: Guarantees the results are in the same order as defined in batch call.
43
+ # client.batch do
44
+ # client.eth_block_number
45
+ # client.eth_mining
46
+ # end
47
+ # => [{"jsonrpc"=>"2.0", "id"=>1, "result"=>"0x26"}, {"jsonrpc"=>"2.0", "id"=>2, "result"=>false}]
48
+ def send_batch(batch)
49
+ result = send_single(batch.to_json)
50
+ result = JSON.parse(result)
51
+
52
+ # Make sure the order is the same as it was when batching calls
53
+ # See 6 Batch here http://www.jsonrpc.org/specification
54
+ return result.sort_by! { |c| c['id'] }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,28 @@
1
+ module EvmClient
2
+
3
+ class ProjectInitializer
4
+
5
+ attr_accessor :contract_names, :combined_output, :contracts, :libraries
6
+
7
+ def initialize(location, optimize = false)
8
+ ENV['ETHEREUM_SOLIDITY_BINARY'] ||= "/usr/local/bin/solc"
9
+ solidity = ENV['ETHEREUM_SOLIDITY_BINARY']
10
+ contract_dir = location
11
+ if optimize
12
+ opt_flag = "--optimize"
13
+ else
14
+ opt_flag = ""
15
+ end
16
+ compile_command = "#{solidity} #{opt_flag} --combined-json abi,bin #{contract_dir}"
17
+ raw_data = `#{compile_command}`
18
+ data = JSON.parse(raw_data)
19
+ @contract_names = data["contracts"].keys
20
+ @libraries = {}
21
+ @contracts = @contract_names.collect do |contract_name|
22
+ ContractInitializer.new(contract_name, data["contracts"][contract_name], self)
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,12 @@
1
+ module EvmClient
2
+
3
+ class Railtie < Rails::Railtie
4
+ rake_tasks do
5
+ load('tasks/ethereum_test.rake')
6
+ load('tasks/ethereum_node.rake')
7
+ load('tasks/ethereum_contract.rake')
8
+ load('tasks/ethereum_transaction.rake')
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,39 @@
1
+ class EvmClient::Singleton
2
+
3
+ class << self
4
+
5
+ attr_accessor :client, :ipcpath, :host, :log, :instance, :default_account
6
+
7
+ def instance
8
+ @instance ||= configure_instance(create_instance)
9
+ end
10
+
11
+ def setup
12
+ yield(self)
13
+ end
14
+
15
+ def reset
16
+ @instance = nil
17
+ @client = nil
18
+ @host = nil
19
+ @log = nil
20
+ @ipcpath = nil
21
+ @default_account = nil
22
+ end
23
+
24
+ private
25
+ def create_instance
26
+ return EvmClient::IpcClient.new(@ipcpath) if @client == :ipc
27
+ return EvmClient::HttpClient.new(@host) if @client == :http
28
+ EvmClient::IpcClient.new
29
+ end
30
+
31
+ def configure_instance(instance)
32
+ instance.tap do |i|
33
+ i.default_account = @default_account if @default_account.present?
34
+ end
35
+ end
36
+ end
37
+
38
+
39
+ end