Exemplo n.º 1
0
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)})
Exemplo n.º 2
0
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")
Exemplo n.º 3
0
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")
Exemplo n.º 4
0
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")
Exemplo n.º 5
0
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")