um-cseg/chez-betty

View on GitHub
chezbetty/btc.py

Summary

Maintainability
A
0 mins
Test Coverage

import urllib
import urllib.error
import urllib.request
import time
import json
import hashlib
import hmac
from decimal import Decimal


class BTCException(Exception):
    pass

class Bitcoin(object):

    COINBASE_API_KEY = ""
    COINBASE_API_SECRET = ""

    HOSTNAME = ''

    def __init__(self, umid=None):
        self.umid = umid
        pass

    @staticmethod
    def req(url, body=None):
        try:
            opener = urllib.request.build_opener()
            nonce = int(time.time() * 1e6)
            message = str(nonce) + url + ('' if body is None else body)
            signature = hmac.new(bytes(Bitcoin.COINBASE_API_SECRET, "utf-8"),
                                 bytes(message, "utf-8"),
                                 hashlib.sha256).hexdigest()
            opener.addheaders = [('ACCESS_KEY', Bitcoin.COINBASE_API_KEY),
                                ('ACCESS_SIGNATURE', signature),
                                ('ACCESS_NONCE', nonce)]

            # is this really how you do this in this language?
            body_b = None
            if body is not None:
                body_b = bytes(body, "utf-8")

            res_s = opener.open(urllib.request.Request(url,
                body_b, {'Content-Type': 'application/json'})).read()
            return json.loads(str(res_s, 'utf-8'))

        except urllib.error.HTTPError as e:
            raise BTCException("Could not load HTTP url %s: %s, %s" % (url, str(e), e.read()))
        except urllib.error.URLError as e:
            raise BTCException("General urllib failure: %s" % (str(e)))


    @staticmethod
    def noauth_req(url):
        try:
            opener = urllib.request.build_opener()
            res_s = opener.open(urllib.request.Request(url,
                None, {'Content-Type': 'application/json'})).read()
            return json.loads(str(res_s, 'utf-8'))
        except urllib.error.HTTPError as e:
            raise BTCException("Could not load HTTP")
        except urllib.error.URLError as e:
            raise BTCException("General urllib failure")

    @staticmethod
    def get_new_address(umid, auth_key, cb_url='{}/terminal/bitcoin/deposit'):

        cb_url = cb_url.format(Bitcoin.HOSTNAME)

        obj = Bitcoin.req("https://api.coinbase.com/v1/addresses",
                      '{"address": {"callback_url": "%s/%s/%s", "label": "%s"}}' % (cb_url, umid, auth_key, umid))

        if not(obj['success']):
            raise BTCException("Could not get address: %s" % umid)

        return obj['address']

    @staticmethod
    def get_spot_price(currency="USD"):
        obj = Bitcoin.req("https://coinbase.com/api/v1/prices/spot_rate?currency=%s" % currency)
        return Decimal(obj['amount'])

    @staticmethod
    def get_balance():
        obj = Bitcoin.req("https://coinbase.com/api/v1/account/balance")
        return Decimal(obj['amount'])

    @staticmethod
    def get_tx_by_hash(txid):
        return Bitcoin.noauth_req("https://blockchain.info/rawtx/%s" % txid)

    @staticmethod
    def get_block_height():
        return Bitcoin.noauth_req("https://blockchain.info/latestblock")["height"]

    # | separated list (e.g. "14YD3or6jXZowg8PV5VfZo9jmer8TQZHFg|1AZiKMapdu2KibenF67FHFgNbxL9sG2jt5")
    @staticmethod
    def get_tx_from_addrs(addrs):
        return Bitcoin.noauth_req("https://blockchain.info/multiaddr?active=%s" % addrs )

    @staticmethod
    def convert(amount):
        obj = Bitcoin.req("https://coinbase.com/api/v1/sells",
                          '{"qty": %s}' % (amount))

        if not(obj['success']):
            raise BTCException("failure on BTC convert (%s)" % obj['errors'])

        return Decimal(obj['transfer']['total']['amount'])

    @staticmethod
    # Returns the amount in USD that the bitcoins were exchanged for
    def convert_all():
        return Bitcoin.get_balance()*Bitcoin.get_spot_price()*Decimal(0.99)
        #raise NotImplementedError()