class Ethereum::ABI::ContractTranslator
Public Class Methods
new(contract_interface)
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 8 def initialize(contract_interface) if contract_interface.instance_of?(String) contract_interface = JSON.parse contract_interface end @contract = { constructor_data: nil, function_data: {}, event_data: {} } contract_interface.each do |desc| encode_types = desc['inputs'].map {|e| e['type'] } signature = desc['inputs'].map {|e| [e['type'], e['name']] } # type can be omitted, defaulting to function type = desc['type'] || 'function' 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 } 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 } else raise ValueError, "Unknown interface type: #{type}" end end end
Public Instance Methods
constructor_data()
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 95 def constructor_data @contract[:constructor_data] end
decode(name, data)
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 90 def decode(name, data) desc = function_data[name] ABI.decode_abi desc[:decode_types], data end
encode(name, args)
click to toggle source
Return the encoded function call.
@param name [String] One of the existing functions described in the
contract interface.
@param args [Array] The function arguments that will be encoded
and used in the contract execution in the vm.
@return [String] 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.
# File lib/ethereum/abi/contract_translator.rb, line 71 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)
click to toggle source
Return the encoded constructor call.
# File lib/ethereum/abi/contract_translator.rb, line 84 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)
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 111 def event(name, encode_types) event_data[event_id(name, encode_types)] end
event_data()
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 103 def event_data @contract[:event_data] end
event_id(name, encode_types)
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 151 def event_id(name, encode_types) Utils.big_endian_to_int Utils.keccak256(get_sig(name, encode_types)) end
function(name)
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 107 def function(name) function_data[name] end
function_data()
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 99 def function_data @contract[:function_data] end
listen(log, noprint: false)
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 115 def listen(log, noprint: false) return if log.topics.size == 0 || !event_data.has_key?(log.topics[0]) data = event_data[log.topics[0]] types = data[:types] name = data[:name] names = data[:names] indexed = data[:indexed] indexed_types = types.zip(indexed).select {|(t, i)| i.true? }.map(&:first) unindexed_types = types.zip(indexed).select {|(t, i)| i.false? }.map(&:first) deserialized_args = ABI.decode_abi unindexed_types, log.data o = {} c1, c2 = 0, 0 names.each_with_index do |n, i| if indexed[i].true? topic_bytes = Utils.zpad_int log.topics[c1+1] o[n] = ABI.decode_primitive_type ABI::Type.parse(indexed_types[c1]), topic_bytes c1 += 1 else o[n] = deserialized_args[c2] c2 += 1 end end o['_event_type'] = name p o unless noprint o end
method_id(name, encode_types)
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 147 def method_id(name, encode_types) Utils.big_endian_to_int Utils.keccak256(get_sig(name, encode_types))[0,4] end
Private Instance Methods
basename(n)
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 176 def basename(n) i = n.index '(' i ? n[0,i] : n end
canonical_name(x)
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 165 def canonical_name(x) case x when /\A(uint|int)(\[.*\])?\z/ "#{$1}256#{$2}" when /\A(real|ureal|fixed|ufixed)(\[.*\])?\z/ "#{$1}128x128#{$2}" else x end end
get_sig(name, encode_types)
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 161 def get_sig(name, encode_types) "#{name}(#{encode_types.map {|x| canonical_name(x) }.join(',')})" end
logger()
click to toggle source
# File lib/ethereum/abi/contract_translator.rb, line 157 def logger @logger ||= Logger.new 'eth.abi.contract_translator' end