def fee(request: Request, sep6: bool = False) -> Response: """ Definition of the /fee endpoint, in accordance with SEP-0024. See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0024.md#fee """ deposit_op = polaris_settings.OPERATION_DEPOSIT withdrawal_op = polaris_settings.OPERATION_WITHDRAWAL operation = request.GET.get("operation") op_type = request.GET.get("type") asset_code = request.GET.get("asset_code") amount_str = request.GET.get("amount") # Verify that the asset code exists in our database: protocol_filter = { "sep6_enabled": True } if sep6 else { "sep24_enabled": True } asset = Asset.objects.filter(code=asset_code, **protocol_filter).first() if not asset_code or not asset: return render_error_response("invalid 'asset_code'") # Verify that amount is provided, and that can be parsed into a decimal: try: amount = Decimal(amount_str) except (DecimalException, TypeError): return render_error_response("invalid 'amount'") error_resp = None # Verify that the requested operation is valid: if operation not in (deposit_op, withdrawal_op): error_resp = render_error_response( f"'operation' should be either '{deposit_op}' or '{withdrawal_op}'" ) # Verify asset is enabled and within the specified limits elif operation == deposit_op: error_resp = verify_valid_asset_operation(asset, amount, Transaction.KIND.deposit) elif operation == withdrawal_op: error_resp = verify_valid_asset_operation(asset, amount, Transaction.KIND.withdrawal) if error_resp: return error_resp else: return Response({ "fee": registered_fee_func({ "operation": operation, "type": op_type, "asset_code": asset_code, "amount": amount, }) })
def fee(account: str, request: Request) -> Response: """ Definition of the /fee endpoint, in accordance with SEP-0024. See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0024.md#fee """ operation = request.GET.get("operation") op_type = request.GET.get("type") asset_code = request.GET.get("asset_code") amount_str = request.GET.get("amount") # Verify that the asset code exists in our database: if not asset_code or not Asset.objects.filter(code=asset_code).exists(): return render_error_response("invalid 'asset_code'") asset = Asset.objects.get(code=asset_code) # Verify that amount is provided, and that can be parsed into a decimal: try: amount = Decimal(amount_str) except (DecimalException, TypeError): return render_error_response("invalid 'amount'") error_resp = None # Verify that the requested operation is valid: if operation not in (OPERATION_DEPOSIT, OPERATION_WITHDRAWAL): error_resp = render_error_response( f"'operation' should be either '{OPERATION_DEPOSIT}' or '{OPERATION_WITHDRAWAL}'" ) # Verify asset is enabled and within the specified limits elif operation == OPERATION_DEPOSIT: error_resp = verify_valid_asset_operation(asset, amount, Transaction.KIND.deposit) elif operation == OPERATION_WITHDRAWAL: error_resp = verify_valid_asset_operation(asset, amount, Transaction.KIND.withdrawal) if error_resp: return error_resp else: return Response({ "fee": registered_fee_func({ "operation": operation, "type": op_type, "asset_code": asset_code, "amount": amount, }) })
def post_interactive_deposit(request: Request) -> Response: """ POST /transactions/deposit/webapp This endpoint processes form submissions during the deposit interactive flow. The following steps are taken during this process: 1. URL arguments are parsed and validated. 2. content_for_transaction() is called to retrieve the form used to submit this request. This function is implemented by the anchor. 3. The form is used to validate the data submitted, and if the form is a TransactionForm, the fee for the transaction is calculated. 4. after_form_validation() is called to allow the anchor to process the data submitted. This function should change the application state such that the next call to content_for_transaction() returns the next form in the flow. 5. content_for_transaction() is called again to retrieve the next form to be served to the user. If a form is returned, the function redirects to GET /transaction/deposit/webapp. Otherwise, The user's session is invalidated, the transaction status is updated, and the function redirects to GET /more_info. """ args_or_error = interactive_args_validation(request) if "error" in args_or_error: return args_or_error["error"] transaction = args_or_error["transaction"] asset = args_or_error["asset"] callback = args_or_error["callback"] amount = args_or_error["amount"] content = rdi.content_for_transaction(transaction) if not (content and content.get("form")): logger.error("Initial content_for_transaction() call returned None in " f"POST request for transaction: {transaction.id}") if transaction.status != transaction.STATUS.incomplete: return render_error_response( _("The anchor did not provide content, is the interactive flow already complete?" ), status_code=422, content_type="text/html", ) return render_error_response( _("The anchor did not provide form content, unable to serve page." ), status_code=500, content_type="text/html", ) try: form_class, form_args = content.get("form") except TypeError: logger.exception( "content_for_transaction(): 'form' key value must be a tuple") return render_error_response( _("The anchor did not provide content, unable to serve page."), status_code=500, content_type="text/html", ) is_transaction_form = issubclass(form_class, TransactionForm) if is_transaction_form: form = form_class(asset, request.POST, **form_args) else: form = form_class(request.POST, **form_args) if form.is_valid(): if is_transaction_form: fee_params = { "operation": settings.OPERATION_DEPOSIT, "asset_code": asset.code, **form.cleaned_data, } transaction.amount_in = form.cleaned_data["amount"] transaction.amount_fee = registered_fee_func(fee_params) transaction.save() rdi.after_form_validation(form, transaction) content = rdi.content_for_transaction(transaction) if content: args = {"transaction_id": transaction.id, "asset_code": asset.code} if amount: args["amount"] = amount if callback: args["callback"] = callback url = reverse("get_interactive_deposit") return redirect(f"{url}?{urlencode(args)}") else: # Last form has been submitted logger.info( f"Finished data collection and processing for transaction {transaction.id}" ) invalidate_session(request) transaction.status = Transaction.STATUS.pending_user_transfer_start transaction.save() url = reverse("more_info") args = urlencode({"id": transaction.id, "callback": callback}) return redirect(f"{url}?{args}") else: content.update(form=form) return Response(content, template_name="deposit/form.html", status=422)