def deposit(account: str, request: Request) -> Response: """ POST /transactions/deposit/interactive Creates an `incomplete` deposit Transaction object in the database and returns the URL entry-point for the interactive flow. """ asset_code = request.POST.get("asset_code") stellar_account = request.POST.get("account") lang = request.POST.get("lang") if lang: err_resp = validate_language(lang) if err_resp: return err_resp activate_lang_for_request(lang) # Verify that the request is valid. if not all([asset_code, stellar_account]): return render_error_response( _("`asset_code` and `account` are required parameters") ) # Verify that the asset code exists in our database, with deposit enabled. asset = Asset.objects.filter(code=asset_code).first() if not asset: return render_error_response(_("unknown asset: %s") % asset_code) elif not asset.deposit_enabled: return render_error_response(_("invalid operation for asset %s") % asset_code) try: Keypair.from_public_key(stellar_account) except Ed25519PublicKeyInvalidError: return render_error_response(_("invalid 'account'")) # Construct interactive deposit pop-up URL. transaction_id = create_transaction_id() Transaction.objects.create( id=transaction_id, stellar_account=account, asset=asset, kind=Transaction.KIND.deposit, status=Transaction.STATUS.incomplete, to_address=account, ) logger.info(f"Created deposit transaction {transaction_id}") url = interactive_url( request, str(transaction_id), stellar_account, asset_code, settings.OPERATION_DEPOSIT, ) return Response( {"type": "interactive_customer_info_needed", "url": url, "id": transaction_id}, status=status.HTTP_200_OK, )
def withdraw(account: str, request: Request) -> Response: """ POST /transactions/withdraw/interactive Creates an `incomplete` withdraw Transaction object in the database and returns the URL entry-point for the interactive flow. """ lang = request.POST.get("lang") asset_code = request.POST.get("asset_code") if lang: err_resp = validate_language(lang) if err_resp: return err_resp activate_lang_for_request(lang) if not asset_code: return render_error_response(_("'asset_code' is required")) # Verify that the asset code exists in our database, with withdraw enabled. asset = Asset.objects.filter(code=asset_code).first() if not asset or not asset.withdrawal_enabled: return render_error_response( _("invalid operation for asset %s") % asset_code) elif asset.code not in settings.ASSETS: return render_error_response( _("unsupported asset type: %s") % asset_code) distribution_address = settings.ASSETS[ asset.code]["DISTRIBUTION_ACCOUNT_ADDRESS"] # We use the transaction ID as a memo on the Stellar transaction for the # payment in the withdrawal. This lets us identify that as uniquely # corresponding to this `Transaction` in the database. But a UUID4 is a 32 # character hex string, while the Stellar HashMemo requires a 64 character # hex-encoded (32 byte) string. So, we zero-pad the ID to create an # appropriately sized string for the `HashMemo`. transaction_id = create_transaction_id() transaction_id_hex = transaction_id.hex withdraw_memo = "0" * (64 - len(transaction_id_hex)) + transaction_id_hex Transaction.objects.create( id=transaction_id, stellar_account=account, asset=asset, kind=Transaction.KIND.withdrawal, status=Transaction.STATUS.incomplete, withdraw_anchor_account=distribution_address, withdraw_memo=withdraw_memo, withdraw_memo_type=Transaction.MEMO_TYPES.hash, ) logger.info(f"Created withdrawal transaction {transaction_id}") url = interactive_url(request, str(transaction_id), account, asset_code, settings.OPERATION_WITHDRAWAL) return Response({ "type": "interactive_customer_info_needed", "url": url, "id": transaction_id })