Class: EvmClient::Contract
- Inherits:
-
Object
- Object
- EvmClient::Contract
- Defined in:
- lib/evm_client/contract.rb
Instance Attribute Summary collapse
-
#abi ⇒ Object
Returns the value of attribute abi.
-
#address ⇒ Object
Returns the value of attribute address.
-
#call_proxy ⇒ Object
Returns the value of attribute call_proxy.
-
#call_raw_proxy ⇒ Object
Returns the value of attribute call_raw_proxy.
-
#class_object ⇒ Object
Returns the value of attribute class_object.
-
#client ⇒ Object
Returns the value of attribute client.
-
#code ⇒ Object
Returns the value of attribute code.
-
#constructor_inputs ⇒ Object
Returns the value of attribute constructor_inputs.
-
#deployment ⇒ Object
Returns the value of attribute deployment.
-
#events ⇒ Object
Returns the value of attribute events.
-
#functions ⇒ Object
Returns the value of attribute functions.
-
#gas_limit ⇒ Object
Returns the value of attribute gas_limit.
-
#gas_price ⇒ Object
Returns the value of attribute gas_price.
-
#get_filter_change_proxy ⇒ Object
Returns the value of attribute get_filter_change_proxy.
-
#get_filter_logs_proxy ⇒ Object
Returns the value of attribute get_filter_logs_proxy.
-
#key ⇒ Object
Returns the value of attribute key.
-
#name ⇒ Object
Returns the value of attribute name.
-
#new_filter_proxy ⇒ Object
Returns the value of attribute new_filter_proxy.
-
#nonce ⇒ Object
Returns the value of attribute nonce.
-
#sender ⇒ Object
Returns the value of attribute sender.
-
#transact_and_wait_proxy ⇒ Object
Returns the value of attribute transact_and_wait_proxy.
-
#transact_proxy ⇒ Object
Returns the value of attribute transact_proxy.
Class Method Summary collapse
-
.create(file: nil, client: EvmClient::Singleton.instance, code: nil, abi: nil, address: nil, name: nil, contract_index: nil, truffle: nil) ⇒ EvmClient::Contract
Creates a contract wrapper.
-
.find_truffle_artifacts(name, paths = []) ⇒ Hash?
Looks up and loads a Truffle artifacts file.
-
.truffle_paths ⇒ Array<String>
Get the list of paths where to look up Truffle artifacts files.
-
.truffle_paths=(paths) ⇒ Object
Set the list of paths where to look up Truffle artifacts files.
Instance Method Summary collapse
- #build ⇒ Object
- #call(fun, *args) ⇒ Object
- #call_args(fun, args) ⇒ Object
- #call_payload(fun, args) ⇒ Object
- #call_raw(fun, *args) ⇒ Object
- #create_filter(evt, **params) ⇒ Object
- #deploy(*params) ⇒ Object
- #deploy_and_wait(*params, **args, &block) ⇒ Object
- #deploy_args(params) ⇒ Object
- #deploy_payload(params) ⇒ Object
- #estimate(*params) ⇒ Object
- #function_name(fun) ⇒ Object
- #get_filter_changes(evt, filter_id) ⇒ Object
- #get_filter_logs(evt, filter_id) ⇒ Object
-
#initialize(name, code, abi, client = EvmClient::Singleton.instance) ⇒ Contract
constructor
A new instance of Contract.
- #parse_filter_data(evt, logs) ⇒ Object
- #send_raw_transaction(payload, to = nil) ⇒ Object
- #send_transaction(tx_args) ⇒ Object
- #transact(fun, *args) ⇒ Object
- #transact_and_wait(fun, *args) ⇒ Object
Constructor Details
#initialize(name, code, abi, client = EvmClient::Singleton.instance) ⇒ Contract
Returns a new instance of Contract.
13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/evm_client/contract.rb', line 13 def initialize(name, code, abi, client = EvmClient::Singleton.instance) @name = name @code = code @abi = abi @constructor_inputs, @functions, @events = EvmClient::Abi.parse_abi(abi) @formatter = EvmClient::Formatter.new @client = client @sender = client.default_account @encoder = Encoder.new @decoder = Decoder.new @gas_limit = @client.gas_limit @gas_price = @client.gas_price end |
Instance Attribute Details
#abi ⇒ Object
Returns the value of attribute abi.
8 9 10 |
# File 'lib/evm_client/contract.rb', line 8 def abi @abi end |
#address ⇒ Object
Returns the value of attribute address.
5 6 7 |
# File 'lib/evm_client/contract.rb', line 5 def address @address end |
#call_proxy ⇒ Object
Returns the value of attribute call_proxy.
10 11 12 |
# File 'lib/evm_client/contract.rb', line 10 def call_proxy @call_proxy end |
#call_raw_proxy ⇒ Object
Returns the value of attribute call_raw_proxy.
10 11 12 |
# File 'lib/evm_client/contract.rb', line 10 def call_raw_proxy @call_raw_proxy end |
#class_object ⇒ Object
Returns the value of attribute class_object.
8 9 10 |
# File 'lib/evm_client/contract.rb', line 8 def class_object @class_object end |
#client ⇒ Object
Returns the value of attribute client.
8 9 10 |
# File 'lib/evm_client/contract.rb', line 8 def client @client end |
#code ⇒ Object
Returns the value of attribute code.
8 9 10 |
# File 'lib/evm_client/contract.rb', line 8 def code @code end |
#constructor_inputs ⇒ Object
Returns the value of attribute constructor_inputs.
9 10 11 |
# File 'lib/evm_client/contract.rb', line 9 def constructor_inputs @constructor_inputs end |
#deployment ⇒ Object
Returns the value of attribute deployment.
8 9 10 |
# File 'lib/evm_client/contract.rb', line 8 def deployment @deployment end |
#events ⇒ Object
Returns the value of attribute events.
9 10 11 |
# File 'lib/evm_client/contract.rb', line 9 def events @events end |
#functions ⇒ Object
Returns the value of attribute functions.
9 10 11 |
# File 'lib/evm_client/contract.rb', line 9 def functions @functions end |
#gas_limit ⇒ Object
Returns the value of attribute gas_limit.
7 8 9 |
# File 'lib/evm_client/contract.rb', line 7 def gas_limit @gas_limit end |
#gas_price ⇒ Object
Returns the value of attribute gas_price.
7 8 9 |
# File 'lib/evm_client/contract.rb', line 7 def gas_price @gas_price end |
#get_filter_change_proxy ⇒ Object
Returns the value of attribute get_filter_change_proxy.
11 12 13 |
# File 'lib/evm_client/contract.rb', line 11 def get_filter_change_proxy @get_filter_change_proxy end |
#get_filter_logs_proxy ⇒ Object
Returns the value of attribute get_filter_logs_proxy.
11 12 13 |
# File 'lib/evm_client/contract.rb', line 11 def get_filter_logs_proxy @get_filter_logs_proxy end |
#key ⇒ Object
Returns the value of attribute key.
6 7 8 |
# File 'lib/evm_client/contract.rb', line 6 def key @key end |
#name ⇒ Object
Returns the value of attribute name.
8 9 10 |
# File 'lib/evm_client/contract.rb', line 8 def name @name end |
#new_filter_proxy ⇒ Object
Returns the value of attribute new_filter_proxy.
11 12 13 |
# File 'lib/evm_client/contract.rb', line 11 def new_filter_proxy @new_filter_proxy end |
#nonce ⇒ Object
Returns the value of attribute nonce.
7 8 9 |
# File 'lib/evm_client/contract.rb', line 7 def nonce @nonce end |
#sender ⇒ Object
Returns the value of attribute sender.
8 9 10 |
# File 'lib/evm_client/contract.rb', line 8 def sender @sender end |
#transact_and_wait_proxy ⇒ Object
Returns the value of attribute transact_and_wait_proxy.
10 11 12 |
# File 'lib/evm_client/contract.rb', line 10 def transact_and_wait_proxy @transact_and_wait_proxy end |
#transact_proxy ⇒ Object
Returns the value of attribute transact_proxy.
10 11 12 |
# File 'lib/evm_client/contract.rb', line 10 def transact_proxy @transact_proxy end |
Class Method Details
.create(file: nil, client: EvmClient::Singleton.instance, code: nil, abi: nil, address: nil, name: nil, contract_index: nil, truffle: nil) ⇒ EvmClient::Contract
Creates a contract wrapper. This method attempts to instantiate a contract object from a Solidity source file, from a Truffle artifacts file, or from explicit API and bytecode.
-
If :file is present, the method compiles it and looks up the contract factory at :contract_index (0 if not provided). :abi and :code are ignored.
-
If :truffle is present, the method looks up the Truffle artifacts data for :name and uses those data to build a contract instance.
-
Otherwise, the method uses :name, :code, and :abi to build the contract instance.
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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/evm_client/contract.rb', line 54 def self.create(file: nil, client: EvmClient::Singleton.instance, code: nil, abi: nil, address: nil, name: nil, contract_index: nil, truffle: nil) contract = nil if file.present? contracts = EvmClient::Initializer.new(file, client).build_all raise "No contracts compiled" if contracts.empty? if contract_index contract = contracts[contract_index].class_object.new else contract = contracts.first.class_object.new end else if truffle.present? && truffle.is_a?(Hash) artifacts = find_truffle_artifacts(name, (truffle[:paths].is_a?(Array)) ? truffle[:paths] : []) if artifacts abi = artifacts['abi'] # The truffle artifacts store bytecodes with a 0x tag, which we need to remove # this may need to be 'deployedBytecode' code_key = artifacts['bytecode'].present? ? 'bytecode' : 'unlinked_binary' code = (artifacts[code_key].start_with?('0x')) ? artifacts[code_key][2, artifacts[code_key].length] : artifacts[code_key] unless address address = if client network_id = client.net_version['result'] (artifacts['networks'][network_id]) ? artifacts['networks'][network_id]['address'] : nil else nil end end else abi = nil code = nil end else abi = abi.is_a?(String) ? JSON.parse(abi) : abi.map(&:deep_stringify_keys) end contract = EvmClient::Contract.new(name, code, abi, client) contract.build contract = contract.class_object.new end contract.address = address contract end |
.find_truffle_artifacts(name, paths = []) ⇒ Hash?
Looks up and loads a Truffle artifacts file. This method iterates over the ‘truffle_path` elements, looking for an artifact file in the `build/contracts` subdirectory.
300 301 302 303 304 305 306 307 308 309 |
# File 'lib/evm_client/contract.rb', line 300 def self.find_truffle_artifacts(name, paths = []) subpath = File.join('build', 'contracts', "#{name}.json") found = paths.concat(truffle_paths).find { |p| File.file?(File.join(p, subpath)) } if (found) JSON.parse(IO.read(File.join(found, subpath))) else nil end end |
.truffle_paths ⇒ Array<String>
Get the list of paths where to look up Truffle artifacts files.
275 276 277 278 |
# File 'lib/evm_client/contract.rb', line 275 def self.truffle_paths() @truffle_paths = [] unless @truffle_paths @truffle_paths end |
.truffle_paths=(paths) ⇒ Object
Set the list of paths where to look up Truffle artifacts files.
285 286 287 |
# File 'lib/evm_client/contract.rb', line 285 def self.truffle_paths=(paths) @truffle_paths = (paths.is_a?(Array)) ? paths : [] end |
Instance Method Details
#build ⇒ Object
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/evm_client/contract.rb', line 242 def build class_name = @name.camelize parent = self create_function_proxies create_event_proxies class_methods = Class.new do extend Forwardable def_delegators :parent, :deploy_payload, :deploy_args, :call_payload, :call_args, :functions def_delegators :parent, :signed_deploy, :key, :key= def_delegators :parent, :gas_limit, :gas_price, :gas_limit=, :gas_price=, :nonce, :nonce= def_delegators :parent, :abi, :deployment, :events, :name def_delegators :parent, :estimate, :deploy, :deploy_and_wait def_delegators :parent, :address, :address=, :sender, :sender= def_delegator :parent, :call_raw_proxy, :call_raw def_delegator :parent, :call_proxy, :call def_delegator :parent, :transact_proxy, :transact def_delegator :parent, :transact_and_wait_proxy, :transact_and_wait def_delegator :parent, :new_filter_proxy, :new_filter def_delegator :parent, :get_filter_logs_proxy, :get_filter_logs def_delegator :parent, :get_filter_change_proxy, :get_filter_changes define_method :parent do parent end end EvmClient::Contract.send(:remove_const, class_name) if EvmClient::Contract.const_defined?(class_name, false) EvmClient::Contract.const_set(class_name, class_methods) @class_object = class_methods end |
#call(fun, *args) ⇒ Object
179 180 181 182 183 184 185 186 |
# File 'lib/evm_client/contract.rb', line 179 def call(fun, *args) output = call_raw(fun, *args)[:formatted] if output.length == 1 return output[0] else return output end end |
#call_args(fun, args) ⇒ Object
167 168 169 170 171 |
# File 'lib/evm_client/contract.rb', line 167 def call_args(fun, args) = {to: @address, from: @sender, data: call_payload(fun, args)} fun.constant ? : () end |
#call_payload(fun, args) ⇒ Object
163 164 165 |
# File 'lib/evm_client/contract.rb', line 163 def call_payload(fun, args) "0x" + fun.minified_signature + (@encoder.encode_arguments(fun.inputs, args)) end |
#call_raw(fun, *args) ⇒ Object
173 174 175 176 177 |
# File 'lib/evm_client/contract.rb', line 173 def call_raw(fun, *args) raw_result = @client.eth_call(call_args(fun, args))["result"] output = @decoder.decode_arguments(fun.outputs, raw_result) return {data: call_payload(fun, args), raw: raw_result, formatted: output} end |
#create_filter(evt, **params) ⇒ Object
203 204 205 206 207 208 209 210 211 |
# File 'lib/evm_client/contract.rb', line 203 def create_filter(evt, **params) params[:to_block] ||= "latest" params[:from_block] ||= "0x0" params[:address] ||= @address params[:topics] = @encoder.ensure_prefix(evt.signature) payload = {topics: [params[:topics]], fromBlock: params[:from_block], toBlock: params[:to_block], address: @encoder.ensure_prefix(params[:address])} filter_id = @client.eth_new_filter(payload) return @decoder.decode_int(filter_id["result"]) end |
#deploy(*params) ⇒ Object
137 138 139 140 141 142 143 144 145 146 |
# File 'lib/evm_client/contract.rb', line 137 def deploy(*params) if key tx = send_raw_transaction(deploy_payload(params)) else tx = send_transaction(deploy_args(params)) end tx_failed = tx.nil? || tx == "0x0000000000000000000000000000000000000000000000000000000000000000" raise IOError, "Failed to deploy, did you unlock #{sender} account? Transaction hash: #{tx}" if tx_failed @deployment = EvmClient::Deployment.new(tx, @client) end |
#deploy_and_wait(*params, **args, &block) ⇒ Object
148 149 150 151 152 153 154 155 156 |
# File 'lib/evm_client/contract.rb', line 148 def deploy_and_wait(*params, **args, &block) deploy(*params) @deployment.wait_for_deployment(**args, &block) self.events.each do |event| event.set_address(@address) event.set_client(@client) end @address = @deployment.contract_address end |
#deploy_args(params) ⇒ Object
112 113 114 |
# File 'lib/evm_client/contract.rb', line 112 def deploy_args(params) ({from: sender, data: deploy_payload(params)}) end |
#deploy_payload(params) ⇒ Object
104 105 106 107 108 109 110 |
# File 'lib/evm_client/contract.rb', line 104 def deploy_payload(params) if @constructor_inputs.present? raise ArgumentError, "Wrong number of arguments in a constructor" and return if params.length != @constructor_inputs.length end deploy_arguments = @encoder.encode_arguments(@constructor_inputs, params) "0x" + @code + deploy_arguments end |
#estimate(*params) ⇒ Object
158 159 160 161 |
# File 'lib/evm_client/contract.rb', line 158 def estimate(*params) result = @client.eth_estimate_gas(deploy_args(params)) @decoder.decode_int(result["result"]) end |
#function_name(fun) ⇒ Object
236 237 238 239 240 |
# File 'lib/evm_client/contract.rb', line 236 def function_name(fun) count = functions.select {|x| x.name == fun.name }.count name = (count == 1) ? "#{fun.name.underscore}" : "#{fun.name.underscore}__#{fun.inputs.collect {|x| x.type}.join("__")}" name.to_sym end |
#get_filter_changes(evt, filter_id) ⇒ Object
232 233 234 |
# File 'lib/evm_client/contract.rb', line 232 def get_filter_changes(evt, filter_id) parse_filter_data evt, @client.eth_get_filter_changes(filter_id) end |
#get_filter_logs(evt, filter_id) ⇒ Object
228 229 230 |
# File 'lib/evm_client/contract.rb', line 228 def get_filter_logs(evt, filter_id) parse_filter_data evt, @client.eth_get_filter_logs(filter_id) end |
#parse_filter_data(evt, logs) ⇒ Object
213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/evm_client/contract.rb', line 213 def parse_filter_data(evt, logs) formatter = EvmClient::Formatter.new collection = [] logs["result"].each do |result| inputs = evt.input_types outputs = inputs.zip(result["topics"][1..-1]) data = {blockNumber: result["blockNumber"].hex, transactionHash: result["transactionHash"], blockHash: result["blockHash"], transactionIndex: result["transactionIndex"].hex, topics: []} outputs.each do |output| data[:topics] << formatter.from_payload(output) end collection << data end return collection end |
#send_raw_transaction(payload, to = nil) ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/evm_client/contract.rb', line 120 def send_raw_transaction(payload, to = nil) Eth.configure { |c| c.chain_id = @client.net_version["result"].to_i } @nonce ||= @client.get_nonce(key.address) args = { from: key.address, value: 0, data: payload, nonce: @nonce, gas_limit: gas_limit, gas_price: gas_price } args[:to] = to if to tx = Eth::Tx.new(args) tx.sign key @client.eth_send_raw_transaction(tx.hex)["result"] end |
#send_transaction(tx_args) ⇒ Object
116 117 118 |
# File 'lib/evm_client/contract.rb', line 116 def send_transaction(tx_args) @client.eth_send_transaction(tx_args)["result"] end |
#transact(fun, *args) ⇒ Object
188 189 190 191 192 193 194 195 |
# File 'lib/evm_client/contract.rb', line 188 def transact(fun, *args) if key tx = send_raw_transaction(call_payload(fun, args), address) else tx = send_transaction(call_args(fun, args)) end return EvmClient::Transaction.new(tx, @client, call_payload(fun, args), args) end |
#transact_and_wait(fun, *args) ⇒ Object
197 198 199 200 201 |
# File 'lib/evm_client/contract.rb', line 197 def transact_and_wait(fun, *args) tx = transact(fun, *args) tx.wait_for_miner return tx end |