import logging
from rest_framework import viewsets
from rest_framework.authentication import SessionAuthentication
from rest_framework.decorators import api_view
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.views import APIView
from payments.coin_handlers import get_manager
from payments.models import Deposit, Coin, CoinPair, AddressAccountMap, Conversion
from payments.serializers import DepositSerializer, CoinSerializer, CoinPairSerializer, ConversionSerializer
"""
+===================================================+
| © 2019 Privex Inc. |
| https://www.privex.io |
+===================================================+
| |
| CryptoToken Converter |
| |
| Core Developer(s): |
| |
| (+) Chris (@someguy123) [Privex] |
| |
+===================================================+
"""
# Create your views here.
from django.views.generic import TemplateView
log = logging.getLogger(__name__)
[docs]class IndexView(TemplateView):
template_name = 'base.html'
[docs]class ConvertView(TemplateView):
template_name = 'convert.html'
[docs]@api_view(['GET'])
def api_root(request, format=None):
return Response({
'coins': reverse('coin-list', request=request, format=format),
'pairs': reverse('coinpair-list', request=request, format=format),
'deposits': reverse('deposit-list', request=request, format=format),
'conversions': reverse('conversion-list', request=request, format=format),
'start_conversion': reverse('start_convert', request=request, format=format)
})
[docs]class CustomPaginator(LimitOffsetPagination):
default_limit = 100
max_limit = 1000
[docs]class CoinAPI(viewsets.ReadOnlyModelViewSet):
lookup_value_regex = '[^/]+'
queryset = Coin.objects.filter(enabled=True)
serializer_class = CoinSerializer
filterset_fields = ('symbol', 'symbol_id', 'our_account', 'coin_type', 'can_issue')
[docs]class DepositAPI(viewsets.ReadOnlyModelViewSet):
queryset = Deposit.objects.all().order_by('-created_at')
order_by = 'created'
serializer_class = DepositSerializer
filterset_fields = (
'address', 'from_account', 'to_account', 'txid', 'memo', 'conversion__to_address',
'conversion__to_memo', 'conversion__to_txid', 'status', 'coin'
)
pagination_class = CustomPaginator
[docs]class CoinPairAPI(viewsets.ReadOnlyModelViewSet):
lookup_value_regex = '[^/]+'
queryset = CoinPair.objects.filter(from_coin__enabled=True, to_coin__enabled=True)
serializer_class = CoinPairSerializer
filterset_fields = ('from_coin', 'to_coin')
[docs]class ConversionAPI(viewsets.ReadOnlyModelViewSet):
queryset = Conversion.objects.all().order_by('-created_at')
serializer_class = ConversionSerializer
filterset_fields = (
'from_coin', 'to_coin', 'from_address', 'to_address',
'deposit__from_account', 'deposit__to_account', 'deposit__memo'
)
pagination_class = CustomPaginator
[docs]def r_err(msg, status=500):
return Response(dict(error=True, message=msg), status=status)
[docs]class DRFNoCSRF(SessionAuthentication):
[docs] def enforce_csrf(self, request):
return # disable csrf check
[docs]class ConvertAPI(APIView):
"""
Required form / JSON fields:
* ``from_coin`` - The API coin symbol to convert from (send this coin)
* ``to_coin`` - The API coin symbol to convert into (we send you this coin)
* ``destination`` - The account / address to send to
Optional:
* ``dest_memo`` - For coins that support memos, you can specify a custom memo to use when sending.
Example (application/json)
.. code-block:: json
{"from_coin": "BTC", "to_coin": "BTCP", "destination": "someguy123"}
Example (application/x-www-form-urlencoded)::
from_coin=BTC&to_coin=BTCP&destination=someguy123
"""
authentication_classes = (DRFNoCSRF,)
[docs] def post(self, request: Request):
try:
return self._post(request)
except:
log.exception("An unhandled exception occurred while handling ConvertAPI.post")
return r_err('An unknown error has occurred... please contact support', 500)
def _post(self, request: Request):
d = request.data
# If they didn't specify one of these, it will simply raise an exception for us to handle below.
try:
from_coin = str(d['from_coin']).upper()
to_coin = str(d['to_coin']).upper()
destination = str(d['destination'])
dest_memo = d.get('memo', None)
except (AttributeError, KeyError):
return r_err("You must specify 'from_coin', 'to_coin', and 'destination'", 400)
# Check if the coin pair specified actually exists. If it doesn't, it'll throw a DoesNotExist.
try:
c = CoinPair.objects.get(from_coin__symbol=from_coin, to_coin__symbol=to_coin)
except CoinPair.DoesNotExist:
return r_err("There is no such coin pair {} -> {}".format(d['from_coin'], d['to_coin']), 404)
# Grab the x(BaseManager) instances for the from/to coin, so we can do some validation + generate deposit info
m = get_manager(from_coin)
m_to = get_manager(to_coin)
# To save users from sending their coins into the abyss, make sure their destination address/account is
# actually valid / exists.
if not m_to.address_valid(destination):
return r_err("The destination {} address/account '{}' is not valid".format(to_coin, destination), 400)
# Ask the Coin Handler for their from_coin how to handle deposits for that coin
dep_type, dep_addr = m.get_deposit()
# Data to return in the 'result' key of our response.
res = dict(
ex_rate=c.exchange_rate, destination=destination,
pair=str(c)
)
if dep_type == 'account':
# If the coin handler uses an account system, that means we just give them our account to deposit into,
# and generate a memo with destination coin/address details.
res['memo'] = "{} {}".format(to_coin, destination)
if dest_memo is not None:
res['memo'] = "{} {} {}".format(to_coin, destination, dest_memo)
res['account'] = dep_addr
else:
# If it's not account based, assume it's address based.
dep_data = dict(deposit_coin=c.from_coin, deposit_address=dep_addr,
destination_coin=c.to_coin, destination_address=destination,
destination_memo=dest_memo)
res['address'] = dep_addr
# Store the address so we can map it to their destination coin when they deposit to it.
AddressAccountMap(**dep_data).save()
return Response(res)