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.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.travis.yml +32 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/LICENSE.txt +21 -0
- data/PREREQUISITES.md +75 -0
- data/README.md +665 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/install_parity +22 -0
- data/bin/setup +7 -0
- data/contracts/AccountingLib.sol +112 -0
- data/contracts/AuditorInterface.sol +4 -0
- data/contracts/AuditorRegistry.sol +14 -0
- data/contracts/CustodianInterface.sol +27 -0
- data/contracts/CustodianRegistry.sol +40 -0
- data/contracts/DigixConfiguration.sol +68 -0
- data/contracts/Directory.sol +67 -0
- data/contracts/DoublyLinked.sol +54 -0
- data/contracts/GenericInterface.sol +56 -0
- data/contracts/GenericRegistry.sol +15 -0
- data/contracts/Gold.sol +105 -0
- data/contracts/GoldRegistry.sol +82 -0
- data/contracts/GoldTokenLedger.sol +3 -0
- data/contracts/Interface.sol +27 -0
- data/contracts/Minter.sol +3 -0
- data/contracts/Recaster.sol +3 -0
- data/contracts/Testing.sol +59 -0
- data/contracts/VendorInterface.sol +82 -0
- data/contracts/VendorRegistry.sol +39 -0
- data/contracts/classic/Digixbot.sol +106 -0
- data/contracts/classic/DigixbotConfiguration.sol +62 -0
- data/contracts/classic/DigixbotEthereum.sol +86 -0
- data/contracts/classic/DigixbotUsers.sol +103 -0
- data/contracts/classic/Gold.sol +497 -0
- data/contracts/classic/GoldRegistry.sol +503 -0
- data/contracts/classic/GoldTokenLedger.sol +560 -0
- data/contracts/classic/GoldTokenMinter.sol +607 -0
- data/contracts/classic/ParticipantRegistry.sol +94 -0
- data/contracts/classic/QueueSample.sol +54 -0
- data/evm_client.gemspec +36 -0
- data/lib/evm_client.rb +15 -0
- data/lib/evm_client/abi.rb +32 -0
- data/lib/evm_client/client.rb +146 -0
- data/lib/evm_client/contract.rb +341 -0
- data/lib/evm_client/contract_event.rb +32 -0
- data/lib/evm_client/contract_initializer.rb +54 -0
- data/lib/evm_client/decoder.rb +99 -0
- data/lib/evm_client/deployment.rb +49 -0
- data/lib/evm_client/encoder.rb +118 -0
- data/lib/evm_client/event_log.rb +88 -0
- data/lib/evm_client/explorer_url_helper.rb +25 -0
- data/lib/evm_client/formatter.rb +146 -0
- data/lib/evm_client/function.rb +40 -0
- data/lib/evm_client/function_input.rb +14 -0
- data/lib/evm_client/function_output.rb +14 -0
- data/lib/evm_client/http_client.rb +44 -0
- data/lib/evm_client/initializer.rb +27 -0
- data/lib/evm_client/ipc_client.rb +57 -0
- data/lib/evm_client/project_initializer.rb +28 -0
- data/lib/evm_client/railtie.rb +12 -0
- data/lib/evm_client/singleton.rb +39 -0
- data/lib/evm_client/solidity.rb +40 -0
- data/lib/evm_client/transaction.rb +41 -0
- data/lib/evm_client/version.rb +3 -0
- data/lib/tasks/ethereum_contract.rake +27 -0
- data/lib/tasks/ethereum_node.rake +52 -0
- data/lib/tasks/ethereum_test.rake +32 -0
- data/lib/tasks/ethereum_transaction.rake +24 -0
- 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
|