Class: Ethereum::ABI::ContractTranslator

Inherits:
Object
  • Object
show all
Defined in:
lib/ethereum/abi/contract_translator.rb

Instance Method Summary collapse

Constructor Details

#initialize(contract_interface) ⇒ ContractTranslator

Returns a new instance of ContractTranslator.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ethereum/abi/contract_translator.rb', line 9

def initialize(contract_interface)
  if contract_interface.instance_of?(String)
    contract_interface = JSON.parse contract_interface
  end

  @contract = {
    fallback_data: nil,
    constructor_data: nil,
    function_data: {},
    event_data: {}
  }

  contract_interface.each do |desc|
    type = desc['type'] || 'function'
    encode_types = []
    signature = []

    if type != 'fallback' && desc.has_key?('inputs')
      encode_types = desc['inputs'].map {|e| e['type'] }
      signature = desc['inputs'].map {|e| [e['type'], e['name']] }
    end

    case type
    when 'function'
      name = basename desc['name']
      decode_types = desc['outputs'].map {|e| e['type'] }
      @contract[:function_data][name] = {
        prefix: method_id(name, encode_types),
        encode_types: encode_types,
        decode_types: decode_types,
        is_constant: desc.fetch('constant', false),
        signature: signature,
        payable: desc.fetch('payable', false)
      }
    when 'event'
      name = basename desc['name']
      indexed = desc['inputs'].map {|e| e['indexed'] }
      names = desc['inputs'].map {|e| e['name'] }
      @contract[:event_data][event_id(name, encode_types)] = {
        types: encode_types,
        name: name,
        names: names,
        indexed: indexed,
        anonymous: desc.fetch('anonymous', false)
      }
    when 'constructor'
      raise ValueError, "Only one constructor is supported." if @contract[:constructor_data]
      @contract[:constructor_data] = {
        encode_types: encode_types,
        signature: signature
      }
    when 'fallback'
      raise ValueError, "Only one fallback function is supported." if @contract[:fallback_data]
      @contract[:fallback_data] = {
        payable: desc['payable']
      }
    else
      raise ValueError, "Unknown interface type: #{type}"
    end
  end
end

Instance Method Details

#constructor_dataObject



175
176
177
# File 'lib/ethereum/abi/contract_translator.rb', line 175

def constructor_data
  @contract[:constructor_data]
end

#decode_event(log_topics, log_data) ⇒ Object

Return a dictionary represent the log.

Notes: this function won’t work with anonymous events.

Raises:

Parameters:

  • The log's indexed arguments.

  • The encoded non-indexed arguments.



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/ethereum/abi/contract_translator.rb', line 125

def decode_event(log_topics, log_data)
  # topics[0]: keccak256(normalized_event_name)
  raise ValueError, "Unknown log type" unless log_topics.size > 0 && event_data.has_key?(log_topics[0])

  event_id = log_topics[0]
  event = event_data[event_id]

  names = event[:names]
  types = event[:types]
  indexed = event[:indexed]

  unindexed_types = types.zip(indexed).select {|(t, i)| i.false? }.map(&:first)
  unindexed_args = ABI.decode_abi unindexed_types, log_data

  result = {}
  indexed_count = 1 # skip topics[0]
  names.each_with_index do |name, i|
    v = if indexed[i].true?
          topic_bytes = Utils.zpad_int log_topics[indexed_count]
          indexed_count += 1
          ABI.decode_primitive_type ABI::Type.parse(types[i]), topic_bytes
        else
          unindexed_args.shift
        end

    result[name] = v
  end

  result['_event_type'] = event[:name]
  result
end

#decode_function_result(name, data) ⇒ Array[Object] Also known as: decode

Return the function call result decoded.

Parameters:

  • One of the existing functions described in the contract interface.

  • The encoded result from calling function 'name`.

Returns:

  • The values returned by the call to function



111
112
113
114
# File 'lib/ethereum/abi/contract_translator.rb', line 111

def decode_function_result(name, data)
  desc = function_data[name]
  ABI.decode_abi desc[:decode_types], data
end

#encode(name, args) ⇒ String

Return the encoded function call.

Raises:

Parameters:

  • One of the existing functions described in the contract interface.

  • The function arguments that will be encoded and used in the contract execution in the vm.

Returns:

  • The encoded function name and arguments so that it can be used with the evm to execute a function call, the binary string follows the Ethereum Contract ABI.



83
84
85
86
87
88
89
90
91
# File 'lib/ethereum/abi/contract_translator.rb', line 83

def encode(name, args)
  raise ValueError, "Unknown function #{name}" unless function_data.include?(name)

  desc = function_data[name]
  func_id = Utils.zpad(Utils.encode_int(desc[:prefix]), 4)
  calldata = ABI.encode_abi desc[:encode_types], args

  "#{func_id}#{calldata}"
end

#encode_constructor_arguments(args) ⇒ Object

Return the encoded constructor call.

Raises:



96
97
98
99
100
# File 'lib/ethereum/abi/contract_translator.rb', line 96

def encode_constructor_arguments(args)
  raise ValueError, "The contract interface didn't have a constructor" unless constructor_data

  ABI.encode_abi constructor_data[:encode_types], args
end

#event(name, encode_types) ⇒ Object



191
192
193
# File 'lib/ethereum/abi/contract_translator.rb', line 191

def event(name, encode_types)
  event_data[event_id(name, encode_types)]
end

#event_dataObject



183
184
185
# File 'lib/ethereum/abi/contract_translator.rb', line 183

def event_data
  @contract[:event_data]
end

#event_id(name, encode_types) ⇒ Object



199
200
201
# File 'lib/ethereum/abi/contract_translator.rb', line 199

def event_id(name, encode_types)
  Utils.big_endian_to_int Utils.keccak256(get_sig(name, encode_types))
end

#function(name) ⇒ Object



187
188
189
# File 'lib/ethereum/abi/contract_translator.rb', line 187

def function(name)
  function_data[name]
end

#function_dataObject



179
180
181
# File 'lib/ethereum/abi/contract_translator.rb', line 179

def function_data
  @contract[:function_data]
end

#listen(log, noprint: true) ⇒ Object

. Return a dictionary representation of the Log instance.

Note: this function won’t work with anonymous events.

Parameters:

  • The Log instance that needs to be parsed.

  • (defaults to: true)

    Flag to turn off printing of the decoded log instance.



166
167
168
169
170
171
172
173
# File 'lib/ethereum/abi/contract_translator.rb', line 166

def listen(log, noprint: true)
  result = decode_event log.topics, log.data
  p result if noprint
  result['_from'] = Utils.encode_hex(log.address)
  result
rescue ValueError
  nil # api compatibility
end

#method_id(name, encode_types) ⇒ Object



195
196
197
# File 'lib/ethereum/abi/contract_translator.rb', line 195

def method_id(name, encode_types)
  Utils.big_endian_to_int Utils.keccak256(get_sig(name, encode_types))[0,4]
end