diff --git a/brightIDfaucet/celery.py b/brightIDfaucet/celery.py index 19ce2d2e..58d44e78 100644 --- a/brightIDfaucet/celery.py +++ b/brightIDfaucet/celery.py @@ -48,7 +48,11 @@ }, "draw-prizetap-raffles": { "task": "prizetap.tasks.draw_the_expired_raffles", - "schedule": 60 + "schedule": 300 + }, + "set-raffle-winner": { + "task": "prizetap.tasks.set_the_winner_of_raffles", + "schedule": 300 } } diff --git a/prizetap/migrations/0027_raffle_status.py b/prizetap/migrations/0027_raffle_status.py new file mode 100644 index 00000000..eb39a822 --- /dev/null +++ b/prizetap/migrations/0027_raffle_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.4 on 2023-09-18 08:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('prizetap', '0026_alter_raffle_nft_id'), + ] + + operations = [ + migrations.AddField( + model_name='raffle', + name='status', + field=models.CharField(choices=[('OPEN', 'Open'), ('REJECTED', 'Rejected'), ('HELD', 'Held'), ('WS', 'Winner is set')], default='OPEN', max_length=10), + ), + ] diff --git a/prizetap/models.py b/prizetap/models.py index c7742dbb..a6813072 100644 --- a/prizetap/models.py +++ b/prizetap/models.py @@ -1,6 +1,7 @@ from django.db import models from faucet.models import Chain from django.utils import timezone +from django.utils.translation import gettext_lazy as _ from authentication.models import NetworkTypes, UserProfile from core.models import BigNumField, UserConstraint from .constraints import * @@ -14,6 +15,12 @@ class Constraint(UserConstraint): class Raffle(models.Model): + class Status(models.TextChoices): + OPEN = "OPEN", _("Open") + REJECTED = "REJECTED", _("Rejected") + HELD = "HELD", _("Held") + WINNER_SET = "WS", _("Winner is set") + class Meta: models.UniqueConstraint( fields=["chain", "contract", "raffleId"], name="unique_raffle" @@ -50,6 +57,9 @@ class Meta: max_number_of_entries = models.IntegerField() max_multiplier = models.IntegerField(default=1) + status = models.CharField( + max_length=10, choices=Status.choices, default=Status.OPEN + ) is_active = models.BooleanField(default=True) @property diff --git a/prizetap/tasks.py b/prizetap/tasks.py index 475875ed..99f51cde 100644 --- a/prizetap/tasks.py +++ b/prizetap/tasks.py @@ -19,6 +19,7 @@ def draw_the_expired_raffles(self): Raffle.objects .filter(deadline__lt=timezone.now()) .filter(is_active=True) + .filter(status=Raffle.Status.OPEN) ) if raffles_queryset.count() > 0: for raffle in raffles_queryset: @@ -26,5 +27,40 @@ def draw_the_expired_raffles(self): print(f"Drawing the raffle {raffle.name}") raffle_client = PrizetapContractClient(raffle) if raffle_client.draw_raffle(): + raffle.status = Raffle.Status.HELD raffle.is_active = False raffle.save() + + +@shared_task(bind=True) +def set_the_winner_of_raffles(self): + + id = f"{self.name}-LOCK" + + with memcache_lock(id, self.app.oid) as acquired: + if not acquired: + print(f"Could not acquire process lock at {self.name}") + return + + raffles_queryset = ( + Raffle.objects + .filter(deadline__lt=timezone.now()) + .filter(is_active=False) + .filter(status=Raffle.Status.HELD) + ) + if raffles_queryset.count() > 0: + for raffle in raffles_queryset: + print(f"Setting the winner of raffle {raffle.name}") + raffle_client = PrizetapContractClient(raffle) + winner_address = raffle_client.get_raffle_winner() + if winner_address: + try: + winner_entry = raffle.entries.filter( + user_profile__wallets__address__iexact=winner_address).get() + winner_entry.is_winner = True + winner_entry.save() + raffle.status = Raffle.Status.WINNER_SET + raffle.save() + except Exception as e: + print(e) + pass diff --git a/prizetap/tests.py b/prizetap/tests.py index 5fb899e3..259e2516 100644 --- a/prizetap/tests.py +++ b/prizetap/tests.py @@ -7,13 +7,14 @@ from faucet.models import Chain, WalletAccount from .models import * from .constraints import * +from .utils import PrizetapContractClient test_wallet_key = "f57fecd11c6034fd2665d622e866f05f9b07f35f253ebd5563e3d7e76ae66809" -test_rpc_url_private = "http://ganache:7545" +test_rpc_url_private = "https://rpc.ankr.com/eth_sepolia" fund_manager = "0x5802f1035AbB8B191bc12Ce4668E3815e8B7Efa0" -erc20_contract_address = "0xB67ec856346b22e4BDA2ab2B53d70D61a2014358" -erc721_contract_address = "0xF927f491a99C653b39354c4A827b6368E5F714d6" +erc20_contract_address = "0x5363502325735d7b27162b2b3482c107fD4c5B3C" +erc721_contract_address = "0x334ab41d0F93d1d61178a21CD7A71387e5c75688" # # Create your tests here. @@ -359,7 +360,7 @@ def test_duplicate_claiming_prize_tx_failure(self): self.assertEqual(response.status_code, 403) -class UitlsTestCase(RaffleTestCase): +class UtilsTestCase(RaffleTestCase): def setUp(self): super().setUp() self.mainnet_chain = Chain.objects.create( @@ -377,3 +378,9 @@ def setUp(self): def test_unitappass_contraint(self): constraint = NotHaveUnitapPass(self.user_profile) self.assertTrue(constraint.is_observed()) + + def test_set_winner(self): + self.raffle.raffleId = 2 + client = PrizetapContractClient(self.raffle) + winner = client.get_raffle_winner() + self.assertEqual(winner, "0x59351584417882EE549eE3B9BF398485ddB5B7E9") diff --git a/prizetap/urls.py b/prizetap/urls.py index e648cb6b..df239010 100644 --- a/prizetap/urls.py +++ b/prizetap/urls.py @@ -26,5 +26,10 @@ "set-claiming-prize-tx//", SetClaimingPrizeTxView.as_view(), name="set-claiming-prize-tx", + ), + path( + "get-raffle-constraints//", + GetRaffleConstraints.as_view(), + name="get-raffle-constraints", ) ] diff --git a/prizetap/utils.py b/prizetap/utils.py index 0c3227fb..ba5b40e1 100644 --- a/prizetap/utils.py +++ b/prizetap/utils.py @@ -19,6 +19,11 @@ def draw_raffle(self): func = self.contract.functions.heldRaffle(self.raffle.raffleId) return self.contract_txn(func) + def get_raffle_winner(self): + func = self.contract.functions.raffles(self.raffle.raffleId) + raffle = self.contract_call(func) + return raffle[8] + class UnitapPassClient(Web3Utils): def __init__(self, chain: Chain) -> None: diff --git a/prizetap/views.py b/prizetap/views.py index a9496393..5047200c 100644 --- a/prizetap/views.py +++ b/prizetap/views.py @@ -1,15 +1,21 @@ +import json from django.shortcuts import get_object_or_404 from rest_framework.response import Response from rest_framework.generics import ListAPIView,CreateAPIView from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView from .models import Raffle, RaffleEntry -from .serializers import RaffleSerializer, RaffleEntrySerializer +from .serializers import ( + RaffleSerializer, + RaffleEntrySerializer, + ConstraintSerializer +) from .validators import ( RaffleEnrollmentValidator, SetRaffleEntryTxValidator, SetClaimingPrizeTxValidator ) +from .constraints import * class RaffleListView(ListAPIView): @@ -122,4 +128,40 @@ def get(self, request, pk): "entry": RaffleEntrySerializer(raffle_entry).data }, status=200, + ) + +class GetRaffleConstraints(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request, raffle_pk): + user_profile = request.user.profile + raffle = get_object_or_404(Raffle, pk=raffle_pk) + try: + param_values = json.loads(raffle.constraint_params) + except: + param_values = {} + + response_constraints = [] + + for c in raffle.constraints.all(): + constraint: ConstraintVerification = eval(c.name)(user_profile) + constraint.response = c.response + try: + constraint.set_param_values(param_values[c.name]) + except KeyError: + pass + is_verified = False + if constraint.is_observed(raffle.constraint_params): + is_verified = True + response_constraints.append({ + **ConstraintSerializer(c).data, + "is_verified": is_verified + }) + + return Response( + { + "success": True, + "constraints": response_constraints + }, + status=200 ) \ No newline at end of file