def get_transfer_account_for_token(self, token): return find_transfer_accounts_with_matching_token(self, token)
def post(self): post_data = request.get_json() uuid = post_data.get('uuid') created = post_data.get('created') transfer_use = post_data.get('transfer_use') try: use_ids = transfer_use.split(',') # passed as '3,4' etc. except AttributeError: use_ids = transfer_use transfer_mode = post_data.get('transfer_mode') transfer_amount = round(Decimal(post_data.get('transfer_amount', 0)), 6) transfer_random_key = post_data.get('transfer_random_key') pin = post_data.get('pin') nfc_serial_number = post_data.get('nfc_id') user_id = post_data.get('user_id') public_identifier = post_data.get('public_identifier') transfer_account_id = post_data.get('transfer_account_id') qr_data = post_data.get('qr_data') if qr_data is not None: qr_data = str(qr_data).strip(" ").strip("\t") my_transfer_account_id = post_data.get("my_transfer_account_id") is_sending = post_data.get('is_sending', False) transfer_card = None my_transfer_account = None authorised = False if transfer_account_id: counterparty_transfer_account = TransferAccount.query.get(transfer_account_id) else: counterparty_transfer_account = None if uuid: existing_transfer = CreditTransfer.query.filter_by(uuid=uuid).first() if existing_transfer: # We return a 201 here so that the client removes the uuid from the cache response_object = { 'message': 'Transfer already in cache', 'data': { 'credit_transfer': me_credit_transfer_schema.dump(existing_transfer).data, } } return make_response(jsonify(response_object)), 201 if qr_data: split_qr_data = qr_data.split('-') transfer_amount = int(split_qr_data[0]) transfer_account_id = int(split_qr_data[1]) user_id = int(split_qr_data[2]) qr_hash = split_qr_data[3] counterparty_user = User.query.get(user_id) if not counterparty_user: response_object = { 'message': 'No such user for ID {}'.format(user_id), 'feedback': True, } return make_response(jsonify(response_object)), 404 counterparty_transfer_account = TransferAccount.query.get(transfer_account_id) if not counterparty_transfer_account: response_object = { 'message': 'No such Transfer Account for ID {}'.format(transfer_account_id), 'feedback': True, } return make_response(jsonify(response_object)), 404 if counterparty_transfer_account not in counterparty_user.transfer_accounts: if not counterparty_transfer_account: response_object = { 'message': 'User {} not authorised for Transfer Account {}.' .format(user_id, transfer_account_id), 'feedback': True, } return make_response(jsonify(response_object)), 401 my_transfer_account = find_transfer_accounts_with_matching_token( g.user, counterparty_transfer_account.token ) user_secret = counterparty_user.secret if not check_for_any_valid_hash(transfer_amount, transfer_account_id, user_secret, qr_hash): response_object = { 'message': 'Invalid QR Code', 'feedback': True, } return make_response(jsonify(response_object)), 401 authorised = True elif nfc_serial_number: # We treat NFC serials differently because they're automatically authorised under the current version transfer_card = TransferCard.query.filter_by(nfc_serial_number=nfc_serial_number).first() if transfer_card: counterparty_user = transfer_card.user counterparty_transfer_account = transfer_card.transfer_account if not transfer_card or not counterparty_user or not counterparty_transfer_account: response_object = { 'message': 'Card not found', 'feedback': True } return make_response(jsonify(response_object)), 404 authorised = True else: try: counterparty_user, _ = find_user_with_transfer_account_from_identifiers( user_id, public_identifier, transfer_account_id) except (NoTransferAccountError, UserNotFoundError) as e: if not Web3.isAddress(public_identifier.strip('ethereum:')) or not is_sending: response_object = { 'message': str(e), 'feedback': True } return make_response(jsonify(response_object)), 400 my_transfer_account = TransferAccount.query.get(my_transfer_account_id) if not my_transfer_account: response_object = { 'message': 'Transfer Account not found for my_transfer_account_id {}'.format( my_transfer_account_id) } return make_response(jsonify(response_object)), 400 #We're sending directly to a blockchain address return handle_transfer_to_blockchain_address(transfer_amount, my_transfer_account, public_identifier.strip('ethereum:'), transfer_use, transfer_mode=TransferModeEnum.EXTERNAL, uuid=uuid) if not counterparty_user: response_object = { 'message': 'User not found', 'feedback': True } return make_response(jsonify(response_object)), 400 authorised = counterparty_user.verify_password(str(pin)) if is_sending: authorised = True if not authorised: response_object = { 'message': 'Not Authorised', 'feedback': True, } return make_response(jsonify(response_object)), 401 if not my_transfer_account: if not my_transfer_account_id: response_object = { 'message': 'You must provide your Transfer Account ID', } return make_response(jsonify(response_object)), 400 my_transfer_account = TransferAccount.query.get(my_transfer_account_id) if not my_transfer_account: response_object = { 'message': 'Transfer Account not found for my_transfer_account_id {}'.format(my_transfer_account_id) } return make_response(jsonify(response_object)), 400 if my_transfer_account not in g.user.transfer_accounts: response_object = { 'message': 'Transfer account provided does not belong to user', } return make_response(jsonify(response_object)), 401 if is_sending: send_user = g.user send_transfer_account = my_transfer_account receive_user = counterparty_user receive_transfer_account = counterparty_transfer_account else: if counterparty_transfer_account is None: response_object = { 'message': 'Counterparty Transfer Account not specified' } return make_response(jsonify(response_object)), 400 send_user = counterparty_user send_transfer_account = counterparty_transfer_account receive_user = g.user receive_transfer_account = my_transfer_account if transfer_amount == 0 or transfer_amount > send_transfer_account.balance: db.session.commit() response_object = { 'message': 'Insufficient funds', 'feedback': True, } return make_response(jsonify(response_object)), 400 try: transfer = make_payment_transfer(transfer_amount=transfer_amount, send_user=send_user, send_transfer_account=send_transfer_account, receive_user=receive_user, receive_transfer_account=receive_transfer_account, transfer_use=transfer_use, transfer_mode=transfer_mode, uuid=uuid, transfer_card=transfer_card) except AccountNotApprovedError as e: db.session.commit() if e.is_sender is True: response_object = { 'message': "Sender is not approved", 'feedback': True, } return make_response(jsonify(response_object)), 400 elif e.is_sender is False: response_object = { 'message': "Recipient is not approved", 'feedback': True, } return make_response(jsonify(response_object)), 400 else: response_object = { 'message': "Account is not approved", 'feedback': True, } return make_response(jsonify(response_object)), 400 except InsufficientBalanceError as e: db.session.commit() response_object = { 'message': "Insufficient balance", 'feedback': True, } return make_response(jsonify(response_object)), 400 if created: try: transfer.created = datetime.datetime.strptime(created, "%Y-%m-%dT%H:%M:%S.%fz") except ValueError as e: pass if is_sending: push_user_transfer_confirmation(receive_user, transfer_random_key) db.session.commit() response_object = { 'message': 'Payment Successful', 'first_name': counterparty_user.first_name, 'last_name': counterparty_user.last_name, 'feedback': True, 'data': { 'credit_transfer': me_credit_transfer_schema.dump(transfer).data } } return make_response(jsonify(response_object)), 201
def exchange_from_amount(self, user, from_token, to_token, from_amount, calculated_to_amount=None, prior_task_uuids=None, transfer_mode=None, queue='high-priority'): self.user = user self.from_token = from_token self.to_token = to_token self.from_amount = from_amount self.exchange_contract = self._find_exchange_contract( from_token, to_token) self.from_transfer = server.models.credit_transfer.CreditTransfer( from_amount, from_token, sender_user=user, recipient_transfer_account= find_transfer_accounts_with_matching_token(self.exchange_contract, from_token), transfer_type=TransferTypeEnum.EXCHANGE, transfer_mode=transfer_mode) db.session.add(self.from_transfer) signing_address = self.from_transfer.sender_transfer_account.blockchain_address prior = [] # TODO: set these so they either only fire on the first use of the exchange, or entirely asyn # We need to approve all the tokens involved for spend by the exchange contract self.to_approval_uuid = bt.make_approval( signing_address=signing_address, token=to_token, spender=self.exchange_contract.blockchain_address, amount=from_amount * 100000, prior_tasks=prior) self.reserve_approval_uuid = bt.make_approval( signing_address=signing_address, token=self.exchange_contract.reserve_token, spender=self.exchange_contract.blockchain_address, amount=from_amount * 100000, prior_tasks=prior) self.from_approval_uuid = bt.make_approval( signing_address=signing_address, token=from_token, spender=self.exchange_contract.blockchain_address, amount=from_amount * 100000, prior_tasks=prior) if calculated_to_amount: to_amount = calculated_to_amount else: to_amount = bt.get_conversion_amount( exchange_contract=self.exchange_contract, from_token=from_token, to_token=to_token, from_amount=from_amount, signing_address=signing_address) self.exchange_rate = to_amount / from_amount self.blockchain_task_uuid = str(uuid4()) g.pending_transactions.append((self, queue)) self.to_transfer = server.models.credit_transfer.CreditTransfer( to_amount, to_token, sender_transfer_account=find_transfer_accounts_with_matching_token( self.exchange_contract, to_token), recipient_user=user, transfer_type=TransferTypeEnum.EXCHANGE, transfer_mode=transfer_mode, require_sufficient_balance=False) db.session.add(self.to_transfer) self.from_transfer.blockchain_task_uuid = self.blockchain_task_uuid self.to_transfer.blockchain_task_uuid = self.blockchain_task_uuid self.from_transfer.resolve_as_complete() self.to_transfer.resolve_as_complete()
def _select_transfer_account(self, token, user): if token is None: raise Exception("Token must be specified") return find_transfer_accounts_with_matching_token(user, token)
def exchange_from_amount( self, user, from_token, to_token, from_amount, calculated_to_amount=None, prior_task_uuids=None, transfer_mode=None ): self.user = user self.from_token = from_token self.to_token = to_token exchange_contract = self._find_exchange_contract(from_token, to_token) self.from_transfer = server.models.credit_transfer.CreditTransfer( from_amount, from_token, sender_user=user, recipient_transfer_account=find_transfer_accounts_with_matching_token(exchange_contract, from_token), transfer_type=TransferTypeEnum.EXCHANGE, transfer_mode=transfer_mode ) if not self.from_transfer.check_sender_has_sufficient_balance(): message = "Sender {} has insufficient balance".format(user) self.from_transfer.resolve_as_rejected(message) raise InsufficientBalanceError(message) db.session.add(self.from_transfer) signing_address = self.from_transfer.sender_transfer_account.blockchain_address prior = [] # TODO: set these so they either only fire on the first use of the exchange, or entirely asyn # We need to approve all the tokens involved for spend by the exchange contract to_approval_uuid = bt.make_approval( signing_address=signing_address, token=to_token, spender=exchange_contract.blockchain_address, amount=from_amount * 100000, prior_tasks=prior ) reserve_approval_uuid = bt.make_approval( signing_address=signing_address, token=exchange_contract.reserve_token, spender=exchange_contract.blockchain_address, amount=from_amount * 100000, prior_tasks=prior ) from_approval_uuid = bt.make_approval( signing_address=signing_address, token=from_token, spender=exchange_contract.blockchain_address, amount=from_amount*100000, prior_tasks=prior ) if calculated_to_amount: to_amount = calculated_to_amount else: to_amount = bt.get_conversion_amount(exchange_contract=exchange_contract, from_token=from_token, to_token=to_token, from_amount=from_amount, signing_address=signing_address) self.exchange_rate = to_amount/from_amount task_uuid = bt.make_liquid_token_exchange( signing_address=signing_address, exchange_contract=exchange_contract, from_token=from_token, to_token=to_token, reserve_token=exchange_contract.reserve_token, from_amount=from_amount, prior_tasks=[to_approval_uuid, reserve_approval_uuid, from_approval_uuid] + (prior_task_uuids or []) ) self.to_transfer = server.models.credit_transfer.CreditTransfer( to_amount, to_token, sender_transfer_account=find_transfer_accounts_with_matching_token(exchange_contract, to_token), recipient_user=user, transfer_type=TransferTypeEnum.EXCHANGE, transfer_mode=transfer_mode ) db.session.add(self.to_transfer) self.blockchain_task_uuid = task_uuid self.from_transfer.blockchain_task_uuid = task_uuid self.to_transfer.blockchain_task_uuid = task_uuid self.from_transfer.resolve_as_completed(existing_blockchain_txn=True) self.to_transfer.resolve_as_completed(existing_blockchain_txn=True)