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,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