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

#constructor ⇒ Object (readonly)

Returns the value of attribute constructor.



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

def constructor
  @constructor
end

#errors ⇒ Object (readonly)

Returns the value of attribute errors.



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

def errors
  @errors
end

#events ⇒ Object (readonly)

Returns the value of attribute events.



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

def events
  @events
end

#functions ⇒ Object (readonly)

Returns the value of attribute functions.



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

def functions
  @functions
end

#raw ⇒ Object (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.



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_names ⇒ Object



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

def function_names = @functions_by_name.keys

#inspect ⇒ Object



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