Skip to content

Commit

Permalink
Solved ethereum converter and transaction validation problem
Browse files Browse the repository at this point in the history
  • Loading branch information
riadelimemmedov committed Mar 18, 2024
1 parent 508be5a commit e68adb2
Show file tree
Hide file tree
Showing 20 changed files with 306 additions and 25 deletions.
Empty file.
42 changes: 42 additions & 0 deletions backend/apps/transaction/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from django import forms
from django.contrib import admin

from .models import Transaction
from .validate_field import validate_confirmations, validate_from_user

# Register your models here.


# * TransactionModelForm
class TransactionModelForm(forms.ModelForm):
class Meta:
model = Transaction
fields = "__all__"

def clean(self):
cleaned_data = super().clean()
confirmations = cleaned_data.get("confirmations")
from_user = cleaned_data.get("from_user")
if len(cleaned_data) == 0:
return
if not validate_confirmations(confirmations):
self.add_error("confirmations", "Confirmations value 1 or 0")
if not validate_from_user(from_user):
self.add_error("from_user", "Please input valid wallet address")


# !TransactionalAdmin
class TransactionalAdmin(admin.ModelAdmin):
form = TransactionModelForm
list_display = [
"from_user",
"confirmations",
"value",
"adopted_pet_slug",
"created",
"modified",
]
list_display_links = ["from_user", "confirmations"]


admin.site.register(Transaction, TransactionalAdmin)
6 changes: 6 additions & 0 deletions backend/apps/transaction/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class TransactionConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "apps.transaction"
54 changes: 54 additions & 0 deletions backend/apps/transaction/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Generated by Django 5.0.1 on 2024-03-18 12:37

import django_extensions.db.fields
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = []

operations = [
migrations.CreateModel(
name="Transaction",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created",
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True, verbose_name="created"
),
),
(
"modified",
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, verbose_name="modified"
),
),
(
"from_user",
models.CharField(max_length=100, verbose_name="from user"),
),
("confirmations", models.IntegerField(verbose_name="confirmations")),
("value", models.CharField(max_length=100, verbose_name="value")),
(
"adopted_pet_slug",
models.CharField(max_length=100, verbose_name="adopted slug"),
),
],
options={
"verbose_name": "Transaction",
"verbose_name_plural": "Transactions",
},
),
]
Empty file.
22 changes: 22 additions & 0 deletions backend/apps/transaction/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_extensions.db.models import TimeStampedModel

from .validate_field import validate_confirmations, validate_from_user


# !Transaction
class Transaction(TimeStampedModel):
from_user = models.CharField(_("from user"), max_length=100)
confirmations = models.IntegerField(
_("confirmations"), validators=[validate_confirmations]
)
value = models.CharField(_("value"), max_length=100)
adopted_pet_slug = models.CharField(_("adopted slug"), max_length=100)

class Meta:
verbose_name = "Transaction"
verbose_name_plural = "Transactions"

def __str__(self):
return f"{self.from_user} -- {self.value} -- {self.value}"
26 changes: 26 additions & 0 deletions backend/apps/transaction/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import re

from rest_framework import serializers

from .models import Transaction
from .validate_field import validate_confirmations, validate_from_user


# !TransactionSerializer
class TransactionSerializer(serializers.ModelSerializer):
from_user = serializers.CharField(validators=[validate_from_user])
confirmations = serializers.CharField(validators=[validate_confirmations])

def validate(self, attrs):
from_user = attrs.get("from_user")
confirmations = attrs.get("confirmations")

if not validate_from_user(from_user):
raise serializers.ValidationError("Please input valid wallet address")
if not validate_confirmations(confirmations):
raise serializers.ValidationError("Confirmations value 1 or 0")
return attrs

class Meta:
model = Transaction
fields = "__all__"
3 changes: 3 additions & 0 deletions backend/apps/transaction/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
6 changes: 6 additions & 0 deletions backend/apps/transaction/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.urls import path

