# frozen_string_literal: true require('faraday') require('json') # @author Hernani Rodrigues Vaz module Etht # classe para tratar pedidos etherscan com chave API class Etherscan attr_reader :key, :url # @param [String] :key apikey a juntar aos pedidos HTTP url: # @param [String] :url base URL to use as a prefix for all requests # @return [Etherscan] API etherscan base def initialize(key: ENV['ETHERSCAN_API_KEY'], url: 'https://api.etherscan.io/') @key = key @url = url end # @return [] adapter for the connection - default :net_http def adapter @adapter ||= Faraday.default_adapter end # manage the default properties and the middleware stack for fulfilling an HTTP request # # @return [] connection object with an URL & adapter def conn @conn ||= Faraday.new(url: url) do |c| c.request(:url_encoded) c.adapter(adapter) end end # @return [] resultado json do GET HTTP request def get(**ars) r = conn.get('api') do |o| o.headers = { content_type: 'application/json', accept: 'application/json', user_agent: 'etherscan;ruby' } o.params = ars.merge({ apikey: key }) end raise(Etht::Erro, r) if r.status != 200 JSON(r.body)['result'] end # get ether balance for a single address # # @param [String] add address for balance # @return [] devolve saldo def address_balance(add) raise(Etht::Erro, 'address must be defined') if add.nil? get(module: 'account', action: 'balance', address: add, tag: 'latest') end # get ether balance for multiple addresses # # @param [String] ads lista de addresses (max 20) # @return [Array] devolve lista com contas & saldo def multi_address_balance(ads) raise(Etht::Erro, 'up to 20 accounts in a single batch') if ads.size > 20 get(module: 'account', action: 'balancemulti', address: ads.join(','), tag: 'latest') end # get ERC20-token account balance # # @param add (see address_balance) # @param [String] cdd token contract address # @return (see address_balance) def token_balance(add, cdd) raise(Etht::Erro, 'contract or address must be defined') if (cdd || add).nil? get(module: 'account', action: 'tokenbalance', address: add, contractaddress: cdd) end # get a list of normal transactions by address # # @param [String] add address for transactions # @param [Hash] ars opcoes trabalho # @option ars [String] :start_block starting blockNo to retrieve results # @option ars [String] :end_block ending blockNo to retrieve results # @option ars [String] :sort asc -> ascending order, desc -> descending order # @option ars [String] :page to get paginated results # @option ars [String] :offset max records to return # @return [Array] devolve ate max 10000 das ultimas transacoes def normal_tx(add, **ars) raise(Etht::Erro, 'address must be defined') if add.nil? transcations('txlist', add, nil, **ars) end # get a list of ERC20 - token transfer events # # @param add (see normal_tx) # @param [String] cdd token address (nil to get a list of all ERC20 transactions) # @param ars (see normal_tx) # @option ars (see normal_tx) # @return (see normal_tx) def token_tx(add, cdd = nil, **ars) raise(Etht::Erro, 'contract or address must be defined') if (cdd || add).nil? transcations('tokentx', add, cdd, **ars) end private # get a list of transactions # # @param [String] act accao a executar # @param add (see normal_tx) # @param cdd (see token_tx) # @param ars (see normal_tx) # @option ars (see normal_tx) # @return (see normal_tx) def transcations(act, add, cdd, **ars) get(**{ module: 'account', action: act, address: add }.merge({ contractaddress: cdd, startblock: ars[:start_block], endblock: ars[:end_block], page: ars[:page], offset: ars[:offset], sort: ars[:sort] }.reject { |_, v| v.nil? })) end end end