Class: Eth::Contract

Inherits:
Object
  • Object
show all
Defined in:
lib/eth/contract.rb

Overview

Provides classes to access smart contracts

Defined Under Namespace

Classes: Error, Event, Function, FunctionInput, FunctionOutput, Initializer

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, bin, abi) ⇒ Contract

Constructor of the Eth::Contract class.

Note, do not use this directly. Use from_abi, from_bin, or from_file!

Parameters:

  • name (String)

    contract name.

  • bin (String)

    contract bin string.

  • abi (String)

    contract abi string.



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/eth/contract.rb', line 38

def initialize(name, bin, abi)

  # The contract name will be the class name and needs title casing.
  _name = name.dup
  _name[0] = name[0].upcase

  @name = _name
  @bin = bin
  @abi = abi
  @constructor_inputs, @functions, @events, @errors = parse_abi(abi)
end

Instance Attribute Details

#abiObject

Returns the value of attribute abi.



27
28
29
# File 'lib/eth/contract.rb', line 27

def abi
  @abi
end

#addressObject

Returns the value of attribute address.



24
25
26
# File 'lib/eth/contract.rb', line 24

def address
  @address
end

#binObject

Returns the value of attribute bin.



27
28
29
# File 'lib/eth/contract.rb', line 27

def bin
  @bin
end

#class_objectObject

Returns the value of attribute class_object.



27
28
29
# File 'lib/eth/contract.rb', line 27

def class_object
  @class_object
end

#constructor_inputsObject

Returns the value of attribute constructor_inputs.



28
29
30
# File 'lib/eth/contract.rb', line 28

def constructor_inputs
  @constructor_inputs
end

#errorsObject

Returns the value of attribute errors.



28
29
30
# File 'lib/eth/contract.rb', line 28

def errors
  @errors
end

#eventsObject

Returns the value of attribute events.



28
29
30
# File 'lib/eth/contract.rb', line 28

def events
  @events
end

#functionsObject

Returns the value of attribute functions.



28
29
30
# File 'lib/eth/contract.rb', line 28

def functions
  @functions
end

#gas_limitObject

Returns the value of attribute gas_limit.



26
27
28
# File 'lib/eth/contract.rb', line 26

def gas_limit
  @gas_limit
end

#gas_priceObject

Returns the value of attribute gas_price.



26
27
28
# File 'lib/eth/contract.rb', line 26

def gas_price
  @gas_price
end

#keyObject

Returns the value of attribute key.



25
26
27
# File 'lib/eth/contract.rb', line 25

def key
  @key
end

#max_fee_per_gasObject

Returns the value of attribute max_fee_per_gas.



26
27
28
# File 'lib/eth/contract.rb', line 26

def max_fee_per_gas
  @max_fee_per_gas
end

#max_priority_fee_per_gasObject

Returns the value of attribute max_priority_fee_per_gas.



26
27
28
# File 'lib/eth/contract.rb', line 26

def max_priority_fee_per_gas
  @max_priority_fee_per_gas
end

#nameObject

Returns the value of attribute name.



27
28
29
# File 'lib/eth/contract.rb', line 27

def name
  @name
end

#nonceObject

Returns the value of attribute nonce.



26
27
28
# File 'lib/eth/contract.rb', line 26

def nonce
  @nonce
end

Class Method Details

.from_abi(abi:, address:, name:) ⇒ Eth::Contract::Object

Creates a contract wrapper from ABI and address.

Parameters:

  • abi (String)

    contract abi string.

  • address (String)

    contract address.

  • name (String)

    name of contract.

Returns:

  • (Eth::Contract::Object)

    Returns the class of the smart contract.

Raises:

  • (JSON::ParserError)

    if the json format is wrong.

  • (ArgumentError)

    if ABI, address, or name is missing.



71
72
73
74
75
76
77
78
# File 'lib/eth/contract.rb', line 71

def self.from_abi(abi:, address:, name:)
  abi = abi.is_a?(Array) ? abi : JSON.parse(abi)
  contract = Eth::Contract.new(name, nil, abi)
  contract.build
  contract = contract.class_object.new
  contract.address = address
  contract
end

.from_bin(bin:, abi:, name:) ⇒ Eth::Contract::Object

Creates a contract wrapper from binary and ABI.

Parameters:

  • bin (String)

    contract bin string.

  • abi (String)

    contract abi string.

  • name (String)

    name of contract.

