def fee(request): """ Definition of the /fee endpoint, in accordance with SEP-0006. See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0006.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(name=asset_code).exists(): return render_error_response("invalid 'asset_code'") asset = Asset.objects.get(name=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 = float(amount_str) except (TypeError, ValueError): 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_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") asset_code = request.GET.get("asset_code") if not asset_code or not Asset.objects.filter(name=asset_code).exists(): return render_error_response("invalid 'asset_code'") transaction_id = request.GET.get("transaction_id") if not transaction_id: return render_error_response("no 'transaction_id' provided") # 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" ) form = DepositForm(request.POST) asset = Asset.objects.get(name=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_external, 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 render(request, "deposit/success.html", context={"tx_json": tx_json}) return render(request, "deposit/form.html", {"form": form})
def interactive_deposit(request): # Validate query parameters: account, asset_code, transaction_id. account = request.GET.get("account") if not account: return render_error_response("no 'account' provided") asset_code = request.GET.get("asset_code") if not asset_code or not Asset.objects.filter(name=asset_code).exists(): return render_error_response("invalid 'asset_code'") transaction_id = request.GET.get("transaction_id") if not transaction_id: return render_error_response("no 'transaction_id' provided") # 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" ) form = DepositForm(request.POST) asset = Asset.objects.get(name=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_external, amount_in=amount_in, amount_fee=amount_fee, ) transaction.save() create_stellar_deposit.delay(transaction.id) # TODO: Use the proposed callback approach. return render(request, "deposit/success.html") return render(request, "deposit/form.html", {"form": form})
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") asset_code = request.GET.get("asset_code") if not asset_code or not Asset.objects.filter(name=asset_code).exists(): return render_error_response("invalid 'asset_code'") # 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") form = WithdrawForm(request.POST) asset = Asset.objects.get(name=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_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_ACCOUNT_ADDRESS, withdraw_memo=withdraw_memo, withdraw_memo_type=Transaction.MEMO_TYPES.hash, ) transaction.save() serializer = TransactionSerializer(transaction) tx_json = json.dumps({"transaction": serializer.data}) return render(request, "withdraw/success.html", context={"tx_json": tx_json}) return render(request, "withdraw/form.html", {"form": form})