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 """ # Verify that the asset code exists in our database: asset_code = request.GET.get("asset_code") 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 the requested operation is valid: operation = request.GET.get("operation") if operation not in (OPERATION_DEPOSIT, OPERATION_WITHDRAWAL): return render_error_response( f"'operation' should be either '{OPERATION_DEPOSIT}' or '{OPERATION_WITHDRAWAL}'" ) # Verify that amount is provided, and that it is parseable into a float: amount_str = request.GET.get("amount") try: amount = Decimal(amount_str) except (DecimalException, TypeError): return render_error_response("invalid 'amount'") # Validate that the operation, and the specified type (if provided) # are applicable to the given asset: op_type = request.GET.get("type", "") if not _op_type_is_valid(asset_code, operation, op_type): return render_error_response( f"the specified operation is not available for '{asset_code}'") return Response({"fee": calc_fee(asset, operation, amount)})
def interactive_withdraw(request: Request) -> Response: """ """ transaction_id = request.GET.get("transaction_id") asset_code = request.GET.get("asset_code") asset = Asset.objects.filter(code=asset_code).first() if not transaction_id: return render_error_response("no 'transaction_id' provided", content_type="text/html") elif not (asset_code and asset): return render_error_response("invalid 'asset_code'", content_type="text/html") try: transaction = Transaction.objects.get(id=transaction_id, asset=asset) except (Transaction.DoesNotExist, ValidationError): return render_error_response( "Transaction with ID and asset_code not found", content_type="text/html", status_code=status.HTTP_404_NOT_FOUND, ) if request.method == "GET": form_class = rwi.form_for_transaction(transaction) return Response({"form": form_class()}, template_name="withdraw/form.html") # request.method == "POST" form = rwi.form_for_transaction(transaction)(request.POST) is_transaction_form = issubclass(form.__class__, TransactionForm) if is_transaction_form: form.asset = asset if form.is_valid(): if is_transaction_form: transaction.amount_in = form.cleaned_data["amount"] transaction.amount_fee = calc_fee(asset, settings.OPERATION_WITHDRAWAL, transaction.amount_in) transaction.save() # Perform any defined post-validation logic defined by Polaris users rwi.after_form_validation(form, transaction) # Check to see if there is another form to render form_class = rwi.form_for_transaction(transaction) if form_class: return Response({"form": form_class()}, template_name="withdraw/form.html") else: # Last form has been submitted invalidate_session(request) transaction.status = Transaction.STATUS.pending_user_transfer_start transaction.save() url, args = reverse("more_info"), urlencode({"id": transaction_id}) return redirect(f"{url}?{args}") else: return Response({"form": form}, template_name="withdraw/form.html")
def post_interactive_deposit(request: Request) -> Response: """ """ transaction, asset, error_resp = interactive_args_validation(request) if error_resp: return error_resp form = rdi.form_for_transaction(transaction)(request.POST) is_transaction_form = issubclass(form.__class__, TransactionForm) if is_transaction_form: form.asset = asset if form.is_valid(): if is_transaction_form: transaction.amount_in = form.cleaned_data["amount"] transaction.amount_fee = calc_fee(asset, settings.OPERATION_DEPOSIT, transaction.amount_in) transaction.save() # Perform any defined post-validation logic defined by Polaris users rdi.after_form_validation(form, transaction) # Check to see if there is another form to render form_class = rdi.form_for_transaction(transaction) if form_class: args = {"transaction_id": transaction.id, "asset_code": asset.code} url = reverse("get_interactive_deposit") return redirect(f"{url}?{urlencode(args)}") else: # Last form has been submitted invalidate_session(request) transaction.status = Transaction.STATUS.pending_user_transfer_start transaction.save() url, args = reverse("more_info"), urlencode({"id": transaction.id}) return redirect(f"{url}?{args}") else: return Response({"form": form}, template_name="deposit/form.html")
def interactive_withdraw(request): """ `GET /withdraw/interactive_withdraw` opens a form used to input information about the withdrawal. This creates a corresponding transaction in our database. """ transaction_id = request.GET.get("transaction_id") if not transaction_id: return render_error_response("no 'transaction_id' provided", content_type="text/html") asset_code = request.GET.get("asset_code") if not asset_code or not Asset.objects.filter(code=asset_code).exists(): return render_error_response("invalid 'asset_code'", content_type="text/html") # GET: The server needs to display the form for the user to input withdrawal information. if request.method == "GET": form = WithdrawForm() # POST: The user submitted a form with the withdrawal info. else: if Transaction.objects.filter(id=transaction_id).exists(): return render_error_response( "transaction with matching 'transaction_id' already exists", content_type="text/html") form = WithdrawForm(request.POST) asset = Asset.objects.get(code=asset_code) form.asset = asset # If the form is valid, we create a transaction pending user action # and render the success page. if form.is_valid(): amount_in = form.cleaned_data["amount"] amount_fee = calc_fee(asset, settings.OPERATION_WITHDRAWAL, amount_in) # 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 = uuid.UUID(transaction_id).hex withdraw_memo = "0" * ( 64 - len(transaction_id_hex)) + transaction_id_hex transaction = Transaction( id=transaction_id, stellar_account=settings.STELLAR_DISTRIBUTION_ACCOUNT_ADDRESS, asset=asset, kind=Transaction.KIND.withdrawal, status=Transaction.STATUS.pending_user_transfer_start, amount_in=amount_in, amount_fee=amount_fee, withdraw_anchor_account=settings. STELLAR_DISTRIBUTION_ACCOUNT_ADDRESS, withdraw_memo=withdraw_memo, withdraw_memo_type=Transaction.MEMO_TYPES.hash, ) transaction.save() serializer = TransactionSerializer( transaction, context={"more_info_url": _construct_more_info_url(request)}, ) tx_json = json.dumps({"transaction": serializer.data}) return Response( { "tx_json": tx_json, "transaction": transaction, "asset_code": asset_code, }, template_name="transaction/more_info.html") return Response({"form": form}, template_name="withdraw/form.html")
def interactive_deposit(request): """ `GET /deposit/interactive_deposit` opens a form used to input information about the deposit. This creates a corresponding transaction in our database, pending processing by the external agent. """ # Validate query parameters: account, asset_code, transaction_id. account = request.GET.get("account") if not account: return render_error_response("no 'account' provided", content_type="text/html") asset_code = request.GET.get("asset_code") if not asset_code or not Asset.objects.filter(code=asset_code).exists(): return render_error_response("invalid 'asset_code'", content_type="text/html") transaction_id = request.GET.get("transaction_id") if not transaction_id: return render_error_response("no 'transaction_id' provided", content_type="text/html") # GET: The server needs to display the form for the user to input the deposit information. if request.method == "GET": form = DepositForm() # POST: The user submitted a form with the amount to deposit. else: if Transaction.objects.filter(id=transaction_id).exists(): return render_error_response( "transaction with matching 'transaction_id' already exists", content_type="text/html") form = DepositForm(request.POST) asset = Asset.objects.get(code=asset_code) form.asset = asset # If the form is valid, we create a transaction pending external action # and render the success page. if form.is_valid(): amount_in = form.cleaned_data["amount"] amount_fee = calc_fee(asset, settings.OPERATION_DEPOSIT, amount_in) transaction = Transaction( id=transaction_id, stellar_account=account, asset=asset, kind=Transaction.KIND.deposit, status=Transaction.STATUS.pending_user_transfer_start, amount_in=amount_in, amount_fee=amount_fee, to_address=account, ) transaction.save() serializer = TransactionSerializer( transaction, context={"more_info_url": _construct_more_info_url(request)}, ) tx_json = json.dumps({"transaction": serializer.data}) return Response( { "tx_json": tx_json, "transaction": transaction, "asset_code": transaction.asset.code, }, template_name="transaction/more_info.html", ) return Response({"form": form}, template_name="deposit/form.html")