from .views import TransactionView

app_name = "transaction"
urlpatterns = [path("", TransactionView.as_view(), name="transactions")]
17 changes: 17 additions & 0 deletions backend/apps/transaction/validate_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import re

from rest_framework import serializers
from rest_framework.exceptions import ValidationError


# ?validate_confirmations
def validate_confirmations(value):
if int(value) > 1:
return False
return value


# ?validate_from_user
def validate_from_user(value):
pattern = re.compile("^0x[a-fA-F0-9]{40}$")
return bool(pattern.match(f"{value}"))
22 changes: 22 additions & 0 deletions backend/apps/transaction/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView

from .serializers import TransactionSerializer

# Create your views here.


# !TransactionView
class TransactionView(APIView):
serializer_class = TransactionSerializer

def get(self, request):
return Response({"message": "Transaction successfull"})

def post(self, request, format=None):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
1 change: 1 addition & 0 deletions backend/config/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"apps.pet",
"apps.upload",
"apps.order",
"apps.transaction",
]

# !Installed Apps
Expand Down
3 changes: 3 additions & 0 deletions backend/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
urlpatterns += [path("upload/", include("apps.upload.urls", namespace="upload"))]
urlpatterns += [path("pets/", include("apps.pet.urls", namespace="pet"))]
urlpatterns += [path("orders/", include("apps.order.urls", namespace="order"))]
urlpatterns += [
path("transactions/", include("apps.transaction.urls", namespace="transaction"))
]


