def generate_toml(request): """Generate the TOML file.""" toml_dict = { "ACCOUNTS": [ asset.distribution_account for asset in Asset.objects.exclude(distribution_seed__isnull=True) ], "VERSION": "0.1.0", "SIGNING_KEY": settings.SIGNING_KEY, "NETWORK_PASSPHRASE": settings.STELLAR_NETWORK_PASSPHRASE, } if "sep-24" in django_settings.POLARIS_ACTIVE_SEPS: toml_dict["TRANSFER_SERVER"] = os.path.join(settings.HOST_URL, "sep24") toml_dict["TRANSFER_SERVER_SEP0024"] = toml_dict["TRANSFER_SERVER"] if "sep-6" in django_settings.POLARIS_ACTIVE_SEPS: toml_dict["TRANSFER_SERVER"] = os.path.join(settings.HOST_URL, "sep6") if "sep-10" in django_settings.POLARIS_ACTIVE_SEPS: toml_dict["WEB_AUTH_ENDPOINT"] = os.path.join(settings.HOST_URL, "auth") if "sep-12" in django_settings.POLARIS_ACTIVE_SEPS: toml_dict["KYC_SERVER"] = os.path.join(settings.HOST_URL, "kyc") if "sep-31" in django_settings.POLARIS_ACTIVE_SEPS: toml_dict["DIRECT_PAYMENT_SERVER"] = os.path.join(settings.HOST_URL, "sep31") toml_dict.update(registered_toml_func()) return HttpResponse(toml.dumps(toml_dict), content_type="text/plain")
def generate_toml(request): """Generate the TOML file.""" toml_dict = { "TRANSFER_SERVER": request.build_absolute_uri("/"), "WEB_AUTH_ENDPOINT": request.build_absolute_uri("/auth"), "ACCOUNTS": [settings.STELLAR_DISTRIBUTION_ACCOUNT_ADDRESS], "VERSION": "0.1.0", } toml_dict.update(registered_toml_func()) return HttpResponse(toml.dumps(toml_dict), content_type="text/plain")
def generate_toml(_request: Request) -> Response: """Generate a TOML-formatted string""" if registered_toml_func is get_stellar_toml: # integration function is not used, check to see if a static file is defined static_toml = None if settings.LOCAL_MODE: static_toml = finders.find("polaris/local-stellar.toml") if not static_toml: static_toml = finders.find("polaris/stellar.toml") if static_toml: with open(static_toml) as f: return Response(f.read(), content_type="text/plain") # The anchor uses the registered TOML function, replaced or not toml_dict = { "ACCOUNTS": [ asset.distribution_account for asset in Asset.objects.exclude(distribution_seed__isnull=True) ], "VERSION": "0.1.0", "NETWORK_PASSPHRASE": settings.STELLAR_NETWORK_PASSPHRASE, } if "sep-24" in settings.ACTIVE_SEPS: toml_dict["TRANSFER_SERVER"] = os.path.join(settings.HOST_URL, "sep24") toml_dict["TRANSFER_SERVER_SEP0024"] = toml_dict["TRANSFER_SERVER"] if "sep-6" in settings.ACTIVE_SEPS: toml_dict["TRANSFER_SERVER"] = os.path.join(settings.HOST_URL, "sep6") if "sep-10" in settings.ACTIVE_SEPS: toml_dict["WEB_AUTH_ENDPOINT"] = os.path.join(settings.HOST_URL, "auth") toml_dict["SIGNING_KEY"] = settings.SIGNING_KEY if "sep-12" in settings.ACTIVE_SEPS: toml_dict["KYC_SERVER"] = os.path.join(settings.HOST_URL, "kyc") if "sep-31" in settings.ACTIVE_SEPS: toml_dict["DIRECT_PAYMENT_SERVER"] = os.path.join( settings.HOST_URL, "sep31") toml_dict.update(registered_toml_func()) # We could assign content to this.STELLAR_TOML_CONTENTS, but if the anchor hasn't # transitioned to the static file approach, it's possible the anchor does not want # the TOML contents to be cached. content = toml.dumps(toml_dict) return Response(content, content_type="text/plain")
def generate_toml(request: Request) -> Response: """Generate a TOML-formatted string""" # Define the module variable to reference in-memory constants # Check if we've already read the TOML contents this = sys.modules[__name__] if hasattr(this, "STELLAR_TOML_CONTENTS"): return Response(this.STELLAR_TOML_CONTENTS, content_type="text/plain") # Check for a static TOML file if the cache is empty and the TOML # integration function is not replaced. if registered_toml_func is get_stellar_toml: static_toml = finders.find("polaris/stellar.toml") if static_toml: with open(static_toml) as f: this.STELLAR_TOML_CONTENTS = f.read() return Response(this.STELLAR_TOML_CONTENTS, content_type="text/plain") # The anchor uses the registered TOML function, replaced or not toml_dict = { "ACCOUNTS": [ asset.distribution_account for asset in Asset.objects.exclude(distribution_seed__isnull=True) ], "VERSION": "0.1.0", "SIGNING_KEY": settings.SIGNING_KEY, "NETWORK_PASSPHRASE": settings.STELLAR_NETWORK_PASSPHRASE, } if "sep-24" in django_settings.POLARIS_ACTIVE_SEPS: toml_dict["TRANSFER_SERVER"] = os.path.join(settings.HOST_URL, "sep24") toml_dict["TRANSFER_SERVER_SEP0024"] = toml_dict["TRANSFER_SERVER"] if "sep-6" in django_settings.POLARIS_ACTIVE_SEPS: toml_dict["TRANSFER_SERVER"] = os.path.join(settings.HOST_URL, "sep6") if "sep-10" in django_settings.POLARIS_ACTIVE_SEPS: toml_dict["WEB_AUTH_ENDPOINT"] = os.path.join(settings.HOST_URL, "auth") if "sep-12" in django_settings.POLARIS_ACTIVE_SEPS: toml_dict["KYC_SERVER"] = os.path.join(settings.HOST_URL, "kyc") if "sep-31" in django_settings.POLARIS_ACTIVE_SEPS: toml_dict["DIRECT_PAYMENT_SERVER"] = os.path.join(settings.HOST_URL, "sep31") toml_dict.update(registered_toml_func()) # We could assign content to this.STELLAR_TOML_CONTENTS, but if the anchor hasn't # transitioned to the static file approach, it's possible the anchor does not want # the TOML contents to be cached. content = toml.dumps(toml_dict) return Response(content, content_type="text/plain")
def generate_toml(request): """Generate the TOML file.""" toml_dict = { "TRANSFER_SERVER": request.build_absolute_uri("/"), "WEB_AUTH_ENDPOINT": request.build_absolute_uri("/auth"), "ACCOUNTS": [ asset["DISTRIBUTION_ACCOUNT_ADDRESS"] for asset in settings.ASSETS.values() ], "VERSION": "0.1.0", "SIGNING_KEY": settings.SIGNING_KEY, "NETWORK_PASSPHRASE": settings.STELLAR_NETWORK_PASSPHRASE, } toml_dict.update(registered_toml_func()) return HttpResponse(toml.dumps(toml_dict), content_type="text/plain")
def get_interactive_deposit(request: Request) -> Response: """ GET /transactions/deposit/webapp This endpoint retrieves the next form to be served to the user in the interactive flow. The following steps are taken during this process: 1. URL arguments are parsed and validated. 2. interactive_url() is called to determine whether or not the anchor uses an external service for the interactive flow. If a URL is returned, this function redirects to the URL. However, the session cookie should still be included in the response so future calls to GET /transactions/deposit/interactive/complete are authenticated. 3. form_for_transaction() is called to retrieve the next form to render to the user. 4. get and post URLs are constructed with the appropriate arguments and passed to the response to be rendered to the user. """ 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"] url = rdi.interactive_url(request, transaction, asset, amount, callback) if url: # The anchor uses a standalone interactive flow return redirect(url) form = rdi.form_for_transaction(transaction, amount=amount) content = rdi.content_for_template(Template.DEPOSIT, form=form, transaction=transaction) if not (form or content): logger.error( "The anchor did not provide content, unable to serve page.") 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 content, unable to serve page."), status_code=500, content_type="text/html", ) elif content is None: content = {} if registered_scripts_func is not scripts: logger.warning( "DEPRECATED: the `scripts` Polaris integration function will be " "removed in Polaris 2.0 in favor of allowing the anchor to override " "and extend Polaris' Django templates. See the Template Extensions " "documentation for more information.") if form: template_scripts = registered_scripts_func({"form": form, **content}) else: template_scripts = registered_scripts_func(content) url_args = {"transaction_id": transaction.id, "asset_code": asset.code} if callback: url_args["callback"] = callback if amount: url_args["amount"] = amount toml_data = registered_toml_func() post_url = f"{reverse('post_interactive_deposit')}?{urlencode(url_args)}" get_url = f"{reverse('get_interactive_deposit')}?{urlencode(url_args)}" content.update( form=form, post_url=post_url, get_url=get_url, scripts=template_scripts, operation=settings.OPERATION_DEPOSIT, asset=asset, use_fee_endpoint=registered_fee_func != calculate_fee, org_logo_url=toml_data.get("DOCUMENTATION", {}).get("ORG_LOGO"), ) return Response(content, template_name="polaris/deposit.html")
def post_interactive_withdraw(request: Request) -> Response: """ POST /transactions/withdraw/webapp This endpoint processes form submissions during the withdraw interactive flow. The following steps are taken during this process: 1. URL arguments are parsed and validated. 2. form_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 form_for_transaction() returns the next form in the flow. 5. form_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/withdraw/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"] form = rwi.form_for_transaction(transaction, post_data=request.data) if not form: logger.error("Initial form_for_transaction() call returned None " f"for {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 content, unable to serve page."), status_code=500, content_type="text/html", ) if not form.is_bound: # The anchor must initialize the form with request.data logger.error( "form returned was not initialized with POST data, returning 500") return render_error_response( _("Unable to validate form submission."), status_code=500, content_type="text/html", ) elif form.is_valid(): if issubclass(form.__class__, TransactionForm): transaction.amount_in = form.cleaned_data["amount"] transaction.save() rwi.after_form_validation(form, transaction) next_form = rwi.form_for_transaction(transaction) if next_form or rwi.content_for_template( Template.WITHDRAW, form=next_form, transaction=transaction): args = {"transaction_id": transaction.id, "asset_code": asset.code} if amount: args["amount"] = amount if callback: args["callback"] = callback url = reverse("get_interactive_withdraw") 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) # Add memo now that interactive flow is complete # # 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_hex = transaction.id.hex padded_hex_memo = "0" * ( 64 - len(transaction_id_hex)) + transaction_id_hex transaction.memo = memo_hex_to_base64(padded_hex_memo) # Update status # This signals to the wallet that the transaction can be submitted 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 = (rwi.content_for_template( Template.WITHDRAW, form=form, transaction=transaction) or {}) if registered_scripts_func is not scripts: logger.warning( "DEPRECATED: the `scripts` Polaris integration function will be " "removed in Polaris 2.0 in favor of allowing the anchor to override " "and extend Polaris' Django templates. See the Template Extensions " "documentation for more information.") template_scripts = registered_scripts_func({"form": form, **content}) url_args = {"transaction_id": transaction.id, "asset_code": asset.code} if callback: url_args["callback"] = callback if amount: url_args["amount"] = amount toml_data = registered_toml_func() post_url = f"{reverse('post_interactive_withdraw')}?{urlencode(url_args)}" get_url = f"{reverse('get_interactive_withdraw')}?{urlencode(url_args)}" content.update( form=form, post_url=post_url, get_url=get_url, scripts=template_scripts, operation=settings.OPERATION_WITHDRAWAL, asset=asset, use_fee_endpoint=registered_fee_func != calculate_fee, org_logo_url=toml_data.get("DOCUMENTATION", {}).get("ORG_LOGO"), ) return Response(content, template_name="polaris/withdraw.html", status=422)