class Sibit::Btc

Btc.com API.

Public Class Methods

new(log: Sibit::Log.new, http: Sibit::Http.new, dry: false) click to toggle source

Constructor.

# File lib/sibit/btc.rb, line 43
def initialize(log: Sibit::Log.new, http: Sibit::Http.new, dry: false)
  @http = http
  @log = log
  @dry = dry
end

Public Instance Methods

balance(address) click to toggle source

Gets the balance of the address, in satoshi.

# File lib/sibit/btc.rb, line 55
def balance(address)
  uri = Iri.new('https://chain.api.btc.com/v3/address').append(address).append('unspent')
  json = Sibit::Json.new(http: @http, log: @log).get(uri)
  if json['err_no'] == 1
    @log.info("The balance of #{address} is zero (not found)")
    return 0
  end
  data = json['data']
  if data.nil?
    @log.info("The balance of #{address} is probably zero (not found)")
    return 0
  end
  txns = data['list']
  if txns.nil?
    @log.info("The balance of #{address} is probably zero (not found)")
    return 0
  end
  balance = txns.map { |tx| tx['value'] }.inject(&:+) || 0
  @log.info("The balance of #{address} is #{balance}, total txns: #{txns.count}")
  balance
end
block(hash) click to toggle source

This method should fetch a Blockchain block and return as a hash.

# File lib/sibit/btc.rb, line 156
def block(hash)
  head = Sibit::Json.new(http: @http, log: @log).get(
    Iri.new('https://chain.api.btc.com/v3/block').append(hash)
  )
  data = head['data']
  raise Sibit::Error, "The block #{hash} not found" if data.nil?
  nxt = data['next_block_hash']
  nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
  {
    provider: self.class.name,
    hash: data['hash'],
    orphan: data['is_orphan'],
    next: nxt,
    previous: data['prev_block_hash'],
    txns: txns(hash)
  }
end
fees() click to toggle source

Get recommended fees, in satoshi per byte.

# File lib/sibit/btc.rb, line 105
def fees
  raise Sibit::NotSupportedError, 'Btc.com doesn\'t provide recommended fees'
end
height(hash) click to toggle source

The height of the block.

# File lib/sibit/btc.rb, line 92
def height(hash)
  json = Sibit::Json.new(http: @http, log: @log).get(
    Iri.new('https://chain.api.btc.com/v3/block').append(hash)
  )
  data = json['data']
  raise Sibit::Error, "The block #{hash} not found" if data.nil?
  h = data['height']
  raise Sibit::Error, "The block #{hash} found but the height is absent" if h.nil?
  @log.info("The height of #{hash} is #{h}")
  h
end
latest() click to toggle source

Gets the hash of the latest block.

# File lib/sibit/btc.rb, line 110
def latest
  uri = Iri.new('https://chain.api.btc.com/v3/block/latest')
  json = Sibit::Json.new(http: @http, log: @log).get(uri)
  data = json['data']
  raise Sibit::Error, 'The latest block not found' if data.nil?
  hash = data['hash']
  @log.info("The hash of the latest block is #{hash}")
  hash
end
next_of(hash) click to toggle source

Get hash of the block after this one, or NIL if it's the last one in Blockchain.

# File lib/sibit/btc.rb, line 78
def next_of(hash)
  head = Sibit::Json.new(http: @http, log: @log).get(
    Iri.new('https://chain.api.btc.com/v3/block').append(hash)
  )
  data = head['data']
  raise Sibit::Error, "The block #{hash} not found" if data.nil?
  nxt = data['next_block_hash']
  nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
  @log.info("In BTC.com the block #{hash} is the latest, there is no next block") if nxt.nil?
  @log.info("The next block of #{hash} is #{nxt}") unless nxt.nil?
  nxt
end
price(_currency = 'USD') click to toggle source

Current price of BTC in USD (float returned).

# File lib/sibit/btc.rb, line 50
def price(_currency = 'USD')
  raise Sibit::NotSupportedError, 'Btc.com API doesn\'t provide prices'
end
push(_hex) click to toggle source

Push this transaction (in hex format) to the network.

# File lib/sibit/btc.rb, line 151
def push(_hex)
  raise Sibit::NotSupportedError, 'Btc.com doesn\'t provide payment gateway'
end
utxos(sources) click to toggle source

Fetch all unspent outputs per address.

# File lib/sibit/btc.rb, line 121
def utxos(sources)
  txns = []
  sources.each do |hash|
    json = Sibit::Json.new(http: @http, log: @log).get(
      Iri.new('https://chain.api.btc.com/v3/address').append(hash).append('unspent')
    )
    data = json['data']
    raise Sibit::Error, "The address #{hash} not found" if data.nil?
    txns = data['list']
    next if txns.nil?
    txns.each do |u|
      outs = Sibit::Json.new(http: @http, log: @log).get(
        Iri.new('https://chain.api.btc.com/v3/tx').append(u['tx_hash']).add(verbose: 3)
      )['data']['outputs']
      outs.each_with_index do |o, i|
        next unless o['addresses'].include?(hash)
        txns << {
          value: o['value'],
          hash: u['tx_hash'],
          index: i,
          confirmations: u['confirmations'],
          script: [o['script_hex']].pack('H*')
        }
      end
    end
  end
  txns
end

Private Instance Methods

txns(hash) click to toggle source
# File lib/sibit/btc.rb, line 176
def txns(hash)
  page = 1
  psize = 50
  all = []
  loop do
    data = Sibit::Json.new(http: @http, log: @log).get(
      Iri.new('https://chain.api.btc.com/v3/block')
        .append(hash).append('tx').add(page: page, pagesize: psize)
    )['data']
    raise Sibit::Error, "The block #{hash} has no data at page #{page}" if data.nil?
    list = data['list']
    raise Sibit::Error, "The list is empty for block #{hash} at page #{page}" if list.nil?
    txns = list.map do |t|
      {
        hash: t['hash'],
        outputs: t['outputs'].reject { |o| o['spent_by_tx'] }.map do |o|
          {
            address: o['addresses'][0],
            value: o['value']
          }
        end
      }
    end
    all += txns
    page += 1
    break if txns.length < psize
  end
  all
end