Class: BlockGiven::Abi::Interface

Inherits:
Object
  • Object
show all
Defined in:
lib/block_given/abi/interface.rb

Overview

Parsed ABI: functions (with overload resolution), events and custom errors.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(definitions) ⇒ Interface

Returns a new instance of Interface.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/block_given/abi/interface.rb', line 30

def initialize(definitions)
  @raw = definitions.map { |d| d.transform_keys(&:to_s) }
  @functions = []
  @events = []
  @errors = []
  @constructor = nil
  @raw.each do |definition|
    case definition["type"]
    when "function", nil then @functions << Function.new(definition)
    when "event" then @events << Event.new(definition)
    when "error" then @errors << CustomError.new(definition)
    when "constructor" then @constructor = definition
    end
  end
  @functions_by_name = @functions.group_by(&:ruby_name)
end

Instance Attribute Details

#constructorObject (readonly)

Returns the value of attribute constructor.



9
10
11
# File 'lib/block_given/abi/interface.rb', line 9

def constructor
  @constructor
end

#errorsObject (readonly)

Returns the value of attribute errors.



9
10
11
# File 'lib/block_given/abi/interface.rb', line 9

def errors
  @errors
end

#eventsObject (readonly)

Returns the value of attribute events.



9
10
11
# File 'lib/block_given/abi/interface.rb', line 9

def events
  @events
end

#functionsObject (readonly)

Returns the value of attribute functions.



9
10
11
# File 'lib/block_given/abi/interface.rb', line 9

def functions
  @functions
end

#rawObject (readonly)

Returns the value of attribute raw.



9
10
11
# File 'lib/block_given/abi/interface.rb', line 9

def raw
  @raw
end

Class Method Details

.load_definitions(source) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
# File 'lib/block_given/abi/interface.rb', line 18

def self.load_definitions(source)
  case source
  when Array then source
  when Hash then source["abi"] || source[:abi] || raise(AbiError, "Hash has no 'abi' key")
  when Pathname, File then load_definitions(JSON.parse(File.read(source)))
  when String then load_definitions(JSON.parse(source))
  else raise AbiError, "cannot parse ABI from #{source.class}"
  end
rescue JSON::ParserError => e
  raise AbiError, "invalid ABI JSON: #{e.message}"
end

.parse(source) ⇒ Object

Accepts an ABI Array, a Hardhat/Foundry artifact Hash (with "abi"), a JSON String or a Pathname.



12
13
14
15
16
# File 'lib/block_given/abi/interface.rb', line 12

def self.parse(source)
  return source if source.is_a?(Interface)

  new(load_definitions(source))
end

Instance Method Details

#error_by_selector(selector) ⇒ Object



100
101
102
# File 'lib/block_given/abi/interface.rb', line 100

def error_by_selector(selector)
  @errors.find { |e| e.selector == selector.to_s.downcase }
end

#event(name) ⇒ Object



89
90
91
92
93
94
# File 'lib/block_given/abi/interface.rb', line 89

def event(name)
  name = name.to_s
  key = Utils.snake_case(name).to_sym
  @events.find { |e| e.ruby_name == key || e.signature == name } ||
    raise(EventNotFoundError, "no event #{name.inspect} in ABI (known: #{@events.map(&:name).join(', ')})")
end

#event_by_topic(topic) ⇒ Object



96
97
98
# File 'lib/block_given/abi/interface.rb', line 96

def event_by_topic(topic)
  @events.find { |e| e.topic == topic.to_s.downcase }
end

#function(name, args: [], kwargs: {}) ⇒ Object

Finds a function by name (snake_case or camelCase) or by full signature ("transfer(address,uint256)"). Overloads are disambiguated by argument count or keyword names.

Raises:



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/block_given/abi/interface.rb', line 52

def function(name, args: [], kwargs: {})
  name = name.to_s
  return function_by_signature(name) if name.include?("(")

  candidates = @functions_by_name[Utils.snake_case(name).to_sym]
  if candidates.nil?
    raise FunctionNotFoundError,
          "no function #{name.inspect} in ABI (known: #{function_names.join(', ')})"
  end
  return candidates.first if candidates.size == 1

  matching =
    if kwargs.empty?
      candidates.select { |f| f.inputs.size == args.size }
    else
      keys = kwargs.keys.map { |k| Utils.snake_case(k).to_sym }.sort
      candidates.select { |f| f.input_names.sort == keys }
    end
  return matching.first if matching.size == 1

  if matching.size > 1
    raise AmbiguousFunctionError,
          "#{name} is overloaded, use the full signature: #{candidates.map(&:signature).join(' | ')}"
  end
  raise FunctionNotFoundError,
        "no overload of #{name} matches the given arguments (#{candidates.map(&:signature).join(' | ')})"
end

#function_by_selector(selector) ⇒ Object



85
86
87
# File 'lib/block_given/abi/interface.rb', line 85

def function_by_selector(selector)
  @functions.find { |f| f.selector == selector.downcase }
end

#function_by_signature(signature) ⇒ Object



80
81
82
83
# File 'lib/block_given/abi/interface.rb', line 80

def function_by_signature(signature)
  @functions.find { |f| f.signature == signature.delete(" ") } ||
    raise(FunctionNotFoundError, "no function with signature #{signature.inspect}")
end

#function_namesObject



47
# File 'lib/block_given/abi/interface.rb', line 47

def function_names = @functions_by_name.keys

#inspectObject



104
105
106
# File 'lib/block_given/abi/interface.rb', line 104

def inspect
  "#<BlockGiven::Abi::Interface functions=#{@functions.size} events=#{@events.size} errors=#{@errors.size}>"
end