def calculate_fiat_amount(self, satoshis): """ Calculates the fiat amount that X satoshis gets you right now. """ fiat_btc = BTCTransaction.get_btc_market_price(self.currency_code) markup_fee = fiat_btc * self.get_cashout_percent_markup() / 100.00 fiat_btc = fiat_btc - markup_fee fiat_total = fiat_btc * satoshis_to_btc(satoshis) return math.floor(fiat_total * 100) / 100
def send_btc(self, satoshis_to_send, destination_btc_address, destination_email_address=None, notes=None): """ Send satoshis to a destination address or email address. CB requires a fee for txns < .001 BTC, so this method will automatically include txn fees for those. Returns a tuple of the form (some or all may be none): btc_txn, sent_btc_obj, api_call, err_str """ msg = "Can't have both a destination email and BTC address. %s | %s" % ( destination_email_address, destination_btc_address) assert not (destination_email_address and destination_btc_address), msg msg = 'Must send to a destination email OR BTC address' assert destination_email_address or destination_btc_address, msg dest_addr_to_use = None if destination_btc_address: dest_addr_to_use = destination_btc_address send_btc_dict = { 'destination_btc_address': destination_btc_address } msg = '%s is not a valid bitcoin address' % destination_btc_address assert is_valid_btc_address(destination_btc_address), msg if destination_email_address: dest_addr_to_use = destination_email_address send_btc_dict = {'destination_email': destination_email_address} msg = '%s is not a valid email address' % destination_email_address # FIXME: implement btc_to_send = satoshis_to_btc(satoshis_to_send) SEND_URL = 'https://coinbase.com/api/v1/transactions/send_money' body = 'transaction[to]=%s&transaction[amount]=%s' % (dest_addr_to_use, btc_to_send) post_params = {'to': dest_addr_to_use, 'amount': btc_to_send} if satoshis_to_send <= btc_to_satoshis( .001) and not destination_email_address: # https://coinbase.com/api/doc/1.0/transactions/send_money.html # Coinbase pays transaction fees on payments greater than or equal to 0.001 BTC. # But for smaller amounts you have to add your own # For some reason, coinbase requires 2x fees of .2 mBTC vs (.1 mBTC) body += '&transaction[user_fee]=0.0002' post_params['user_fee'] = 0.0002 if notes: # TODO: url encode this? body += '&transaction[notes]=' + notes post_params['notes'] = notes r = get_cb_request(url=SEND_URL, api_key=self.api_key, api_secret=self.api_secret, body=body) # Log the API call api_call = APICall.objects.create(api_name=APICall.COINBASE_SEND_BTC, url_hit=SEND_URL, response_code=r.status_code, post_params=post_params, api_results=r.content, merchant=self.merchant, credential=self) self.handle_status_code(r.status_code) resp_json = json.loads(r.content) if resp_json.get('error') or resp_json.get('errors'): err_str = resp_json.get('error') # combine the two if resp_json.get('errors'): if err_str: err_str += ' %s' % resp_json.get('errors') else: err_str = resp_json.get('errors') # this assumes all error messages here are safe to display to the user return None, None, api_call, err_str transaction = resp_json['transaction'] satoshis = -1 * btc_to_satoshis(transaction['amount']['amount']) txn_hash = transaction['hsh'] # Record the Send send_btc_dict.update({ 'credential': self, 'txn_hash': txn_hash, 'satoshis': satoshis, 'transaction_id': transaction['id'], 'notes': notes, }) sent_btc_obj = CBSSentBTC.objects.create(**send_btc_dict) if txn_hash: return BTCTransaction.objects.create( txn_hash=txn_hash, satoshis=satoshis, conf_num=0), sent_btc_obj, api_call, None else: # Coinbase seems finicky about transaction hashes return None, sent_btc_obj, api_call, None
def request_cashout(self, satoshis_to_sell): SELL_URL = 'https://coinbase.com/api/v1/sells' btc_to_sell = satoshis_to_btc(satoshis_to_sell) body_to_use = 'qty=%s' % btc_to_sell r = get_cb_request( url=SELL_URL, api_key=self.api_key, api_secret=self.api_secret, body=body_to_use, ) # Log the API call APICall.objects.create(api_name=APICall.COINBASE_CASHOUT_BTC, url_hit=SELL_URL, response_code=r.status_code, api_results=r.content, post_params={'qty': btc_to_sell}, merchant=self.merchant, credential=self) self.handle_status_code(r.status_code) resp_json = json.loads(r.content) success = resp_json['success'] assert success is True, '%s: %s' % (success, resp_json.get('errors')) transfer = resp_json['transfer'] status = transfer['status'] assert status.lower() in ('pending', 'created'), status btc_obj = transfer['btc'] assert btc_obj['currency'] == 'BTC', btc_obj satoshis = btc_to_satoshis(btc_obj['amount']) assert satoshis == satoshis_to_sell, btc_obj['amount'] currency_to_recieve = transfer['total']['currency'] fiat_fees_in_cents = 0 for fee_key in transfer['fees']: fee = transfer['fees'][fee_key] fiat_fees_in_cents += int(fee['cents']) msg = '%s != %s' % (fee['currency_iso'], currency_to_recieve) assert fee['currency_iso'] == currency_to_recieve, msg fiat_fees = fiat_fees_in_cents / 100.0 cbs_sell_btc = CBSSellBTC.objects.create( coinbase_code=transfer['code']) return CBSSellBTC.objects.create( credential=self, cbs_sell_btc=cbs_sell_btc, satoshis=satoshis, currency_code=currency_to_recieve, fees_in_fiat=fiat_fees, to_receive_in_fiat=float(transfer['total']['amount']), )
def calculate_exchange_rate(self): return float(self.fiat_amount) / satoshis_to_btc(self.satoshis)
def send_btc(self, satoshis_to_send, destination_btc_address): """ Returns a tuple of the form (some or all may be none): btc_txn, sent_btc_obj, api_call, err_str """ msg = '%s is not a valid bitcoin address' % destination_btc_address assert is_valid_btc_address(destination_btc_address), msg btc_to_send = satoshis_to_btc(satoshis_to_send) SEND_URL = 'https://www.bitstamp.net/api/bitcoin_withdrawal/' trading_obj = self.get_trading_obj() try: post_params = { 'amount': btc_to_send, 'address': destination_btc_address } withdrawal_info = trading_obj.bitcoin_withdrawal(**post_params) withdrawal_id = withdrawal_info['id'] msg = "%s is not an int, it's a %s" % (withdrawal_id, type(withdrawal_id)) assert type(withdrawal_id) is int, msg # Log the API call api_call = APICall.objects.create( api_name=APICall.BITSTAMP_SEND_BTC, url_hit=SEND_URL, response_code=200, post_params=post_params, api_results=str(withdrawal_info), merchant=self.merchant, credential=self) self.mark_success() except Exception as e: # Log the API Call api_call = APICall.objects.create( api_name=APICall.BITSTAMP_SEND_BTC, url_hit=SEND_URL, response_code=0, # not accurate post_params=post_params, api_results=str(e), merchant=self.merchant, credential=self) self.mark_failure() print 'Error was: %s' % e # TODO: this assumes all error messages here are safe to display to the user return None, None, api_call, str(e) sent_btc_obj = BTSSentBTC.objects.create( credential=self, satoshis=satoshis_to_send, destination_btc_address=destination_btc_address, withdrawal_id=withdrawal_id, status='0', ) # This API doesn't return a TX hash on sending bitcoin :( return None, sent_btc_obj, api_call, None