Skip to content

Commit

Permalink
Support multiple wallet for same chains
Browse files Browse the repository at this point in the history
  • Loading branch information
PooyaFekri committed Sep 30, 2023
1 parent 3d1d61c commit 65f1eed
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 188 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.0.4 on 2023-09-27 11:03

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('authentication', '0017_alter_userprofile_username'),
]

operations = [
migrations.AlterUniqueTogether(
name='wallet',
unique_together={('wallet_type', 'address')},
),
migrations.AddField(
model_name='wallet',
name='primary',
field=models.BooleanField(default=False),
),
]
17 changes: 17 additions & 0 deletions authentication/migrations/0019_alter_wallet_unique_together.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.0.4 on 2023-09-28 03:27

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('authentication', '0018_alter_wallet_unique_together_wallet_primary'),
]

operations = [
migrations.AlterUniqueTogether(
name='wallet',
unique_together=set(),
),
]
12 changes: 10 additions & 2 deletions authentication/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ def get_or_create(self, first_context_id):
return _profile


class WalletManager(models.Manager):
def get_primary_wallet(self):
try:
self.get(primary=True, wallet_type='EVM')
except Wallet.DoesNotExist:
return None


class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, related_name="profile")
initial_context_id = models.CharField(max_length=512, unique=True)
Expand Down Expand Up @@ -107,9 +115,9 @@ class Wallet(models.Model):
UserProfile, on_delete=models.PROTECT, related_name="wallets"
)
address = models.CharField(max_length=512, unique=True)
primary = models.BooleanField(default=False, null=False, blank=False)

class Meta:
unique_together = (("wallet_type", "user_profile"),)
objects = WalletManager()

def __str__(self):
return f"{self.wallet_type} Wallet for profile with contextId {self.user_profile.initial_context_id}"
9 changes: 9 additions & 0 deletions authentication/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,12 @@ class IsAuraVerified(BasePermission):

def has_permission(self, request, view):
return bool(request.user.profile.is_aura_verified)


class IsOwner(BasePermission):
"""
Just owner has can access
"""

def has_object_permission(self, request, view, obj):
return obj.user_profile == request.user.profile
49 changes: 24 additions & 25 deletions authentication/serializers.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from django.db import IntegrityError
from rest_framework.authtoken.models import Token
from rest_framework import serializers

from authentication.models import (
UserProfile,
Wallet,
)
from rest_framework.authtoken.models import Token
from rest_framework import serializers
from faucet.faucet_manager.claim_manager import LimitedChainClaimManager

from faucet.models import GlobalSettings


Expand All @@ -28,32 +27,31 @@ def update(self, instance, validated_data):
pass


# class SetUsernameSerializer(serializers.Serializer):
# username = UsernameRequestSerializer.username

# def save(self, user_profile):
# username = self.validated_data.get("username")

# try:
# user_profile.username = username
# user_profile.save()
# return {"message": "Username Set"}

# except IntegrityError:
# raise ValidationError(
# {"message": "This username already exists. Try another one."}
# )


class WalletSerializer(serializers.ModelSerializer):
class Meta:
model = Wallet
fields = [
"pk",
"wallet_type",
"address",
'primary'
]

def update(self, instance, validated_data):
if validated_data.get('primary') is False or instance.wallet_type != 'EVM':
raise serializers.ValidationError({'message': 'primary must be true or wallet_type must be EVM'})
user_profile = self.context["request"].user.profile
try:
wallet = Wallet.objects.get(user_profile=user_profile, primary=True)
wallet.primary = False
instance.primary = True
Wallet.objects.bulk_update([wallet, instance], ['primary'])
return instance
except Wallet.DoesNotExist:
instance.primary = True
instance.save()
return instance