# *Settings Debug
Expand Down
29 changes: 15 additions & 14 deletions backend/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,9 @@ info:
version: 1.0.0
description: This project purpose creating ecommerce api for business company
paths:
/orders/:
get:
operationId: orders_retrieve
description: Order
tags:
- orders
security:
- cookieAuth: []
- basicAuth: []
- {}
responses:
'200':
description: No response body
/orders/create-checkout-session:
post:
operationId: orders_create
operationId: orders_create_checkout_session_create
description: Order
tags:
- orders
Expand Down Expand Up @@ -207,6 +195,19 @@ paths:
responses:
'204':
description: No response body
/transactions/create-checkout-session:
post:
operationId: transactions_create_checkout_session_create
description: Order
tags:
- transactions
security:
- cookieAuth: []
- basicAuth: []
- {}
responses:
'200':
description: No response body
/upload/image/:
post:
operationId: upload_image_create
Expand Down
2 changes: 1 addition & 1 deletion frontend/contract-ui/contracts/PetLocal.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"abi":"[{\"type\":\"constructor\",\"payable\":false,\"inputs\":[{\"type\":\"uint256\",\"name\":\"initialPetIndex\"}]},{\"type\":\"function\",\"name\":\"addPet\",\"constant\":false,\"payable\":false,\"inputs\":[],\"outputs\":[]},{\"type\":\"function\",\"name\":\"addToCart\",\"constant\":false,\"payable\":false,\"inputs\":[{\"type\":\"uint256\",\"name\":\"_petId\"},{\"type\":\"string\",\"name\":\"_petName\"},{\"type\":\"string\",\"name\":\"_petColor\"},{\"type\":\"uint256\",\"name\":\"_petPrice\"},{\"type\":\"string\",\"name\":\"_petPhoto\"}],\"outputs\":[]},{\"type\":\"function\",\"name\":\"adoptPet\",\"constant\":false,\"payable\":false,\"inputs\":[{\"type\":\"uint256\",\"name\":\"adoptIdx\"}],\"outputs\":[]},{\"type\":\"function\",\"name\":\"allAdoptedPets\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[{\"type\":\"uint256\"}],\"outputs\":[{\"type\":\"uint256\"}]},{\"type\":\"function\",\"name\":\"getAllAdoptedPets\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"uint256[]\"}]},{\"type\":\"function\",\"name\":\"getAllAdoptedPetsByOwner\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"uint256[]\"}]},{\"type\":\"function\",\"name\":\"getCartItems\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"tuple[]\",\"components\":[{\"type\":\"uint256\",\"name\":\"id\"},{\"type\":\"string\",\"name\":\"name\"},{\"type\":\"string\",\"name\":\"color\"},{\"type\":\"uint256\",\"name\":\"price\"},{\"type\":\"string\",\"name\":\"photo\"}]}]},{\"type\":\"function\",\"name\":\"getCartLength\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"uint256\"}]},{\"type\":\"function\",\"name\":\"getOwner\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"address\"}]},{\"type\":\"function\",\"name\":\"owner\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"address\"}]},{\"type\":\"function\",\"name\":\"ownerAddressToPetList\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[{\"type\":\"address\"},{\"type\":\"uint256\"}],\"outputs\":[{\"type\":\"uint256\"}]},{\"type\":\"function\",\"name\":\"petIdxToOwnerAddress\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[{\"type\":\"uint256\"}],\"outputs\":[{\"type\":\"address\"}]},{\"type\":\"function\",\"name\":\"petIndex\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"uint256\"}]},{\"type\":\"function\",\"name\":\"removeCart\",\"constant\":false,\"payable\":false,\"inputs\":[{\"type\":\"uint256\",\"name\":\"index\"}],\"outputs\":[]}]","address":"0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512","network":"localhost","deployer":"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"}
{"abi":"[{\"type\":\"constructor\",\"payable\":false,\"inputs\":[{\"type\":\"uint256\",\"name\":\"initialPetIndex\"}]},{\"type\":\"function\",\"name\":\"addPet\",\"constant\":false,\"payable\":false,\"inputs\":[],\"outputs\":[]},{\"type\":\"function\",\"name\":\"addToCart\",\"constant\":false,\"payable\":false,\"inputs\":[{\"type\":\"uint256\",\"name\":\"_petId\"},{\"type\":\"string\",\"name\":\"_petName\"},{\"type\":\"string\",\"name\":\"_petColor\"},{\"type\":\"uint256\",\"name\":\"_petPrice\"},{\"type\":\"string\",\"name\":\"_petPhoto\"}],\"outputs\":[]},{\"type\":\"function\",\"name\":\"adoptPet\",\"constant\":false,\"payable\":false,\"inputs\":[{\"type\":\"uint256\",\"name\":\"adoptIdx\"}],\"outputs\":[]},{\"type\":\"function\",\"name\":\"allAdoptedPets\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[{\"type\":\"uint256\"}],\"outputs\":[{\"type\":\"uint256\"}]},{\"type\":\"function\",\"name\":\"getAllAdoptedPets\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"uint256[]\"}]},{\"type\":\"function\",\"name\":\"getAllAdoptedPetsByOwner\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"uint256[]\"}]},{\"type\":\"function\",\"name\":\"getCartItems\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"tuple[]\",\"components\":[{\"type\":\"uint256\",\"name\":\"id\"},{\"type\":\"string\",\"name\":\"name\"},{\"type\":\"string\",\"name\":\"color\"},{\"type\":\"uint256\",\"name\":\"price\"},{\"type\":\"string\",\"name\":\"photo\"}]}]},{\"type\":\"function\",\"name\":\"getCartLength\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"uint256\"}]},{\"type\":\"function\",\"name\":\"getOwner\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"address\"}]},{\"type\":\"function\",\"name\":\"owner\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"address\"}]},{\"type\":\"function\",\"name\":\"ownerAddressToPetList\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[{\"type\":\"address\"},{\"type\":\"uint256\"}],\"outputs\":[{\"type\":\"uint256\"}]},{\"type\":\"function\",\"name\":\"petIdxToOwnerAddress\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[{\"type\":\"uint256\"}],\"outputs\":[{\"type\":\"address\"}]},{\"type\":\"function\",\"name\":\"petIndex\",\"constant\":true,\"stateMutability\":\"view\",\"payable\":false,\"inputs\":[],\"outputs\":[{\"type\":\"uint256\"}]},{\"type\":\"function\",\"name\":\"removeCart\",\"constant\":false,\"payable\":false,\"inputs\":[{\"type\":\"uint256\",\"name\":\"index\"}],\"outputs\":[]}]","address":"0x5FbDB2315678afecb367f032d93F642f64180aa3","network":"localhost","deployer":"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"}
1 change: 1 addition & 0 deletions frontend/contract-ui/helpers/init_moralis.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useMoralis } from "react-moralis";
const { web3,account,Moralis } = useMoralis();