Returns:

  • (Eth::Contract::Object)

    Returns the class of the smart contract.

Raises:

  • (JSON::ParserError)

    if the json format is wrong.

  • (ArgumentError)

    if ABI, binary, or name is missing.



88
89
90
91
92
93
# File 'lib/eth/contract.rb', line 88

def self.from_bin(bin:, abi:, name:)
  abi = abi.is_a?(Array) ? abi : JSON.parse(abi)
  contract = Eth::Contract.new(name, bin, abi)
  contract.build
  contract.class_object.new
end

.from_file(file:, contract_index: 0) ⇒ Eth::Contract::Object

Creates a contract wrapper from a Solidity file.

Parameters:

  • file (String)

    solidity file path.

  • contract_index (Number) (defaults to: 0)

    specify contract.

Returns:

  • (Eth::Contract::Object)

    Returns the class of the smart contract.

Raises:

  • (ArgumentError)

    if the file path is empty or no contracts were compiled.



56
57
58
59
60
61
# File 'lib/eth/contract.rb', line 56

def self.from_file(file:, contract_index: 0)
  raise ArgumentError, "Cannot find the contract at #{file.to_s}!" if !File.exist?(file.to_s)
  contracts = Eth::Contract::Initializer.new(file).build_all
  raise ArgumentError, "No contracts compiled." if contracts.empty?
  contracts[contract_index].class_object.new
end

Instance Method Details

#buildObject

Create meta classes for smart contracts.



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/eth/contract.rb', line 156

def build
  class_name = @name
  parent = self
  class_methods = Class.new do
    extend Forwardable
    def_delegators :parent, :key, :key=
    def_delegators :parent, :name, :abi, :bin
    def_delegators :parent, :gas_limit, :gas_price, :gas_limit=, :gas_price=, :nonce, :nonce=
    def_delegators :parent, :max_fee_per_gas, :max_fee_per_gas=, :max_priority_fee_per_gas, :max_priority_fee_per_gas=
    def_delegators :parent, :events, :errors
    def_delegators :parent, :address, :address=
    def_delegator :parent, :functions
    def_delegator :parent, :function
    def_delegator :parent, :error
    def_delegator :parent, :decode_error
    def_delegator :parent, :constructor_inputs
    define_method :parent do
      parent
    end
  end
  Eth::Contract.send(:remove_const, class_name) if Eth::Contract.const_defined?(class_name, false)
  Eth::Contract.const_set(class_name, class_methods)
  @class_object = class_methods
end

#decode_error(rpc_error) ⇒ String

Decodes a custom error returned by an RPC error using the contract ABI.

Parameters:

  • rpc_error (RpcError)

    the RPC error containing revert data.

Returns:

  • (String)

    a human readable error message.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/eth/contract.rb', line 137

def decode_error(rpc_error)
  data = rpc_error.data
  return rpc_error.message if data.nil? || errors.nil?

  signature = data[0, 10]
  if (err = errors.find { |e| e.signature == signature })
    values = err.decode(data)
    args = values&.map { |v| v.is_a?(String) ? v : v.inspect }&.join(",")
    args ||= ""
    "execution reverted: #{err.name}(#{args})"
  elsif signature == "0x08c379a0"
    reason = Abi.decode(["string"], "0x" + data[10..])&.first
    "execution reverted: #{reason}"
  else
    rpc_error.message
  end
end

#error(name, args: nil) ⇒ Eth::Contract::Error

Finds an error by name.

Parameters:

  • name (String)

    error name.

  • args (Integer, nil) (defaults to: nil)

    number of arguments of an error.

Returns:

Raises:

  • (ArgumentError)

    if error not found.



127
128
129
130
131
# File 'lib/eth/contract.rb', line 127

def error(name, args: nil)
  errors.find do |e|
    e.name == name && (args.nil? || args == e.inputs.size)
  end || raise(ArgumentError, "this error does not exist!")
end

#function(name, args: nil) ⇒ Eth::Contract::Function

Finds a function by name.

Parameters:

  • name (String)

    function name.

  • args (Integer, nil) (defaults to: nil)

    number of arguments of a function.

Returns:

Raises:

  • (ArgumentError)

    if function not found.



115
116
117
118
119
# File 'lib/eth/contract.rb', line 115

def function(name, args: nil)
  functions.find do |f|
    f.name == name && (args.nil? || args == f.inputs.size)
  end || raise(ArgumentError, "this function does not exist!")
end