class ProfileSerializer(serializers.ModelSerializer):
wallets = WalletSerializer(many=True, read_only=True)
Expand Down Expand Up @@ -81,10 +79,11 @@ def get_total_weekly_claims_remaining(self, instance):
gs = GlobalSettings.objects.first()
if gs is not None:
return (
gs.weekly_chain_claim_limit
- LimitedChainClaimManager.get_total_weekly_claims(instance)
gs.weekly_chain_claim_limit
- LimitedChainClaimManager.get_total_weekly_claims(instance)
)



class SimpleProfilerSerializer(serializers.ModelSerializer):
wallets = WalletSerializer(many=True, read_only=True)
username = serializers.SerializerMethodField()
Expand All @@ -102,4 +101,4 @@ class Meta:
def get_username(self, user_profile: UserProfile):
if not user_profile.username:
return f"User{user_profile.pk}"
return user_profile.username
return user_profile.username
85 changes: 46 additions & 39 deletions authentication/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
from django.utils import timezone
from django.urls import reverse
from django.contrib.auth.models import User
from rest_framework.response import Response
from rest_framework.test import APITestCase
from rest_framework.authtoken.models import Token
from rest_framework.status import HTTP_403_FORBIDDEN, HTTP_409_CONFLICT, HTTP_200_OK
from authentication.models import UserProfile
from rest_framework.status import HTTP_403_FORBIDDEN, HTTP_409_CONFLICT, HTTP_200_OK, HTTP_400_BAD_REQUEST, \
HTTP_404_NOT_FOUND, HTTP_201_CREATED
from authentication.models import UserProfile, Wallet
from faucet.models import ClaimReceipt

### get address as username and signed address as password and verify signature
Expand All @@ -30,7 +32,7 @@
lambda a, b: True,
)
def create_new_user(
_address="0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9",
_address="0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9",
) -> UserProfile:
# (u, created) = User.objects.get_or_create(username=_address, password="test")
p = UserProfile.objects.get_or_create(_address)
Expand All @@ -50,6 +52,11 @@ def create_verified_user() -> UserProfile:
return user


def create_new_wallet(user_profile, _address, wallet_type) -> Wallet:
wallet, is_create = Wallet.objects.get_or_create(user_profile=user_profile, address=_address,
wallet_type=wallet_type)
return wallet

class CheckUsernameTestCase(APITestCase):
def setUp(self) -> None:
self.endpoint = "AUTHENTICATION:check-username"
Expand Down Expand Up @@ -219,75 +226,75 @@ def test_become_sponsor(self):
self.assertEqual(response.status_code, HTTP_200_OK)


class TestSetWalletAddress(APITestCase):
class TestListCreateWallet(APITestCase):
def setUp(self) -> None:
self.password = "test"
self._address = "0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461G3Ef9A9"
self.endpoint = reverse("AUTHENTICATION:set-wallet-user")
self.endpoint = reverse("AUTHENTICATION:wallets-user")
self.user_profile = create_new_user()
self.client.force_authenticate(user=self.user_profile.user)

def test_invalid_arguments_provided_should_fail(self):
response = self.client.post(self.endpoint)
self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)

response = self.client.post(self.endpoint, data={"address": False})
self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)

response = self.client.post(self.endpoint, data={"wallet_type": False})
self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)

def test_create_wallet_address(self):
response = self.client.post(self.endpoint,
data={"address": self._address, "wallet_type": "EVM", "primary": True})
self.assertEqual(response.status_code, HTTP_201_CREATED)

def test_set_same_address_for_multiple_users_should_fail(self):
def test_create_same_address_twice(self):
response = self.client.post(
self.endpoint, data={"address": self._address, "wallet_type": "EVM"}
self.endpoint, data={"address": self._address, "wallet_type": "EVM", 'primary': True}
)
self.assertEqual(response.status_code, HTTP_200_OK)

self.assertEqual(response.status_code, HTTP_201_CREATED)
response = self.client.post(
self.endpoint, data={"address": self._address, "wallet_type": "Solana"}
self.endpoint, data={"address": self._address, "wallet_type": "EVM", 'primary': True}
)
self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)