// export some needed package to abroad
//* useMoralisInit
export default function useMoralisInit (){
if(web3 != null && account != null && Moralis != null){
return { web3,account,Moralis };
Expand Down
2 changes: 1 addition & 1 deletion frontend/contract-ui/helpers/init_stripe.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ?getStripePublishableKey
// *getStripePublishableKey
const getStripePublishableKey = () => {
// Initialize Stripe.js
return Stripe(import.meta.env.VITE_VUE_PUBLISHABLE_KEY)
Expand Down
26 changes: 22 additions & 4 deletions frontend/contract-ui/src/components/Petitem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,12 @@ export function PetItem({checkIsAuthenticated,isAuthenticated,contract,account,i
const addToCart = async (e) => {
const is_auth = isAuthenticated
const is_exits_in_cart = await checkCartItems(e.target.getAttribute('pet-id'))
const is_adopted = await checkIsAdopted(e.target.getAttribute('pet-index'))

if (is_auth && contract != null && !is_exits_in_cart){
if(is_auth && contract != null && is_adopted){
toast.info('This pet already buyed')
}
else if(is_auth && contract != null && !is_exits_in_cart){
const pet_slug = e.target.getAttribute('pet-slug')
const pet = await getPet(pet_slug)
// let overrides = {
Expand All @@ -77,10 +81,10 @@ export function PetItem({checkIsAuthenticated,isAuthenticated,contract,account,i
// }

const signer = getSigner(account)
const result = await contract.connect(signer).addToCart(pet.id,pet.name,"red",parseInt(pet.price),pet.pet_photo_link)
const result = await contract.connect(signer).addToCart(pet.id,pet.name,pet.color,parseInt(pet.price),pet.pet_photo_link)
toast.success('Added to cart successfully')
}
else{
else if(is_auth && contract != null && is_exits_in_cart){
toast.error('You have already added to cart')
}
}
Expand All @@ -99,6 +103,20 @@ export function PetItem({checkIsAuthenticated,isAuthenticated,contract,account,i
return is_exists
}

// ?checkIsAdopted
const checkIsAdopted = async (pet_index) => {
const signer = getSigner(account)
const adopted_pets = await contract.connect(signer).getAllAdoptedPetsByOwner()
let is_adopted = false
adopted_pets.map((pet) => {
if(pet.toString() == pet_index){
is_adopted = true
return
}
})
return is_adopted
}


//return jsx to client
return (
Expand Down Expand Up @@ -140,7 +158,7 @@ export function PetItem({checkIsAuthenticated,isAuthenticated,contract,account,i
</a>
{
!isAdmin && isAuthenticated ? (
<button onClick={addToCart} pet-id={pet.id} pet-slug={pet.slug} className="text-white w-55 bg-blue-700 hover:bg-blue-800 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
<button onClick={addToCart} pet-index={index} pet-id={pet.id} pet-slug={pet.slug} className="text-white w-55 bg-blue-700 hover:bg-blue-800 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
ml-2 group inline-flex items-center h-9 rounded-full text-sm font-semibold whitespace-nowrap px-3 pr-6 pl-4 focus:outline-none focus:ring-2 ">
Add to cart
<svg
Expand Down
Loading

0 comments on commit e68adb2

Please sign in to comment.