Exemplo n.º 1
0
    def post(account: str, request: Request, **kwargs):
        if kwargs:
            return render_error_response(
                _("POST requests should not specify subresources in the URI"))
        elif not registered_sep31_receiver_integration.valid_sending_anchor(
                account):
            return render_error_response("invalid sending account",
                                         status_code=403)

        try:
            params = validate_post_request(request)
        except ValueError as e:
            return render_error_response(str(e))

        # validate fields separately since error responses need different format
        missing_fields = validate_post_fields(params.get("fields"),
                                              params.get("asset"),
                                              params.get("lang"))
        if missing_fields:
            return Response(
                {
                    "error": "transaction_info_needed",
                    "fields": missing_fields
                },
                status=400,
            )

        transaction_id = create_transaction_id()
        # create memo
        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)
        # create transaction object without saving to the DB
        transaction = Transaction(
            id=transaction_id,
            protocol=Transaction.PROTOCOL.sep31,
            kind=Transaction.KIND.send,
            status=Transaction.STATUS.pending_sender,
            stellar_account=account,
            asset=params["asset"],
            amount_in=params["amount"],
            memo=transaction_memo,
            memo_type=Transaction.MEMO_TYPES.hash,
            receiving_anchor_account=params["asset"].distribution_account,
        )

        error_data = registered_sep31_receiver_integration.process_post_request(
            params, transaction)
        try:
            response_data = process_post_response(error_data, transaction)
        except ValueError as e:
            logger.error(str(e))
            return render_error_response(_("unable to process the request"),
                                         status_code=500)
        else:
            transaction.save()

        return Response(response_data,
                        status=400 if "error" in response_data else 200)
Exemplo n.º 2
0
def withdraw(
    account: str,
    client_domain: Optional[str],
    request: Request,
) -> Response:
    args = parse_request_args(request)
    if "error" in args:
        return args["error"]
    args["account"] = account

    transaction_id = create_transaction_id()
    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)
    transaction = Transaction(
        id=transaction_id,
        stellar_account=account,
        asset=args["asset"],
        kind=Transaction.KIND.withdrawal,
        status=Transaction.STATUS.pending_user_transfer_start,
        receiving_anchor_account=args["asset"].distribution_account,
        memo=transaction_memo,
        memo_type=Transaction.MEMO_TYPES.hash,
        protocol=Transaction.PROTOCOL.sep6,
        more_info_url=request.build_absolute_uri(
            f"{SEP6_MORE_INFO_PATH}?id={transaction_id}"),
        on_change_callback=args["on_change_callback"],
        client_domain=client_domain,
    )

    # All request arguments are validated in parse_request_args()
    # except 'type', 'dest', and 'dest_extra'. Since Polaris doesn't know
    # which argument was invalid, the anchor is responsible for raising
    # an exception with a translated message.
    try:
        integration_response = rwi.process_sep6_request(args, transaction)
    except ValueError as e:
        return render_error_response(str(e))
    try:
        response, status_code = validate_response(args, integration_response,
                                                  transaction)
    except ValueError as e:
        logger.error(str(e))
        return render_error_response(_("unable to process the request"),
                                     status_code=500)

    if status_code == 200:
        response["memo"] = transaction.memo
        response["memo_type"] = transaction.memo_type
        logger.info(f"Created withdraw transaction {transaction.id}")
        transaction.save()
    elif Transaction.objects.filter(id=transaction.id).exists():
        logger.error(
            "Do not save transaction objects for invalid SEP-6 requests")
        return render_error_response(_("unable to process the request"),
                                     status_code=500)

    return Response(response, status=status_code)
Exemplo n.º 3
0
def deposit(account: str, client_domain: Optional[str], request: Request,) -> Response:
    args = parse_request_args(request)
    if "error" in args:
        return args["error"]
    args["account"] = account

    transaction_id = create_transaction_id()
    transaction = Transaction(
        id=transaction_id,
        stellar_account=account,
        asset=args["asset"],
        kind=Transaction.KIND.deposit,
        status=Transaction.STATUS.pending_user_transfer_start,
        memo=args["memo"],
        memo_type=args["memo_type"] or Transaction.MEMO_TYPES.text,
        to_address=account,
        protocol=Transaction.PROTOCOL.sep6,
        more_info_url=request.build_absolute_uri(
            f"{SEP6_MORE_INFO_PATH}?id={transaction_id}"
        ),
        claimable_balance_supported=args["claimable_balance_supported"],
        on_change_callback=args["on_change_callback"],
        client_domain=client_domain,
    )

    try:
        integration_response = rdi.process_sep6_request(args, transaction)
    except ValueError as e:
        return render_error_response(str(e))
    except APIException as e:
        return render_error_response(str(e), status_code=e.status_code)

    try:
        response, status_code = validate_response(
            args, integration_response, transaction
        )
    except (ValueError, KeyError) as e:
        logger.error(str(e))
        return render_error_response(
            _("unable to process the request"), status_code=500
        )

    if status_code == 200:
        logger.info(f"Created deposit transaction {transaction.id}")
        transaction.save()
    elif Transaction.objects.filter(id=transaction.id).exists():
        logger.error("Do not save transaction objects for invalid SEP-6 requests")
        return render_error_response(
            _("unable to process the request"), status_code=500
        )

    return Response(response, status=status_code)
Exemplo n.º 4
0
def deposit(account: str, request: Request) -> Response:
    args = parse_request_args(request)
    if "error" in args:
        return args["error"]
    args["account"] = account

    transaction_id = create_transaction_id()
    transaction = Transaction(
        id=transaction_id,
        stellar_account=account,
        asset=args["asset"],
        kind=Transaction.KIND.deposit,
        status=Transaction.STATUS.pending_user_transfer_start,
        memo=args["memo"],
        memo_type=args["memo_type"] or Transaction.MEMO_TYPES.text,
        to_address=account,
        protocol=Transaction.PROTOCOL.sep6,
    )

    try:
        integration_response = rdi.process_sep6_request(args, transaction)
    except ValueError as e:
        return render_error_response(str(e))
    except APIException as e:
        return render_error_response(str(e), status_code=e.status_code)

    try:
        response, status_code = validate_response(
            args, integration_response, transaction
        )
    except (ValueError, KeyError):
        return render_error_response(
            _("unable to process the request"), status_code=500
        )

    if status_code == 200:
        logger.info(f"Created deposit transaction {transaction.id}")
        transaction.save()
    elif Transaction.objects.filter(id=transaction.id).exists():
        logger.error("Do not save transaction objects for invalid SEP-6 requests")
        return render_error_response(
            _("unable to process the request"), status_code=500
        )

    return Response(response, status=status_code)
Exemplo n.º 5
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.º 6
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")