def test_not_existing_wallet_then_create_and_set_address_for_that_is_ok(self):
def test_get_wallet_list(self):
response = self.client.post(
self.endpoint, data={"address": self._address, "wallet_type": "EVM"}
self.endpoint, data={"address": self._address, "wallet_type": "EVM", 'primary': True}
)
self.assertEqual(response.status_code, HTTP_201_CREATED)
response = self.client.get(self.endpoint, {'wallet_type': 'EVM'})
self.assertEqual(response.status_code, HTTP_200_OK)
self.assertGreater(len(response.data), 0)


# class TestGetWalletAddress(APITestCase):
# def setUp(self) -> None:
# self.password = "test"
# self._address = "0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9"
# self.endpoint_set = reverse('AUTHENTICATION:set-wallet-user')
# self.endpoint_get = reverse('AUTHENTICATION:get-wallet-user')
# self.user_profile = create_new_user()
# self.client.force_authenticate(user=self.user_profile.user)
#
# def test_get_existing_wallet_is_ok(self):
# response = self.client.post(self.endpoint_set, data={'address': self._address, 'wallet_type': "EVM"})
# self.assertEqual(response.status_code, HTTP_200_OK)
#
# response = self.client.post(self.endpoint_get, data={'wallet_type': "EVM"})
# self.assertEqual(response.status_code, HTTP_200_OK)
#
# def test_not_existing_wallet_should_fail_getting_profile(self):
# response = self.client.post(self.endpoint_get, data={'wallet_type': "EVM"})
# self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)


class TestGetWalletsView(APITestCase):
class TestWalletView(APITestCase):
def setUp(self) -> None:
self.password = "test"
self._address = "0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9"
self.endpoint = reverse("AUTHENTICATION:get-wallets-user")
self.user_profile = create_new_user()
wallet = create_new_wallet(self.user_profile, self._address, 'EVM')
self.endpoint = reverse("AUTHENTICATION:wallet-user", kwargs={'pk': wallet.pk})
self.client.force_authenticate(user=self.user_profile.user)

def test_request_to_this_api_is_ok(self):
response = self.client.get(self.endpoint)
self.assertEqual(response.status_code, HTTP_200_OK)

def test_change_primary_ture(self):
response: Response = self.client.patch(self.endpoint, data={'primary': True})
self.assertEqual(response.status_code, HTTP_200_OK)
self.assertEqual(response.data.get('primary'), True)

def test_access_to_another_user_wallet(self):
_address = '0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A2'
other_user = create_new_user(_address)
wallet = create_new_wallet(other_user, _address, 'EVM')
_endpoint = reverse('AUTHENTICATION:wallet-user', kwargs={'pk': wallet.pk})
response = self.client.get(_endpoint)
self.assertEqual(response.status_code, HTTP_404_NOT_FOUND)


class TestGetProfileView(APITestCase):
def setUp(self) -> None:
Expand Down
22 changes: 6 additions & 16 deletions authentication/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,14 @@
name="check-username",
),
path(
"user/set-wallet/",
SetWalletAddressView.as_view(),
name="set-wallet-user",
"user/wallets/",
WalletListCreateView.as_view(),
name="wallets-user",
),
path(
"user/get-wallet/",
GetWalletAddressView.as_view(),
name="get-wallet-user",
),
path(
"user/delete-wallet/",
DeleteWalletAddressView.as_view(),
name="delete-wallet-user",
),
path(
"user/get-wallets/",
GetWalletsView.as_view(),
name="get-wallets-user",
"user/wallets/<int:pk>/",
WalletView.as_view(),
name="wallet-user",
),
path("user/info/", GetProfileView.as_view(), name="get-profile-user"),
path("user/sponsor/", SponsorView.as_view(), name="sponsor-user"),
Expand Down
Loading

0 comments on commit 65f1eed

Please sign in to comment.