From bafdaeed3a3622b9a30426fc38cb1729bbd14aba Mon Sep 17 00:00:00 2001 From: Parikshit85 Date: Mon, 25 Mar 2024 17:01:16 +0530 Subject: [PATCH 001/157] Add: Influencer Referrals --- src/api/marketplace/accounts/models.py | 1 + src/api/marketplace/accounts/serializers.py | 2 + src/api/marketplace/marketplace/settings.py | 1 + src/api/marketplace/marketplace/urls.py | 3 +- src/api/marketplace/marketplace/views.py | 47 ++- src/api/marketplace/referrals/__init__.py | 0 src/api/marketplace/referrals/admin.py | 7 + src/api/marketplace/referrals/apps.py | 6 + .../referrals/migrations/__init__.py | 0 src/api/marketplace/referrals/models.py | 55 ++++ src/api/marketplace/referrals/serializers.py | 8 + src/api/marketplace/referrals/tests.py | 3 + src/api/marketplace/referrals/urls.py | 8 + src/api/marketplace/referrals/views.py | 64 ++++ .../profile/[id]/_referrals/index.tsx | 293 ++++++++++++++++++ src/ui/app/influencer/profile/[id]/page.tsx | 111 +++++-- src/ui/app/login/page.tsx | 120 ++++++- src/ui/src/hooks/useTwitterAuth.ts | 4 +- 18 files changed, 694 insertions(+), 39 deletions(-) create mode 100644 src/api/marketplace/referrals/__init__.py create mode 100644 src/api/marketplace/referrals/admin.py create mode 100644 src/api/marketplace/referrals/apps.py create mode 100644 src/api/marketplace/referrals/migrations/__init__.py create mode 100644 src/api/marketplace/referrals/models.py create mode 100644 src/api/marketplace/referrals/serializers.py create mode 100644 src/api/marketplace/referrals/tests.py create mode 100644 src/api/marketplace/referrals/urls.py create mode 100644 src/api/marketplace/referrals/views.py create mode 100644 src/ui/app/influencer/profile/[id]/_referrals/index.tsx diff --git a/src/api/marketplace/accounts/models.py b/src/api/marketplace/accounts/models.py index 87350d80..d48fec8c 100644 --- a/src/api/marketplace/accounts/models.py +++ b/src/api/marketplace/accounts/models.py @@ -81,6 +81,7 @@ class User(AbstractUser): blank=True, ) jwt = models.CharField(max_length=255, blank=True, null=True) + referral_code = models.CharField(max_length=16, unique=True, blank=True, null=True) login_method = models.CharField( choices=LOGIN_METHOD_CHOICES, max_length=25, blank=True, null=True ) diff --git a/src/api/marketplace/accounts/serializers.py b/src/api/marketplace/accounts/serializers.py index 445d1dc4..f03fdbba 100644 --- a/src/api/marketplace/accounts/serializers.py +++ b/src/api/marketplace/accounts/serializers.py @@ -5,6 +5,7 @@ from core.serializers import LanguageMasterSerializer, RegionMasterSerializer from orders.models import Order, OrderItem, Review from packages.models import Package, Service +from referrals.serializers import UserReferralsSerializer from .models import ( AccountLanguage, AccountRegion, @@ -227,6 +228,7 @@ class UserSerializer(serializers.ModelSerializer): region = AccountRegionSerializer( read_only=True, source="region_user_account" ) + referral = UserReferralsSerializer(read_only=True, source="user_referral_account") class Meta: model = User diff --git a/src/api/marketplace/marketplace/settings.py b/src/api/marketplace/marketplace/settings.py index 82183a22..a49d211f 100644 --- a/src/api/marketplace/marketplace/settings.py +++ b/src/api/marketplace/marketplace/settings.py @@ -157,6 +157,7 @@ "packages", "core", "orders", + "referrals", "notifications", "corsheaders", "drf_yasg", diff --git a/src/api/marketplace/marketplace/urls.py b/src/api/marketplace/marketplace/urls.py index 9668c7b0..aea785a1 100644 --- a/src/api/marketplace/marketplace/urls.py +++ b/src/api/marketplace/marketplace/urls.py @@ -43,8 +43,9 @@ path("core/", include('core.urls'), name='core'), path("orders/", include('orders.urls')), path("account/", include('accounts.urls')), + path("referrals/", include('referrals.urls')), path("notifications/", include('notifications.urls')), - path("auth-twitter-user//", views.authTwitterUser, name="auth-twitter-user"), + path("auth-twitter-user///", views.authTwitterUser, name="auth-twitter-user"), path( "twitter-login-callback/", views.twitterLoginCallback, diff --git a/src/api/marketplace/marketplace/views.py b/src/api/marketplace/marketplace/views.py index da8dc2e5..42d70dd3 100644 --- a/src/api/marketplace/marketplace/views.py +++ b/src/api/marketplace/marketplace/views.py @@ -1,3 +1,5 @@ +import random +import string from django.shortcuts import redirect from accounts.serializers import UserSerializer from tweepy import Client @@ -17,6 +19,7 @@ import hashlib from requests_oauthlib import OAuth2Session import json +from referrals.models import ReferralRewardsMaster, UserReferrals logger = logging.getLogger(__name__) @@ -28,6 +31,14 @@ twitter = None +def generate_referral_code(): + # Generate a unique referral code + code = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10)) + # Check if the generated code is unique + while User.objects.filter(referral_code=code).exists(): + code = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10)) + return code + def logoutUser(request): response_data = { 'isSuccess': True, @@ -70,6 +81,9 @@ def authTwitterUser(request, role, requestType): try: request.session["role"] = role request.session["requestType"] = requestType + referral_code = request.GET.get("referral_code", "") + if referral_code: + request.session["referral_code"] = referral_code # If the request is connect type, store the user id in session if(requestType == "connect"): @@ -107,6 +121,8 @@ def twitterLoginCallback(request): code = request.GET.get("code") role = request.session.get("role", "") requestType = request.session.get("requestType", "") + referral_code = request.session.get("referral_code", "") + try: # Authenticate User authentication_result = authenticateUser(request, code) @@ -115,7 +131,7 @@ def twitterLoginCallback(request): refresh_token = authentication_result["refresh_token"] # Create USER and JWT and send response if(requestType == "auth"): - return createJWT(userData, access_token, role, refresh_token) + return createJWT(userData, access_token, role, refresh_token, referral_code) # Connect the user with twitter account elif(requestType == "connect"): user_id = request.session.get("user_id", "") @@ -200,7 +216,7 @@ def manage_categories(hashtags, twitter_account): ) new_account_category.save() -def createUser(userData, access_token, role, refresh_token): +def createUser(userData, access_token, role, refresh_token, referral_code): try: is_new_user = False existing_user = TwitterAccount.objects.filter(twitter_id=userData.id).first() @@ -267,7 +283,23 @@ def createUser(userData, access_token, role, refresh_token): ) new_user_account.save() if role=="business_owner": - is_new_user = True + is_new_user = True + existing_user_account=new_user_account + + # New user login with referral_code - + if referral_code: + # Get user account based on referral_code + try: + referred_by = User.objects.get(referral_code=referral_code) + if referred_by: + # Who referred whom + UserReferrals.objects.create(user_account=new_user_account, referred_by=referred_by) + + except Exception as e: + return { + "status": "error", + "message": f"No referral given, invalid referral code - {referral_code}.", + } else: if existing_user_account.role.name != role: return { @@ -277,6 +309,11 @@ def createUser(userData, access_token, role, refresh_token): else: existing_user_account.last_login = datetime.datetime.now() existing_user_account.save() + + # Creating referral_code for users + if not existing_user_account.referral_code: + existing_user_account.referral_code = generate_referral_code() + existing_user_account.save() current_user = User.objects.filter(twitter_account=current_twitter_user).first() logger.info("User Successfully created/updated") @@ -338,7 +375,7 @@ def connectUser(userData, access_token, refresh_token, user_id): return response # The userData here is from twitter -def createJWT(userData, access_token, role, refresh_token): +def createJWT(userData, access_token, role, refresh_token, referral_code): try: # Creating a response object with JWT cookie if role == "business_owner": @@ -346,7 +383,7 @@ def createJWT(userData, access_token, role, refresh_token): elif role == "influencer": redirect_url = f"{config('FRONT_END_URL')}influencer/" - current_user_data = createUser(userData, access_token, role, refresh_token) + current_user_data = createUser(userData, access_token, role, refresh_token, referral_code) if current_user_data["status"] == "error": return redirect( redirect_url diff --git a/src/api/marketplace/referrals/__init__.py b/src/api/marketplace/referrals/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/api/marketplace/referrals/admin.py b/src/api/marketplace/referrals/admin.py new file mode 100644 index 00000000..db6da8e2 --- /dev/null +++ b/src/api/marketplace/referrals/admin.py @@ -0,0 +1,7 @@ +from django.contrib import admin + +from .models import UserReferrals, ReferralRewardsMaster +# Register your models here. + +admin.site.register(UserReferrals) +admin.site.register(ReferralRewardsMaster) \ No newline at end of file diff --git a/src/api/marketplace/referrals/apps.py b/src/api/marketplace/referrals/apps.py new file mode 100644 index 00000000..cf262422 --- /dev/null +++ b/src/api/marketplace/referrals/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ReferralsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'referrals' \ No newline at end of file diff --git a/src/api/marketplace/referrals/migrations/__init__.py b/src/api/marketplace/referrals/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/api/marketplace/referrals/models.py b/src/api/marketplace/referrals/models.py new file mode 100644 index 00000000..742ae168 --- /dev/null +++ b/src/api/marketplace/referrals/models.py @@ -0,0 +1,55 @@ +from django.db import models +import uuid +from accounts.models import User + +# Create your models here. + +class UserReferrals(models.Model): + id = models.UUIDField( + primary_key=True, + verbose_name="User Referral ID", + default=uuid.uuid4, + editable=False, + ) + user_account = models.OneToOneField( + User, + related_name="referral_user_account", + on_delete=models.SET_NULL, + null=True, + blank=True, + ) + referred_by = models.OneToOneField(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='referred_by_account') + + class Meta: + db_table = "user_referrals" + + def __str__(self): + return f"{self.user_account.username} referrals" + + +class ReferralRewardsMaster(models.Model): + TYPE_CHOICES = ( + ('comission', 'comission'), + ('discount', 'discount') + ) + id = models.UUIDField( + primary_key=True, + verbose_name="User Referral Rewards ID", + default=uuid.uuid4, + editable=False, + ) + # type, percentage, deleted, created_at, deleted_at + type = models.CharField(choices=TYPE_CHOICES, + max_length=50, default='service') + percentage = models.DecimalField( + max_digits=6, decimal_places=3, blank=True, null=True) + deleted = models.BooleanField(default=False, blank=True, null=True) + created_at = models.DateTimeField(auto_now_add=True) + deleted_at = models.DateTimeField(blank=True, null=True) + + class Meta: + db_table = "referral_rewards_master" + + def __str__(self): + return f"{self.type} type" + diff --git a/src/api/marketplace/referrals/serializers.py b/src/api/marketplace/referrals/serializers.py new file mode 100644 index 00000000..f863480c --- /dev/null +++ b/src/api/marketplace/referrals/serializers.py @@ -0,0 +1,8 @@ +from rest_framework import serializers + +from .models import UserReferrals + +class UserReferralsSerializer(serializers.ModelSerializer): + class Meta: + model = UserReferrals + fields = "__all__" \ No newline at end of file diff --git a/src/api/marketplace/referrals/tests.py b/src/api/marketplace/referrals/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/src/api/marketplace/referrals/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/src/api/marketplace/referrals/urls.py b/src/api/marketplace/referrals/urls.py new file mode 100644 index 00000000..36d16545 --- /dev/null +++ b/src/api/marketplace/referrals/urls.py @@ -0,0 +1,8 @@ +from django.urls import path +from .views import ReferralLink, ReferralValidity + + +urlpatterns = [ + path("check-referral-validity/", ReferralValidity.as_view(), name="referral-validity"), + path("referral-link/", ReferralLink.as_view(), name="referral-link"), +] \ No newline at end of file diff --git a/src/api/marketplace/referrals/views.py b/src/api/marketplace/referrals/views.py new file mode 100644 index 00000000..1b9f2387 --- /dev/null +++ b/src/api/marketplace/referrals/views.py @@ -0,0 +1,64 @@ +from rest_framework.views import APIView +from accounts.models import User +from marketplace.authentication import JWTAuthentication +from decouple import config +from rest_framework.response import Response +from rest_framework import status +from marketplace.services import ( + handleServerException, +) + + +class ReferralLink(APIView): + def createLink(self,code): + hostname = config('SERVER') + return f"{hostname}auth-twitter-user/influencer/?referral_code={code}" + + authentication_classes = [JWTAuthentication] + def get(self, request): + try: + referral_code = request.user_account.referral_code + if referral_code: + referralLink = self.createLink(referral_code) + if referral_code and referralLink: + return Response( + { + "isSuccess": True, + "data": {"referralLink": referralLink}, + "message": "User Referrals retrieved successfully", + }, + status=status.HTTP_200_OK, + ) + else: + return Response( + { + "isSuccess": False, + "data": None, + "message": "User Referrals not found", + }, + status=status.HTTP_404_NOT_FOUND, + ) + except Exception as e: + return handleServerException(e) + +class ReferralValidity(APIView): + def check_validity(self, code): + try: + User.objects.get(referral_code=code) + return True + except User.DoesNotExist: + return False + def get(self, request): + try: + referral_code = request.GET.get("referral_code", "") + is_valid = self.check_validity(referral_code) + return Response( + { + "isSuccess": is_valid, + "data": {"isValid": is_valid}, + "message": f"Is referral code valid? - {is_valid}", + }, + status=status.HTTP_200_OK, + ) + except Exception as e: + return handleServerException(e) \ No newline at end of file diff --git a/src/ui/app/influencer/profile/[id]/_referrals/index.tsx b/src/ui/app/influencer/profile/[id]/_referrals/index.tsx new file mode 100644 index 00000000..76f70b7b --- /dev/null +++ b/src/ui/app/influencer/profile/[id]/_referrals/index.tsx @@ -0,0 +1,293 @@ +import { notification } from "@/src/components/shared/notification"; +import { getService } from "@/src/services/httpServices"; +import { Box, Grid, IconButton, Typography } from "@mui/material"; +import React, { useState } from "react"; +import { CopyAll, PersonAdd } from "@mui/icons-material"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Paper from "@mui/material/Paper"; + +type Props = {}; + +const REFERAL_GUIDE = [ + "Share your unique referral link to an influential individual in the Xfluencer community.", + "Your referral joins Xfluencer and establishes their services on the platform.", + "Upon the successful sale of their first service to a business, you earn a 1% commission from the platform fee levied on the purchasing business.", + "Additionally, your referral receives a 0.5% share of the transaction fees for their initial five orders as an Xfluencer, in addition to their listed service price.", +]; + +export default function Referrals({}: Props) { + const [referralLink, setReferralLink] = React.useState(""); + + React.useEffect(() => { + getUserReferralLink(); + }, []); + + const getUserReferralLink = async () => { + const { isSuccess, message, data } = await getService( + `/referrals/referral-link/` + ); + if (isSuccess) { + setReferralLink(data?.data?.referralLink); + } else { + notification( + message ? message : "Error fetching user referral link", + "error" + ); + } + }; + + const copyToClipboard = () => { + navigator.clipboard + .writeText(referralLink) + .then(() => { + notification("Copied to clipboard!"); + }) + .catch((err) => console.error("Failed to copy:", err)); + }; + + return ( + + {/* Middle Container */} + + + + + 24 + + Total Referrals + + + + 120 SOL + + Total Earnings + + + + Share your referral link + + + + {referralLink} + + + + + + + + + {/* Section for influencer's you've (current user) referred */} + + + My Referrals + + + + + + Influencer + Date of Joining + Rewards + + + + + + @loremipsumdolor + + 25, sept 2023 + 10 $ + + +
+
+
+
+ + {/* Right Side Container */} + + + + + + + How to Refer Influencers ? + + + + + {REFERAL_GUIDE.map((step, ind) => { + return ( + + + {ind + 1} + + {step} + + ); + })} + + + {referralLink} + + + + + + + + + + {/* The section by influencer who referred you (Current User) */} + + + You will receive a 0.5% from the transaction fees from your initial + 5 orders as an Xfluencer. + + + + + + Order Id + Reward + + + + + #JHBA-12bGHVBJ-67HN + First + + +
+
+ + 4 rewards left + +
+
+
+ ); +} diff --git a/src/ui/app/influencer/profile/[id]/page.tsx b/src/ui/app/influencer/profile/[id]/page.tsx index 1d699e7e..22ab899a 100644 --- a/src/ui/app/influencer/profile/[id]/page.tsx +++ b/src/ui/app/influencer/profile/[id]/page.tsx @@ -23,6 +23,8 @@ import { import { stringToColor } from "@/src/utils/helper"; import EditIcon from "@mui/icons-material/Edit"; import StarIcon from "@mui/icons-material/Star"; +import { Lock } from "@mui/icons-material"; +import Referrals from "./_referrals"; import { Avatar, Box, @@ -31,6 +33,7 @@ import { CircularProgress, Grid, IconButton, + ButtonGroup, Link, MenuItem, Select, @@ -46,17 +49,6 @@ import React, { useCallback, useEffect } from "react"; import Services from "./_services"; import BookmarkIcon from "@mui/icons-material/Bookmark"; -const tabs = [ - { - value: "services", - label: "Services", - }, - // { - // value: "packages", - // label: "Packages", - // }, -]; - const debounce = (fn: Function, ms = 500) => { let timeoutId: ReturnType; return function (this: any, ...args: any[]) { @@ -64,6 +56,19 @@ const debounce = (fn: Function, ms = 500) => { timeoutId = setTimeout(() => fn.apply(this, args), ms); }; }; +const getReferralStyles = (referralsEnabled: number | boolean | undefined) => { + if (!referralsEnabled) { + return { + cursor: "not-allowed", + color: "rgba(0, 0, 0, 0.26)", + border: "1px solid rgba(0, 0, 0, 0.12)", + "&:hover": { + border: "1px solid rgba(0, 0, 0, 0.12)", + }, + }; + } + return {}; +}; const ProfileLayout = ({ params, @@ -72,6 +77,9 @@ const ProfileLayout = ({ id: string; }; }) => { + const [type, setType] = React.useState("services"); + const [referralsEnabled, setReferralsEnabled] = + React.useState(false); const [twitterPromotionText, setTwitterPromotionText] = React.useState( XFLUENCER_PROMOTION_TEXT ); @@ -89,6 +97,10 @@ const ProfileLayout = ({ const [promotionLoading, setPromotionLoading] = React.useState(false); + useEffect(() => { + setReferralsEnabled(!!(wallets?.length && params?.id == loggedInUser?.id)); + }, [wallets]); + useEffect(() => { if (params.id) { getUserDetails(); @@ -880,11 +892,62 @@ const ProfileLayout = ({ alignItems: "flex-top", }} > - {tabs.map((item) => ( - - {item.label} - - ))} + {params?.id === loggedInUser?.id ? ( + + + + + + + ) : ( + Services + )} {/* For dynamic routes pass props like this (In correspondence with Backend) */} {/* */} - + {type == "services" ? ( + + ) : ( + + )} diff --git a/src/ui/app/login/page.tsx b/src/ui/app/login/page.tsx index 0485939c..fa8dc404 100644 --- a/src/ui/app/login/page.tsx +++ b/src/ui/app/login/page.tsx @@ -1,6 +1,15 @@ "use client"; import Image from "next/image"; -import { Box, Button, Grid, Typography } from "@mui/material"; +import { + Box, + Button, + Grid, + IconButton, + Input, + InputAdornment, + TextField, + Typography, +} from "@mui/material"; import React, { useEffect, useState } from "react"; import XfluencerLogo from "@/public/XfluencerLogo_Icon.png"; import LoginOptions from "./components/loginOptions"; @@ -10,6 +19,9 @@ import WalletConnectModal from "@/src/components/web3Components/walletConnectMod import { useRouter, useSearchParams } from "next/navigation"; import { useAppSelector } from "@/src/hooks/useRedux"; import EmailLoginModal from "@/src/components/emailLoginModal"; +import { getService } from "@/src/services/httpServices"; +import { Send } from "@mui/icons-material"; +import { notification } from "@/src/components/shared/notification"; const Login: React.FC = () => { const router = useRouter(); @@ -20,6 +32,9 @@ const Login: React.FC = () => { const [loginAs, setLoginAs] = useState(role ?? "Business"); const [walletOpen, setWalletOpen] = useState(false); const [emailOpen, setEmailOpen] = useState(false); + const [referralCode, setReferralCode] = useState(""); + const [isReferralCodeValid, setIsReferralCodeValid] = useState(false); + const [isUserTyping, setIsUserTyping] = useState(false); const user = useAppSelector((state) => state.user); const { @@ -32,7 +47,7 @@ const Login: React.FC = () => { if (isTwitterUserLoggedIn) { const redirectRoute = user?.user?.role?.name == "business_owner" - ? "/business/explore" + ? "/business" : "/influencer"; router.push(redirectRoute); } @@ -65,9 +80,29 @@ const Login: React.FC = () => { const handleConnectX = () => { startTwitterAuthentication({ role: loginAs == "Business" ? "business_owner" : "influencer", + referral_code: isReferralCodeValid ? referralCode : "", }); }; + const checkReferralCode = async () => { + try { + const { isSuccess, data } = await getService( + `referrals/check-referral-validity/?referral_code=${referralCode}` + ); + if (isSuccess) { + notification("Referral Code is Valid"); + setIsReferralCodeValid(true); + } else { + notification("Invalid Referral Code", "error"); + setIsReferralCodeValid(false); + } + setIsUserTyping(false); + } catch (error) { + console.error("Error during referal code checking:", error); + setIsReferralCodeValid(false); + } + }; + return ( { Sign in to XFluencer as a {loginAs} - - Login as {loginAs === "Business" ? "Influencer" : "Business"}? + + {loginAs == "Business" + ? "Discover a world of opportunity on our marketplace, where you can connect with top influencers on X to elevate your brand's presence. Browse through a variety of services, from tweets to retweets and more, all priced in Solana – the trusted cryptocurrency for secure transactions." + : "Monetize your X influence with our marketplace. Set your rates in Solana and showcase your services to businesses eager to connect with you. Link your X account to get started!"} @@ -114,14 +147,68 @@ const Login: React.FC = () => { - + + {isReferralCodeValid && + !isUserTyping && + loginAs === "Influencer" ? ( + + Hurray!!! Referral Code {referralCode} Applied. + + ) : null} + {loginAs === "Influencer" ? ( + + { + setReferralCode(e.target.value); + setIsUserTyping(true); + }} + endAdornment={ + + + + + + } + /> + + *Only for first time users + + + ) : null} + {loginAs === "Business" ? ( { ) : null} + + Login as {loginAs === "Business" ? "Influencer" : "Business"}? + {/* Wallet Model */} diff --git a/src/ui/src/hooks/useTwitterAuth.ts b/src/ui/src/hooks/useTwitterAuth.ts index 658d5c5b..def6e9eb 100644 --- a/src/ui/src/hooks/useTwitterAuth.ts +++ b/src/ui/src/hooks/useTwitterAuth.ts @@ -39,11 +39,13 @@ export default function useTwitterAuth() { // Function to initiate X user authentication const startTwitterAuthentication = async ({ role = "", + referral_code = "", }: { role: string; + referral_code?: string; }) => { try { - window.location.href = `${BACKEND_URL}auth-twitter-user/${role}/auth`; + window.location.href = `${BACKEND_URL}auth-twitter-user/${role}/auth/?referral_code=${referral_code}`; } catch (error) { console.error("Error initiating Twitter authentication:", error); } From f1a4a1e32197d9b45acf7c7af2baa3578c0cc48b Mon Sep 17 00:00:00 2001 From: Parikshit85 Date: Mon, 25 Mar 2024 17:28:21 +0530 Subject: [PATCH 002/157] chore: remove deprecated code --- .../profile/[id]/_referrals/index.tsx | 92 +------------------ 1 file changed, 2 insertions(+), 90 deletions(-) diff --git a/src/ui/app/influencer/profile/[id]/_referrals/index.tsx b/src/ui/app/influencer/profile/[id]/_referrals/index.tsx index 76f70b7b..021c1692 100644 --- a/src/ui/app/influencer/profile/[id]/_referrals/index.tsx +++ b/src/ui/app/influencer/profile/[id]/_referrals/index.tsx @@ -69,13 +69,13 @@ export default function Referrals({}: Props) { > - 24 + 0 Total Referrals - 120 SOL + 0 Tokens Total Earnings @@ -109,43 +109,6 @@ export default function Referrals({}: Props) { - - {/* Section for influencer's you've (current user) referred */} - - - My Referrals - - - - - - Influencer - Date of Joining - Rewards - - - - - - @loremipsumdolor - - 25, sept 2023 - 10 $ - - -
-
-
{/* Right Side Container */} @@ -236,57 +199,6 @@ export default function Referrals({}: Props) { - - {/* The section by influencer who referred you (Current User) */} - - - You will receive a 0.5% from the transaction fees from your initial - 5 orders as an Xfluencer. - - - - - - Order Id - Reward - - - - - #JHBA-12bGHVBJ-67HN - First - - -
-
- - 4 rewards left - -
); From 7cc5b0d344a25dd5213601486608bb1180ebb597 Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Mon, 25 Mar 2024 13:32:47 +0000 Subject: [PATCH 003/157] clarify readme on solana python --- solana-python/README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/solana-python/README.md b/solana-python/README.md index edf36934..fefd11b3 100644 --- a/solana-python/README.md +++ b/solana-python/README.md @@ -4,21 +4,18 @@ This is a static client for XFluencer Solana program generated with anchorpy. Th ## Settings -A configuration file `config.json` file is included in this clinet used as example on the test launcher scripts for the different types of instructions: escrow creation, validation and claim amounts. +Configuration file `config.json` contains public keys of wallets and program ids for testing purposes. This is used by launcher scripts to test the program instructions, i.e. escrow creation, validation and claim amounts. -The config file also contains arguments used as instructions, with public keys for business, influencer and platform validator. +You can change this configuration as needed on public keys/keypairs for business, influencer and platform validator. -Change the order code as required. At any point, there will exist a unique escrow triple on-change defined by the triplet: (busines pubkey, influencer pubkey, order code). +As constraint, there will be a unique tupble of data for the program on-change defined by the triplet (busines pubkey, influencer pubkey, order code) -Program Id's are specified here, depending on the network choosen. +Program Id's are also specified here, depending on the network that has been choosen for deployment. Change this accounts accordantly depending on your testing requirements. - ## Transaction Script Launchers -You will find CLI scripts ilustrating how the client and instructions should be used and formed respectively. - To test a specific launcher from CLI, Create virtual environment @@ -41,12 +38,13 @@ Launch example script: You will get an output from the script with Ok or Error, depending on the settings +In this folder there are other test launchers for cancel, create and validate. + ## Deploying the Python Client Release the python client in form of python wheel. This allows to work from the API independently to the code changes happened in this repository. - Enter in `setup.py` to change the version number, that will be the wheel version to release. Once you have change the settings of the setup, create a version of the package from this folder. @@ -122,7 +120,7 @@ As follows the steps to setup a validator instruction: import asyncio from pyxfluencer import validate_escrow_to_cancel, validate_escrow_to_delivered -from pyxfluencer.utils import get_local_keypair_pubkey` +from pyxfluencer.utils import get_local_keypair_pubkey ``` Notice, that functions in the library are async so it is necessary to `await` for them. From 5a6e73c548270768646c36f667759be7796dce97 Mon Sep 17 00:00:00 2001 From: Parikshit85 Date: Tue, 26 Mar 2024 13:01:20 +0530 Subject: [PATCH 004/157] refactor: Order request page if there's only one order and that too in pending state --- src/ui/app/influencer/orders/page.tsx | 32 ++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/ui/app/influencer/orders/page.tsx b/src/ui/app/influencer/orders/page.tsx index 8a178f22..1030be3a 100644 --- a/src/ui/app/influencer/orders/page.tsx +++ b/src/ui/app/influencer/orders/page.tsx @@ -167,6 +167,29 @@ export default function Orders() { } }; + const handleUserInteraction = async () => { + const { isSuccess, data, message } = await postService( + `orders/order-list/`, + { + page_number: pagination.current_page_number, + page_size: pagination.current_page_size, + } + ); + + if (isSuccess && data?.data?.status_counts) { + const { status_counts: orders } = data.data; + const totalOrders = Object.values(orders).reduce( + (acc: number, curr: any) => acc + curr, + 0 + ); + + if (totalOrders === 1 && orders.pending === 1) { + setStepIndex(0); + setRun(true); + } + } + }; + const getOrders = async () => { try { setLoading(true); @@ -180,11 +203,6 @@ export default function Orders() { } ); if (isSuccess) { - // Open the user guide if there's only 1 order request. - if (data?.pagination?.total_data_count == 1) { - setStepIndex(0); - setRun(true); - } setOrders(data?.data?.orders); setPagination({ ...pagination, @@ -365,6 +383,10 @@ export default function Orders() { }, ]; + useEffect(() => { + handleUserInteraction(); + }, []); + useEffect(() => { const delayDebounceFn = setTimeout(() => { getOrders(); From 95188c71c6ce849ec4d5131a7073d04ba20b1821 Mon Sep 17 00:00:00 2001 From: Parikshit85 Date: Tue, 26 Mar 2024 13:39:24 +0530 Subject: [PATCH 005/157] Add: Referral code to promotion link --- src/ui/app/influencer/profile/[id]/page.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ui/app/influencer/profile/[id]/page.tsx b/src/ui/app/influencer/profile/[id]/page.tsx index ce005dc9..ab340a17 100644 --- a/src/ui/app/influencer/profile/[id]/page.tsx +++ b/src/ui/app/influencer/profile/[id]/page.tsx @@ -281,7 +281,11 @@ const ProfileLayout = ({ ); if (isSuccess) { setCurrentUser(data?.data); - // TODO: Add the referral link to the twitterPromotionText and set it to the state + if (data?.data?.referral_code) { + setTwitterPromotionText( + `${XFLUENCER_PROMOTION_TEXT} ${data?.data?.referral_code}` + ); + } } else { notification(message ? message : "Error fetching user details", "error"); } From 23f5a11d6ba5a20d9721413d4f450196138eaa4c Mon Sep 17 00:00:00 2001 From: Mudit Mahajan Date: Tue, 26 Mar 2024 13:40:19 +0530 Subject: [PATCH 006/157] Add new fields to OrderItemType and update NotificationPanel --- .gitignore | 12 +- src/api/marketplace/notifications/views.py | 6 + src/api/marketplace/orders/services.py | 54 +++- src/api/marketplace/orders/tasks.py | 205 ++++++++++++++-- src/ui/app/business/dashboard/page.tsx | 230 ++++++++++++++++++ src/ui/app/business/messages/page.tsx | 194 +++++++++++++-- src/ui/app/components/navbar/index.tsx | 18 +- src/ui/app/influencer/messages/page.tsx | 193 +++++++++++++-- .../components/notificationPanel/index.tsx | 11 +- src/ui/src/utils/types.ts | 2 + 10 files changed, 840 insertions(+), 85 deletions(-) diff --git a/.gitignore b/.gitignore index f11c451d..78e8df27 100644 --- a/.gitignore +++ b/.gitignore @@ -55,11 +55,17 @@ solana-client/node_modules/* solana-client/.next* solana/docker-target* solana-python/build* -solana-python/src/*egg* +solana-python/*egg* +solana-python/venv/ +*pyc # celery-beat src/api/celerybeat-schedule - +#idea src/ui/.idea -src/api/marketplace/.idea \ No newline at end of file +src/api/marketplace/.idea + +#secret +# src/api/marketplace/secret/ +src/api/marketplace/dump.rdb diff --git a/src/api/marketplace/notifications/views.py b/src/api/marketplace/notifications/views.py index efe91466..db7b884f 100644 --- a/src/api/marketplace/notifications/views.py +++ b/src/api/marketplace/notifications/views.py @@ -1,3 +1,4 @@ +from orders.models import OrderMessage from marketplace.authentication import JWTAuthentication from marketplace.services import Pagination, handleNotFound from notifications.serializers import NotificationSerializer @@ -27,6 +28,10 @@ def get(self, request): if is_read is not None: notifications = notifications.filter(is_read=is_read) + unread_message_count = OrderMessage.objects.filter( + receiver_id=user, status__in=['delivered', 'sent']).count() + + # Paginate the notifications pagination = Pagination(notifications, request) serializer = NotificationSerializer(pagination.getData(), many=True) @@ -37,6 +42,7 @@ def get(self, request): "data": { "notifications": serializer.data, "unread_count": unread_count, + "unread_message_count": unread_message_count, }, "message": "All Notifications retrieved successfully", "pagination": pagination.getPageInfo(), diff --git a/src/api/marketplace/orders/services.py b/src/api/marketplace/orders/services.py index 40910317..e0e3a525 100644 --- a/src/api/marketplace/orders/services.py +++ b/src/api/marketplace/orders/services.py @@ -105,7 +105,7 @@ def create_notification_for_order_item(order_item, old_status, new_status): if old_status == 'accepted' and new_status == 'scheduled': # Case 1 - message = 'Your order item ' + order_item.package.name + \ + message = 'Your order item ' + order_item.package.name + ' from order ' + order.order_code + \ ' has been scheduled by ' + influencer.username title = 'Order Item Scheduled' Notification.objects.create( @@ -114,7 +114,7 @@ def create_notification_for_order_item(order_item, old_status, new_status): elif old_status == 'scheduled' and new_status == 'published': # Case 2 - message = 'Your order item ' + order_item.package.name + \ + message = 'Your order item ' + order_item.package.name + ' from order ' + order.order_code + \ ' has been published by ' + influencer.username title = 'Order Item Published' Notification.objects.create( @@ -129,12 +129,58 @@ def create_notification_for_order_item(order_item, old_status, new_status): elif old_status == 'scheduled' and new_status == 'cancelled': # Case 3 - message = 'Your order item ' + order_item.package.name + \ + message = 'Your order item ' + order_item.package.name + ' from order ' + order.order_code + \ ' has been cancelled by ' + influencer.username title = 'Order Item Cancelled' Notification.objects.create( user=buyer, message=message, title=title, slug=BUSINESS_DASHBOARD_URL) + elif old_status == 'scheduled' and new_status == 'failed': + # Case 3 + message = 'Your order item ' + order_item.package.name + 'from order ' + order.order_code + \ + ' has failed to publish by Xfluencer. Please review the order item and try again.' + title = 'Order Item Failed' + notifications = [] + notifications.append({ + 'user_id': buyer.id, + 'message': message, + 'title': title, + 'slug': BUSINESS_DASHBOARD_URL + }) + notifications.append({ + 'user_id': influencer.id, + 'message': message, + 'title': title, + 'slug': INFLUENCER_DASHBOARD_URL + }) + Notification.objects.bulk_create( + [Notification(**notification) for notification in notifications]) + + +def create_post_verification_failed_notification(order_item): + # Get the order + order = order_item.order_id + buyer = order.buyer + influencer = order_item.package.influencer + + message = 'Your order item ' + order_item.package.name + 'from order ' + order.order_code + \ + ' has failed verification by Xfluencer. Please review the order.' + title = 'Order Item Verification Failed' + notifications = [] + notifications.append({ + 'user_id': buyer.id, + 'message': message, + 'title': title, + 'slug': BUSINESS_DASHBOARD_URL + }) + notifications.append({ + 'user_id': influencer.id, + 'message': message, + 'title': title, + 'slug': INFLUENCER_DASHBOARD_URL + }) + Notification.objects.bulk_create( + [Notification(**notification) for notification in notifications]) def create_order_tracking(order, status): """ @@ -173,7 +219,7 @@ def create_reminider_notification(order_item): current_time = timezone.now() time_left = order_item.publish_date - current_time minutes_left = int(time_left.total_seconds() / 60) - message = 'You have an order item ' + order_item.package.name + \ + message = 'You have an order item ' + order_item.package.name + ' from order ' + order_item.order_id.order_code + \ ' due for publishing in ' + str(minutes_left) + ' minutes' title = 'Order Item Reminder' Notification.objects.create( diff --git a/src/api/marketplace/orders/tasks.py b/src/api/marketplace/orders/tasks.py index 61a959cd..a416ea26 100644 --- a/src/api/marketplace/orders/tasks.py +++ b/src/api/marketplace/orders/tasks.py @@ -1,9 +1,11 @@ -from accounts.models import TwitterAccount, User +import asyncio +import logging +from accounts.models import TwitterAccount, User, Wallet +from core.models import Configuration from notifications.models import Notification -from orders.services import create_notification_for_order, create_notification_for_order_item, \ - create_order_item_tracking, create_order_tracking, create_reminider_notification -from orders.models import Order, OrderItem, OrderItemMetaData - +from orders.services import create_notification_for_order, create_notification_for_order_item, create_order_item_status_update_message, \ + create_order_item_tracking, create_order_tracking, create_post_verification_failed_notification, create_reminider_notification +from orders.models import Escrow, OnChainTransaction, Order, OrderItem, OrderItemMetaData, OrderItemMetric from tweepy import Client from decouple import config @@ -36,6 +38,7 @@ ACCESS_SECRET = config("ACCESS_SECRET") TWEET_LIMIT = 280 +logger = logging.getLogger(__name__) def check_order_status(pk): try: @@ -76,7 +79,7 @@ def tweet(text, client): tweet_id = res.data['id'] return tweet_id except Exception as e: - raise Exception(str(e)) + logger.error('Error in publishing tweet: %s', str(e)) def like_tweet(tweet_id, client): @@ -84,7 +87,7 @@ def like_tweet(tweet_id, client): res = client.like_tweet(tweet_id=tweet_id, user_auth=False) return res.data['id'] except Exception as e: - raise Exception(str(e)) + logger.error('Error in liking tweet: %s', str(e)) def reply_to_tweet(text, in_reply_to_tweet_id, client): @@ -93,7 +96,7 @@ def reply_to_tweet(text, in_reply_to_tweet_id, client): text=text, in_reply_to_tweet_id=in_reply_to_tweet_id, user_auth=False) return res.data['id'] except Exception as e: - raise Exception(str(e)) + logger.error('Error in replying to tweet: %s', str(e)) def quote_tweet(text, tweet_id, client): @@ -102,7 +105,7 @@ def quote_tweet(text, tweet_id, client): text=text, quote_tweet_id=tweet_id, user_auth=False) return res.data['id'] except Exception as e: - raise Exception(str(e)) + logger.error('Error in quoting tweet: %s', str(e)) def poll(text, poll_options, poll_duration_minutes, client): @@ -112,7 +115,7 @@ def poll(text, poll_options, poll_duration_minutes, client): user_auth=False) return res.data['id'] except Exception as e: - raise Exception(str(e)) + logger.error('Error in creating poll: %s', str(e)) def retweet(tweet_id, client): @@ -120,7 +123,7 @@ def retweet(tweet_id, client): res = client.retweet(tweet_id=tweet_id, user_auth=False) return res.data['id'] except Exception as e: - raise Exception(str(e)) + logger.error('Error in retweeting tweet: %s', str(e)) def thread(text, client): @@ -148,7 +151,7 @@ def thread(text, client): text=text, in_reply_to_tweet_id=published_tweet_id, user_auth=False) return published_tweet_id except Exception as e: - raise Exception(str(e)) + logger.error('Error in creating thread: %s', str(e)) @shared_task @@ -226,25 +229,44 @@ def twitter_task(order_item_id): elif service_type == 'thread': res = thread(text, client) - order_item.published_tweet_id = res - order_item.status = 'published' - order_item.save() + if res: + order_item.published_tweet_id = res + order_item.status = 'published' + order_item.save() - # Create a order item tracking for the order item - create_order_item_tracking(order_item, order_item.status) + # Get the countfown time for the validate_order_item task + COUNTDOWN_TIME_FOR_VALIDATION = int(Configuration.objects.get( + key='countdown_time_for_validation').value) - # Check if the order is completed - check_order_status(order_item.order_id.id) + # Call the validate_order_item task to run 2 minutes after now + validate_order_item.apply_async( + args=[order_item.id], countdown=COUNTDOWN_TIME_FOR_VALIDATION) - # Create notification for order item - create_notification_for_order_item( - order_item, 'scheduled', 'published') + # Create a order item tracking for the order item + create_order_item_tracking( + order_item=order_item, status=order_item.status) - except Exception as e: - raise Exception(str(e)) + # Check if the order is completed + check_order_status(pk=order_item.order_id.id) - return True + # Create notification for order item + create_notification_for_order_item( + order_item=order_item, old_status='scheduled', new_status='published') + create_order_item_status_update_message( + order_item=order_item, updated_by=order_item.package.influencer) + else: + order_item.status = 'cancelled' + order_item.save() + # Create a order item tracking for the order item + create_order_item_tracking( + order_item=order_item, status=order_item.status) + + create_notification_for_order_item( + order_item=order_item, old_status='scheduled', new_status='failed') + + except Exception as e: + logger.error('Error in twitter task: %s', str(e)) def schedule_tweet(order_item_id): try: @@ -338,3 +360,136 @@ def schedule_reminder_notification(): create_reminider_notification(order_item) except Exception as e: raise Exception(str(e)) + + +def is_post_published(order_item_id) -> bool: + try: + # Get order item + order_item = OrderItem.objects.get(id=order_item_id) + except OrderItem.DoesNotExist: + raise Exception('Order item does not exist') + + try: + # Get the twitter account of the influencer + twitter_account = TwitterAccount.objects.get( + id=order_item.package.influencer.twitter_account.id) + client = Client(bearer_token=twitter_account.access_token, + consumer_key=CONSUMER_KEY, + consumer_secret=CONSUMER_SECRET, + access_token=ACCESS_TOKEN, + access_token_secret=ACCESS_SECRET + ) + res = client.get_tweet( + id=order_item.published_tweet_id, user_auth=False) + + return str(res.data.get('id', '')) == str(order_item.published_tweet_id) + except Exception as e: + logger.error('Error in checking if post is published: %s', str(e)) + return False + + +def is_post_liked(order_item_id) -> bool: + try: + # Get order item + order_item = OrderItem.objects.get(id=order_item_id) + except OrderItem.DoesNotExist: + raise Exception('Order item does not exist') + + try: + # Get the twitter account of the influencer + twitter_account = TwitterAccount.objects.get( + id=order_item.package.influencer.twitter_account.id) + client = Client(bearer_token=twitter_account.access_token, + consumer_key=CONSUMER_KEY, + consumer_secret=CONSUMER_SECRET, + access_token=ACCESS_TOKEN, + access_token_secret=ACCESS_SECRET + ) + res = client.get_liking_users( + id=order_item.published_tweet_id, user_auth=False) + # res.data is an array of {id, username, name} objects + # Check that twitter_account.twitter_id is in the array + return True if any( + str(user['id']) == str(twitter_account.twitter_id) for user in res.data) else False + except Exception as e: + logger.error('Error in checking if post is liked: %s', str(e)) + return False + + +@celery_app.task(base=QueueOnce, once={'graceful': True}) +def validate_order_item(order_item_id): + try: + # Get order item + order_item = OrderItem.objects.get(id=order_item_id) + if order_item.status != 'published': + raise Exception('Order item is not in published status') + is_published = False + if order_item.service_master.twitter_service_type == 'like_tweet': + is_published = is_post_liked(order_item_id=order_item.id) + else: + is_published = is_post_published(order_item_id=order_item.id) + if is_published: + order_item.is_verified = True + order_item.save() + check_order_status(pk=order_item.order_id.id) + else: + print('Post verification failed') + order_item.is_verified = False + order_item.save() + create_post_verification_failed_notification(order_item=order_item) + except Exception as e: + raise Exception(str(e)) + + +@celery_app.task() +def store_order_item_metrics(): + # Get the current date + now = timezone.now() + # Get all order items that are in published status and verified and service type is not retweet or like + order_items = OrderItem.objects.filter( + status='published', + service_master__twitter_service_type__in=['tweet', 'reply_to_tweet', 'quote_tweet', 'poll', 'thread'] + ) + for order_item in order_items: + # Calculate the number of days since the order item was published + days_since_published = (now - order_item.publish_date).days + + # Check if the current day is the 1st, 2nd, 3rd, 7th, 14th, 21st, or 28th day since the order item was published + if days_since_published in [1, 2, 3, 7, 14, 21, 28]: + # Get the twitter account of the influencer + twitter_account = TwitterAccount.objects.get( + id=order_item.package.influencer.twitter_account.id) + client = Client(bearer_token=twitter_account.access_token, + consumer_key=CONSUMER_KEY, + consumer_secret=CONSUMER_SECRET, + access_token=ACCESS_TOKEN, + access_token_secret=ACCESS_SECRET + ) + try: + res = client.get_tweet( + id=order_item.published_tweet_id, user_auth=False, tweet_fields=TWEET_FIELDS) + + # For all the res.data fields, create a OrderItemMetric object + public_metrics = res.data['public_metrics'] + organic_metrics = res.data['organic_metrics'] + non_public_metrics = res.data['non_public_metrics'] + + order_item_metrics = [] + + for key, value in public_metrics.items(): + order_item_metrics.append( + OrderItemMetric(order_item=order_item, metric=key, value=value, type='public_metrics')) + + for key, value in organic_metrics.items(): + order_item_metrics.append( + OrderItemMetric(order_item=order_item, metric=key, value=value, type='organic_metrics')) + + for key, value in non_public_metrics.items(): + order_item_metrics.append( + OrderItemMetric(order_item=order_item, metric=key, value=value, type='non_public_metrics')) + + OrderItemMetric.objects.bulk_create(order_item_metrics) + + except Exception as e: + logger.error('Error in getting tweet metrics: %s', str(e)) + continue diff --git a/src/ui/app/business/dashboard/page.tsx b/src/ui/app/business/dashboard/page.tsx index f7690f0e..807f61a5 100644 --- a/src/ui/app/business/dashboard/page.tsx +++ b/src/ui/app/business/dashboard/page.tsx @@ -43,6 +43,16 @@ import Image from "next/image"; import NextLink from "next/link"; import { useRouter } from "next/navigation"; import React, { useEffect, useState } from "react"; +<<<<<<< Updated upstream +======= +import Joyride, { ACTIONS, EVENTS, STATUS } from "react-joyride"; +import XfluencerLogo from "@/public/svg/Xfluencer_Logo_Beta.svg"; +import { DriveEta } from "@mui/icons-material"; +import CheckCircleOutlineOutlinedIcon from "@mui/icons-material/CheckCircleOutlineOutlined"; +import MessageIcon from "@mui/icons-material/Message"; +import CheckCircleIcon from "@mui/icons-material/CheckCircle"; +import CancelIcon from "@mui/icons-material/Cancel"; +>>>>>>> Stashed changes export default function BusinessDashboardPage() { const router = useRouter(); @@ -512,6 +522,226 @@ export default function BusinessDashboardPage() { }, ]; + const orderItemColumns = [ + // Columns for Package name, Price, Order Item Creation Date, Publish Date, Order Code, Published Tweet Link, Status + { + field: "package__name", + headerName: "Service", + flex: 1, + minWidth: 200, + sortable: false, + renderCell: ( + params: GridRenderCellParams + ): React.ReactNode => { + return ( + + {params?.row?.package?.name} + + ); + }, + }, + { + field: "order_id__order_code", + headerName: "Order", + flex: 1, + renderCell: ( + params: GridRenderCellParams + ): React.ReactNode => { + return ( + + {params?.row?.order_id?.order_code} + + ); + }, + }, + { + field: "price", + headerName: "Price", + flex: 1, + renderCell: ( + params: GridRenderCellParams + ): React.ReactNode => { + return ( + + {params?.row?.price} {params?.row?.currency?.symbol} + + ); + }, + }, + { + field: "publish_date", + headerName: "Publish Date & Time", + flex: 1, + renderCell: ( + params: GridRenderCellParams + ): React.ReactNode => { + return ( + + {params?.row?.publish_date + ? dayjs(params?.row?.publish_date).format( + DISPLAY_DATE_TIME_FORMAT + ) + : "Not Published"} + + ); + }, + }, + { + field: "published_tweet_id", + headerName: "Published Post Link", + flex: 1, + sortable: false, + renderCell: ( + params: GridRenderCellParams + ): React.ReactNode => { + return ( + + {params?.row?.published_tweet_id ? ( + + + + + + ) : ( + + Not Published + + )} + + ); + }, + }, + { + field: "actions", + headerName: "Actions", + flex: 1, + sortable: false, + renderCell: ( + params: GridRenderCellParams + ): React.ReactNode => { + return ( + + + { + setSelectedOrder(params?.row?.order_id); + setOpen(true); + }} + > + + + + {params?.row?.status === ORDER_ITEM_STATUS.PUBLISHED && + params?.row?.service_master?.twitter_service_type !== + SERVICE_MASTER_TWITTER_SERVICE_TYPE.LIKE_TWEET && + params?.row?.service_master?.twitter_service_type !== + SERVICE_MASTER_TWITTER_SERVICE_TYPE?.RETWEET && + params?.row?.is_verified && ( + + + + + + + + )} + {(params?.row?.status === ORDER_ITEM_STATUS.ACCEPTED || + params?.row?.status === ORDER_ITEM_STATUS.CANCELLED) && ( + // Action to approve the post + <> + {!params?.row?.approved && ( + + { + approveOrderItem(params?.row?.id); + }} + > + + + + )} + + )} + + ); + }, + }, + { + field: "status", + headerName: "Status", + flex: 1, + renderCell: ( + params: GridRenderCellParams + ): React.ReactNode => { + return ; + }, + }, + { + field: "is_verified", + headerName: "Verification Status", + flex: 1, + renderCell: ( + params: GridRenderCellParams + ): React.ReactNode => { + return ( + + {params?.row?.is_verified ? ( + + ) : ( + + )} + + ); + }, + }, + ]; + useEffect(() => { getOrdersCount(); }, []); diff --git a/src/ui/app/business/messages/page.tsx b/src/ui/app/business/messages/page.tsx index 2c171b7a..0619da37 100644 --- a/src/ui/app/business/messages/page.tsx +++ b/src/ui/app/business/messages/page.tsx @@ -9,9 +9,10 @@ import { useAppSelector } from "@/src/hooks/useRedux"; import { postService } from "@/src/services/httpServices"; import { ORDER_STATUS } from "@/src/utils/consts"; import ChatIcon from "@mui/icons-material/Chat"; -import { Box, Grid, Typography } from "@mui/material"; +import { Box, CircularProgress, Grid, Typography } from "@mui/material"; import dayjs from "dayjs"; import * as relativeTime from "dayjs/plugin/relativeTime"; +import { useSearchParams } from "next/navigation"; import React, { useEffect } from "react"; const relativeTime1: any = relativeTime; @@ -20,6 +21,9 @@ dayjs.extend(relativeTime1); export default function BusinessMessages() { const user = useAppSelector((state) => state.user)?.user; const [orderChats, setOrderChats] = React.useState([]); + + const searchParams = useSearchParams(); + const [loading, setLoading] = React.useState(false); const [totalUnreadMessages, setTotalUnreadMessages] = React.useState(0); const [selectedOrderChat, setSelectedOrderChat] = React.useState(null); @@ -32,17 +36,133 @@ export default function BusinessMessages() { ], }); - const getAllChats = async () => { + // User Guide for the very first order + const [stepIndex, setStepIndex] = useState(0); + const [run, setRun] = useState(false); + const [hasAMessage, setHasAMessage] = useState(false); + const [steps, setSteps] = useState([ + { + content: ( + + bgimg + + Chat with influencers. + + + This tour will guide you through the chatting room where you can + message the influencers and have a short discussion about your + order. + + + ), + placement: "center", + target: "body", + }, + { + content: ( + + + Customized filters. + + + Advanced filters for chats based on the services, order ID, and + status of orders. + + + ), + placement: "right", + target: ".joyride-message-filters", + }, + { + content: ( + + + Influencer's List. + + + Click on the influencer to chat with them and have a discussion + about the order. + + + ), + placement: "right", + target: ".joyride-user-chats", + }, + { + content: ( + + bgimg + + Congratulations!!! + + + You've completed your messages tour, you're good to go and chat with + your influencer. + + + ), + placement: "center", + target: "body", + }, + ]); + + const handleJoyrideCallback = (data: any) => { + const { action, index, status, type } = data; + + if ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND].includes(type)) { + // Update state to advance the tour + setStepIndex(index + (action === ACTIONS.PREV ? -1 : 1)); + } else if ([STATUS.FINISHED, STATUS.SKIPPED].includes(status)) { + // Need to set our running state to false, so we can restart if we click start again. + setRun(false); + } + }; + + const handleUserInteraction = async () => { const { isSuccess, message, data } = await postService( "/orders/user-order-messages/", - { - ...filters, - } + {} ); if (isSuccess) { - setOrderChats(data?.data?.orders); - setTotalUnreadMessages(data?.data?.total_unread_messages_count); - } + // Fetching all user-message and if there's exactly 1 object, show the user guide + if (data?.data?.orders?.length == 1) { + setStepIndex(0); + setRun(true); + } + if (data?.data?.orders?.length > 0) { + setHasAMessage(true); + } + } + }; + + const getAllChats = async () => { + try { + setLoading(true); + const { isSuccess, message, data } = await postService( + "/orders/user-order-messages/", + { + ...filters, + } + ); + if (isSuccess) { + setOrderChats(data?.data?.orders); + setTotalUnreadMessages(data?.data?.total_unread_messages_count); + } + } finally { + setLoading(false); + } }; useEffect(() => { @@ -100,14 +220,23 @@ export default function BusinessMessages() { > - - {orderChats?.length} Orders - + + {orderChats?.length} Orders + + {loading && } + {orderChats?.length > 0 ? ( <> {orderChats?.map((orderChat) => { @@ -136,17 +265,34 @@ export default function BusinessMessages() { })} ) : ( - - No Orders - + <> + {loading ? ( + + + + ) : ( + + No Orders + + )} + )} diff --git a/src/ui/app/components/navbar/index.tsx b/src/ui/app/components/navbar/index.tsx index e67f553d..4535896c 100644 --- a/src/ui/app/components/navbar/index.tsx +++ b/src/ui/app/components/navbar/index.tsx @@ -103,6 +103,8 @@ const MenuItemsComponent = ({ items }: { items: string[] }) => { const cart = useAppSelector((state) => state.cart); const user = useAppSelector((state) => state.user); const pathname = usePathname(); + const [unreadMessageCount, setUnreadMessageCount] = React.useState(0); + return items ? ( <> {items?.map((key: string) => { @@ -145,7 +147,9 @@ const MenuItemsComponent = ({ items }: { items: string[] }) => { color: "secondary.main", }} > - + {item?.label} ) : ( @@ -172,7 +176,17 @@ const MenuItemsComponent = ({ items }: { items: string[] }) => { /> ) : item?.route.includes("/notifications") ? ( - + + ) : item?.route.includes("/messages") ? ( + + {item?.label} + ) : ( state.user)?.user; + const [loading, setLoading] = React.useState(false); const [orderChats, setOrderChats] = React.useState([]); const [totalUnreadMessages, setTotalUnreadMessages] = React.useState(0); const [selectedOrderChat, setSelectedOrderChat] = @@ -32,18 +33,134 @@ export default function BusinessMessages() { ], }); - const getAllChats = async () => { + // User Guide for the very first order + const [stepIndex, setStepIndex] = useState(0); + const [run, setRun] = useState(false); + const [hasAMessage, setHasAMessage] = useState(false); + const [steps, setSteps] = useState([ + { + content: ( + + bgimg + + Chat with businesses. + + + This tour will guide you through the chatting room where you can + message the business owner and have a short discussion about the + order they've placed. + + + ), + placement: "center", + target: "body", + }, + { + content: ( + + + Customized filters. + + + Advanced filters for chats based on the services, order ID, and + status of order. + + + ), + placement: "right", + target: ".joyride-message-filters", + }, + { + content: ( + + + Businesses List. + + + Click on the business to chat with them and have a discussion about + the order. + + + ), + placement: "right", + target: ".joyride-user-chats", + }, + { + content: ( + + bgimg + + Congratulations!!! + + + You've completed your messages tour, you're good to go and chat with + the business. + + + ), + placement: "center", + target: "body", + }, + ]); + + const handleJoyrideCallback = (data: any) => { + const { action, index, status, type } = data; + + if ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND].includes(type)) { + // Update state to advance the tour + setStepIndex(index + (action === ACTIONS.PREV ? -1 : 1)); + } else if ([STATUS.FINISHED, STATUS.SKIPPED].includes(status)) { + // Need to set our running state to false, so we can restart if we click start again. + setRun(false); + } + }; + + const handleUserInteraction = async () => { const { isSuccess, message, data } = await postService( "/orders/user-order-messages/", - { - ...filters, - } + {} ); if (isSuccess) { - setOrderChats(data?.data?.orders); - setTotalUnreadMessages(data?.data?.total_unread_messages_count); - } else { - notification(message ? message : "Something went wrong", "error"); + // Fetching all user-message and if there's exactly 1 object, show the user guide + if (data?.data?.orders?.length == 1) { + setStepIndex(0); + setRun(true); + } + if (data?.data?.orders?.length > 0) { + setHasAMessage(true); + } + } + }; + + const getAllChats = async () => { + try { + setLoading(true); + const { isSuccess, message, data } = await postService( + "/orders/user-order-messages/", + { + ...filters, + } + ); + if (isSuccess) { + setOrderChats(data?.data?.orders); + setTotalUnreadMessages(data?.data?.total_unread_messages_count); + } else { + notification(message ? message : "Something went wrong", "error"); + } + } finally { + setLoading(false); } }; @@ -78,14 +195,23 @@ export default function BusinessMessages() { > - - {orderChats?.length} Orders - + + {orderChats?.length} Orders + + {loading && } + {orderChats?.length > 0 ? ( <> {orderChats?.map((orderChat) => { @@ -121,17 +247,34 @@ export default function BusinessMessages() { })} ) : ( - - No Orders - + <> + {loading ? ( + + + + ) : ( + + No Orders + + )} + )} diff --git a/src/ui/src/components/notificationPanel/index.tsx b/src/ui/src/components/notificationPanel/index.tsx index b3709254..ef9618d3 100644 --- a/src/ui/src/components/notificationPanel/index.tsx +++ b/src/ui/src/components/notificationPanel/index.tsx @@ -23,7 +23,13 @@ import { useRouter } from "next/navigation"; import React, { useEffect, useRef, useState } from "react"; import { notification } from "../shared/notification"; -export default function NotificationPanel() { +type NotificationPanelProps = { + setUnreadMessageCount: (count: number) => void; +}; + +export default function NotificationPanel({ + setUnreadMessageCount, +}: NotificationPanelProps) { const router = useRouter(); const [unreadCount, setUnreadCount] = useState(0); const [notificationsAnchor, setNotificationsAnchor] = React.useState(null); @@ -54,6 +60,7 @@ export default function NotificationPanel() { total_data_count: data?.pagination?.total_data_count, total_page_count: data?.pagination?.total_page_count, }); + setUnreadMessageCount(data?.data?.unread_message_count); } else { } } finally { @@ -98,7 +105,7 @@ export default function NotificationPanel() { const handleClickNotifications = React.useCallback((event: any) => { setNotificationsAnchor(event.currentTarget); }, []); - + const handleCloseNotifications = () => { setNotificationsAnchor(null); }; diff --git a/src/ui/src/utils/types.ts b/src/ui/src/utils/types.ts index e3ce33b7..5ba33c4a 100644 --- a/src/ui/src/utils/types.ts +++ b/src/ui/src/utils/types.ts @@ -223,6 +223,8 @@ type OrderItemType = { publish_date?: string; published_tweet_id?: string; status?: string; + approved?: boolean; + is_verified?: boolean; }; type TransactionType = { From b8346cc6dadf082fc1beb7e5dd0aca667b955bca Mon Sep 17 00:00:00 2001 From: Mudit Mahajan Date: Tue, 26 Mar 2024 14:49:52 +0530 Subject: [PATCH 007/157] Update order item tracking status to 'failed' --- src/api/marketplace/orders/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/marketplace/orders/tasks.py b/src/api/marketplace/orders/tasks.py index 529a2112..c1c6016f 100644 --- a/src/api/marketplace/orders/tasks.py +++ b/src/api/marketplace/orders/tasks.py @@ -338,7 +338,7 @@ def twitter_task(order_item_id): # Create a order item tracking for the order item create_order_item_tracking( - order_item=order_item, status=order_item.status) + order_item=order_item, status='failed') create_notification_for_order_item( order_item=order_item, old_status='scheduled', new_status='failed') From 6398b7f80ecf52fab2810f43586c7e85d0c52f27 Mon Sep 17 00:00:00 2001 From: Parikshit85 Date: Tue, 26 Mar 2024 14:53:07 +0530 Subject: [PATCH 008/157] Add: reward models and changes to referral app --- src/api/marketplace/accounts/serializers.py | 2 +- src/api/marketplace/marketplace/settings.py | 2 +- src/api/marketplace/marketplace/urls.py | 2 +- src/api/marketplace/marketplace/views.py | 2 +- src/api/marketplace/referrals/admin.py | 7 -- src/api/marketplace/referrals/apps.py | 6 - src/api/marketplace/referrals/models.py | 55 --------- .../{referrals => reward}/__init__.py | 0 src/api/marketplace/reward/admin.py | 8 ++ src/api/marketplace/reward/apps.py | 6 + .../reward/migrations/0001_initial.py | 105 ++++++++++++++++++ ...nfig_reward_config_reward_type_and_more.py | 29 +++++ .../migrations/__init__.py | 0 src/api/marketplace/reward/models.py | 60 ++++++++++ .../{referrals => reward}/serializers.py | 0 .../{referrals => reward}/tests.py | 0 .../marketplace/{referrals => reward}/urls.py | 0 .../{referrals => reward}/views.py | 0 .../profile/[id]/_referrals/index.tsx | 38 ++++++- src/ui/app/login/page.tsx | 2 +- 20 files changed, 250 insertions(+), 74 deletions(-) delete mode 100644 src/api/marketplace/referrals/admin.py delete mode 100644 src/api/marketplace/referrals/apps.py delete mode 100644 src/api/marketplace/referrals/models.py rename src/api/marketplace/{referrals => reward}/__init__.py (100%) create mode 100644 src/api/marketplace/reward/admin.py create mode 100644 src/api/marketplace/reward/apps.py create mode 100644 src/api/marketplace/reward/migrations/0001_initial.py create mode 100644 src/api/marketplace/reward/migrations/0002_remove_rewardconfig_reward_config_reward_type_and_more.py rename src/api/marketplace/{referrals => reward}/migrations/__init__.py (100%) create mode 100644 src/api/marketplace/reward/models.py rename src/api/marketplace/{referrals => reward}/serializers.py (100%) rename src/api/marketplace/{referrals => reward}/tests.py (100%) rename src/api/marketplace/{referrals => reward}/urls.py (100%) rename src/api/marketplace/{referrals => reward}/views.py (100%) diff --git a/src/api/marketplace/accounts/serializers.py b/src/api/marketplace/accounts/serializers.py index f03fdbba..29f47b91 100644 --- a/src/api/marketplace/accounts/serializers.py +++ b/src/api/marketplace/accounts/serializers.py @@ -5,7 +5,7 @@ from core.serializers import LanguageMasterSerializer, RegionMasterSerializer from orders.models import Order, OrderItem, Review from packages.models import Package, Service -from referrals.serializers import UserReferralsSerializer +from reward.serializers import UserReferralsSerializer from .models import ( AccountLanguage, AccountRegion, diff --git a/src/api/marketplace/marketplace/settings.py b/src/api/marketplace/marketplace/settings.py index a49d211f..a07c13fc 100644 --- a/src/api/marketplace/marketplace/settings.py +++ b/src/api/marketplace/marketplace/settings.py @@ -157,7 +157,7 @@ "packages", "core", "orders", - "referrals", + "reward", "notifications", "corsheaders", "drf_yasg", diff --git a/src/api/marketplace/marketplace/urls.py b/src/api/marketplace/marketplace/urls.py index aea785a1..85068c63 100644 --- a/src/api/marketplace/marketplace/urls.py +++ b/src/api/marketplace/marketplace/urls.py @@ -43,7 +43,7 @@ path("core/", include('core.urls'), name='core'), path("orders/", include('orders.urls')), path("account/", include('accounts.urls')), - path("referrals/", include('referrals.urls')), + path("reward/", include('reward.urls')), path("notifications/", include('notifications.urls')), path("auth-twitter-user///", views.authTwitterUser, name="auth-twitter-user"), path( diff --git a/src/api/marketplace/marketplace/views.py b/src/api/marketplace/marketplace/views.py index 42d70dd3..e9fd786c 100644 --- a/src/api/marketplace/marketplace/views.py +++ b/src/api/marketplace/marketplace/views.py @@ -19,7 +19,7 @@ import hashlib from requests_oauthlib import OAuth2Session import json -from referrals.models import ReferralRewardsMaster, UserReferrals +from reward.models import UserReferrals logger = logging.getLogger(__name__) diff --git a/src/api/marketplace/referrals/admin.py b/src/api/marketplace/referrals/admin.py deleted file mode 100644 index db6da8e2..00000000 --- a/src/api/marketplace/referrals/admin.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.contrib import admin - -from .models import UserReferrals, ReferralRewardsMaster -# Register your models here. - -admin.site.register(UserReferrals) -admin.site.register(ReferralRewardsMaster) \ No newline at end of file diff --git a/src/api/marketplace/referrals/apps.py b/src/api/marketplace/referrals/apps.py deleted file mode 100644 index cf262422..00000000 --- a/src/api/marketplace/referrals/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class ReferralsConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'referrals' \ No newline at end of file diff --git a/src/api/marketplace/referrals/models.py b/src/api/marketplace/referrals/models.py deleted file mode 100644 index 742ae168..00000000 --- a/src/api/marketplace/referrals/models.py +++ /dev/null @@ -1,55 +0,0 @@ -from django.db import models -import uuid -from accounts.models import User - -# Create your models here. - -class UserReferrals(models.Model): - id = models.UUIDField( - primary_key=True, - verbose_name="User Referral ID", - default=uuid.uuid4, - editable=False, - ) - user_account = models.OneToOneField( - User, - related_name="referral_user_account", - on_delete=models.SET_NULL, - null=True, - blank=True, - ) - referred_by = models.OneToOneField(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='referred_by_account') - - class Meta: - db_table = "user_referrals" - - def __str__(self): - return f"{self.user_account.username} referrals" - - -class ReferralRewardsMaster(models.Model): - TYPE_CHOICES = ( - ('comission', 'comission'), - ('discount', 'discount') - ) - id = models.UUIDField( - primary_key=True, - verbose_name="User Referral Rewards ID", - default=uuid.uuid4, - editable=False, - ) - # type, percentage, deleted, created_at, deleted_at - type = models.CharField(choices=TYPE_CHOICES, - max_length=50, default='service') - percentage = models.DecimalField( - max_digits=6, decimal_places=3, blank=True, null=True) - deleted = models.BooleanField(default=False, blank=True, null=True) - created_at = models.DateTimeField(auto_now_add=True) - deleted_at = models.DateTimeField(blank=True, null=True) - - class Meta: - db_table = "referral_rewards_master" - - def __str__(self): - return f"{self.type} type" - diff --git a/src/api/marketplace/referrals/__init__.py b/src/api/marketplace/reward/__init__.py similarity index 100% rename from src/api/marketplace/referrals/__init__.py rename to src/api/marketplace/reward/__init__.py diff --git a/src/api/marketplace/reward/admin.py b/src/api/marketplace/reward/admin.py new file mode 100644 index 00000000..db42c00f --- /dev/null +++ b/src/api/marketplace/reward/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin + +from .models import UserReferrals, RewardConfig, RewardTypes +# Register your models here. + +admin.site.register(UserReferrals) +admin.site.register(RewardTypes) +admin.site.register(RewardConfig) \ No newline at end of file diff --git a/src/api/marketplace/reward/apps.py b/src/api/marketplace/reward/apps.py new file mode 100644 index 00000000..b0573cd8 --- /dev/null +++ b/src/api/marketplace/reward/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class RewardConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "reward" diff --git a/src/api/marketplace/reward/migrations/0001_initial.py b/src/api/marketplace/reward/migrations/0001_initial.py new file mode 100644 index 00000000..47ceeba2 --- /dev/null +++ b/src/api/marketplace/reward/migrations/0001_initial.py @@ -0,0 +1,105 @@ +# Generated by Django 4.2.7 on 2024-03-26 08:57 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="RewardTypes", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + verbose_name="User Referral ID", + ), + ), + ("name", models.CharField(blank=True, max_length=255, null=True)), + ], + options={ + "db_table": "reward_types", + }, + ), + migrations.CreateModel( + name="UserReferrals", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + verbose_name="User Referral ID", + ), + ), + ( + "referred_by", + models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="referred_by_account", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "user_account", + models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="referral_user_account", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "db_table": "user_referrals", + }, + ), + migrations.CreateModel( + name="RewardConfig", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + verbose_name="User Referral ID", + ), + ), + ("count", models.IntegerField(blank=True, default=1, null=True)), + ("type", models.CharField(blank=True, max_length=255, null=True)), + ("reward_point", models.IntegerField(blank=True, null=True)), + ( + "reward_config_reward_type", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="reward.rewardtypes", + ), + ), + ], + options={ + "db_table": "reward_config", + }, + ), + ] diff --git a/src/api/marketplace/reward/migrations/0002_remove_rewardconfig_reward_config_reward_type_and_more.py b/src/api/marketplace/reward/migrations/0002_remove_rewardconfig_reward_config_reward_type_and_more.py new file mode 100644 index 00000000..0b830fab --- /dev/null +++ b/src/api/marketplace/reward/migrations/0002_remove_rewardconfig_reward_config_reward_type_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.7 on 2024-03-26 09:07 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("reward", "0001_initial"), + ] + + operations = [ + migrations.RemoveField( + model_name="rewardconfig", + name="reward_config_reward_type", + ), + migrations.AddField( + model_name="rewardconfig", + name="reward_type", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="reward_config_reward_type", + to="reward.rewardtypes", + ), + ), + ] diff --git a/src/api/marketplace/referrals/migrations/__init__.py b/src/api/marketplace/reward/migrations/__init__.py similarity index 100% rename from src/api/marketplace/referrals/migrations/__init__.py rename to src/api/marketplace/reward/migrations/__init__.py diff --git a/src/api/marketplace/reward/models.py b/src/api/marketplace/reward/models.py new file mode 100644 index 00000000..c16ef2f2 --- /dev/null +++ b/src/api/marketplace/reward/models.py @@ -0,0 +1,60 @@ +from django.db import models +import uuid +from accounts.models import User +from django.db.models import SET_NULL + +# Create your models here. + +class RewardTypes(models.Model): + id = models.UUIDField( + primary_key=True, + verbose_name="User Referral ID", + default=uuid.uuid4, + editable=False, + ) + name=models.CharField(max_length=255, blank=True, null=True) + class Meta: + db_table = "reward_types" + + def __str__(self): + return self.name + +class RewardConfig(models.Model): + id = models.UUIDField( + primary_key=True, + verbose_name="User Referral ID", + default=uuid.uuid4, + editable=False, + ) + count=models.IntegerField(default=1, blank=True, null=True) + type=models.CharField(max_length=255, blank=True, null=True) + reward_point=models.IntegerField(blank=True, null=True) + reward_type=models.ForeignKey(RewardTypes, related_name="reward_config_reward_type",on_delete=SET_NULL, null=True, blank=True) + + class Meta: + db_table = "reward_config" + + def __str__(self): + return f"{self.count} number of {self.type} of master type {self.reward_type.name}" + +class UserReferrals(models.Model): + id = models.UUIDField( + primary_key=True, + verbose_name="User Referral ID", + default=uuid.uuid4, + editable=False, + ) + user_account = models.OneToOneField( + User, + related_name="referral_user_account", + on_delete=models.SET_NULL, + null=True, + blank=True, + ) + referred_by = models.OneToOneField(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='referred_by_account') + + class Meta: + db_table = "user_referrals" + + def __str__(self): + return f"{self.user_account.username} referrals" diff --git a/src/api/marketplace/referrals/serializers.py b/src/api/marketplace/reward/serializers.py similarity index 100% rename from src/api/marketplace/referrals/serializers.py rename to src/api/marketplace/reward/serializers.py diff --git a/src/api/marketplace/referrals/tests.py b/src/api/marketplace/reward/tests.py similarity index 100% rename from src/api/marketplace/referrals/tests.py rename to src/api/marketplace/reward/tests.py diff --git a/src/api/marketplace/referrals/urls.py b/src/api/marketplace/reward/urls.py similarity index 100% rename from src/api/marketplace/referrals/urls.py rename to src/api/marketplace/reward/urls.py diff --git a/src/api/marketplace/referrals/views.py b/src/api/marketplace/reward/views.py similarity index 100% rename from src/api/marketplace/referrals/views.py rename to src/api/marketplace/reward/views.py diff --git a/src/ui/app/influencer/profile/[id]/_referrals/index.tsx b/src/ui/app/influencer/profile/[id]/_referrals/index.tsx index 021c1692..32700134 100644 --- a/src/ui/app/influencer/profile/[id]/_referrals/index.tsx +++ b/src/ui/app/influencer/profile/[id]/_referrals/index.tsx @@ -29,7 +29,7 @@ export default function Referrals({}: Props) { const getUserReferralLink = async () => { const { isSuccess, message, data } = await getService( - `/referrals/referral-link/` + `/reward/referral-link/` ); if (isSuccess) { setReferralLink(data?.data?.referralLink); @@ -109,6 +109,42 @@ export default function Referrals({}: Props) { + {/* Section for influencer's you've (current user) referred */} + + + My Referrals + + + + + + Influencer + Date of Joining + Rewards + + + + + + @loremipsumdolor + + 25, sept 2023 + 10 $ + + +
+
+
{/* Right Side Container */} diff --git a/src/ui/app/login/page.tsx b/src/ui/app/login/page.tsx index fa8dc404..af622512 100644 --- a/src/ui/app/login/page.tsx +++ b/src/ui/app/login/page.tsx @@ -87,7 +87,7 @@ const Login: React.FC = () => { const checkReferralCode = async () => { try { const { isSuccess, data } = await getService( - `referrals/check-referral-validity/?referral_code=${referralCode}` + `reward/check-referral-validity/?referral_code=${referralCode}` ); if (isSuccess) { notification("Referral Code is Valid"); From bd71dd52e99f1a0cd3a0f7dec8b535780e04f148 Mon Sep 17 00:00:00 2001 From: Mudit Mahajan Date: Tue, 26 Mar 2024 14:55:58 +0530 Subject: [PATCH 009/157] Update email verification message in OTPAuthV2 view --- src/api/marketplace/accounts/views.py | 4 ++-- src/ui/src/components/verifyEmailModal/index.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/marketplace/accounts/views.py b/src/api/marketplace/accounts/views.py index 1e36857b..5179b6d1 100644 --- a/src/api/marketplace/accounts/views.py +++ b/src/api/marketplace/accounts/views.py @@ -1290,9 +1290,9 @@ def post(self, request): user.save() sendEmail.delay( - "OTP for login to Xfluencer", + "OTP for email verification", "Your OTP is " + str(otp), - "loginEmail.html", + "verifyEmail.html", {"otp": otp, "target": config("FRONT_END_URL")}, [request.data["email"]], ) diff --git a/src/ui/src/components/verifyEmailModal/index.tsx b/src/ui/src/components/verifyEmailModal/index.tsx index eb571822..9bab3ad7 100644 --- a/src/ui/src/components/verifyEmailModal/index.tsx +++ b/src/ui/src/components/verifyEmailModal/index.tsx @@ -235,7 +235,7 @@ export default function VerifyEmailModal({ fullWidth onClick={verifyOTP} > - Login + Submit Date: Tue, 26 Mar 2024 16:38:58 +0530 Subject: [PATCH 010/157] Add: user referral, reward type, reward config and reward point models --- src/api/marketplace/marketplace/views.py | 63 ++++++++++--- src/api/marketplace/reward/admin.py | 5 +- ...lter_userreferrals_referred_by_and_more.py | 89 +++++++++++++++++++ src/api/marketplace/reward/models.py | 32 ++++++- 4 files changed, 172 insertions(+), 17 deletions(-) create mode 100644 src/api/marketplace/reward/migrations/0003_alter_userreferrals_referred_by_and_more.py diff --git a/src/api/marketplace/marketplace/views.py b/src/api/marketplace/marketplace/views.py index e9fd786c..0f309054 100644 --- a/src/api/marketplace/marketplace/views.py +++ b/src/api/marketplace/marketplace/views.py @@ -19,7 +19,7 @@ import hashlib from requests_oauthlib import OAuth2Session import json -from reward.models import UserReferrals +from reward.models import RewardConfig, RewardPoints, UserReferrals logger = logging.getLogger(__name__) @@ -216,6 +216,52 @@ def manage_categories(hashtags, twitter_account): ) new_account_category.save() +def manage_referrals(referral_code, new_user_account, current_twitter_user): + # Get user account based on referral_code + try: + referred_by = User.objects.get(referral_code=referral_code) + if referred_by: + # Who referred whom + + # Based on current_twitter_user.followers_count fetch which reward config the person falls in and give them points + reward_config = RewardConfig.objects.filter( + reward_type__name="referrals", + count__gte=current_twitter_user.followers_count + ) + if reward_config is None: + reward_config = RewardConfig.objects.filter( + reward_type__name="referrals", + ) + + reward_config = reward_config.order_by("count").first() + + # Give reward to the new user. + RewardPoints.objects.create( + user_account=new_user_account, + points=reward_config.reward_point, + reward_configuration=reward_config, + ) + + # Reward to the person who's referral code was used. + referred_by_reward_point = RewardPoints.objects.create( + user_account=referred_by, + points=reward_config.reward_point, + reward_configuration=reward_config + ) + + UserReferrals.objects.create( + user_account=new_user_account, + referred_by=referred_by, + # Mapping the reward point about who's referral code was used. + referred_by_reward_point=referred_by_reward_point) + return None + + except Exception as e: + return { + "status": "error", + "message": f"No referral given, invalid referral code - {referral_code}.", + } + def createUser(userData, access_token, role, refresh_token, referral_code): try: is_new_user = False @@ -288,18 +334,9 @@ def createUser(userData, access_token, role, refresh_token, referral_code): # New user login with referral_code - if referral_code: - # Get user account based on referral_code - try: - referred_by = User.objects.get(referral_code=referral_code) - if referred_by: - # Who referred whom - UserReferrals.objects.create(user_account=new_user_account, referred_by=referred_by) - - except Exception as e: - return { - "status": "error", - "message": f"No referral given, invalid referral code - {referral_code}.", - } + ref = manage_referrals(referral_code, new_user_account, current_twitter_user) + if ref is not None: + return ref else: if existing_user_account.role.name != role: return { diff --git a/src/api/marketplace/reward/admin.py b/src/api/marketplace/reward/admin.py index db42c00f..c4c6f5c8 100644 --- a/src/api/marketplace/reward/admin.py +++ b/src/api/marketplace/reward/admin.py @@ -1,8 +1,9 @@ from django.contrib import admin -from .models import UserReferrals, RewardConfig, RewardTypes +from .models import UserReferrals, RewardConfig, RewardTypes, RewardPoints # Register your models here. admin.site.register(UserReferrals) admin.site.register(RewardTypes) -admin.site.register(RewardConfig) \ No newline at end of file +admin.site.register(RewardConfig) +admin.site.register(RewardPoints) \ No newline at end of file diff --git a/src/api/marketplace/reward/migrations/0003_alter_userreferrals_referred_by_and_more.py b/src/api/marketplace/reward/migrations/0003_alter_userreferrals_referred_by_and_more.py new file mode 100644 index 00000000..0c33e9ec --- /dev/null +++ b/src/api/marketplace/reward/migrations/0003_alter_userreferrals_referred_by_and_more.py @@ -0,0 +1,89 @@ +# Generated by Django 4.2.7 on 2024-03-26 11:08 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("reward", "0002_remove_rewardconfig_reward_config_reward_type_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="userreferrals", + name="referred_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="referred_by_account", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="userreferrals", + name="user_account", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="referral_user_account", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.CreateModel( + name="RewardPoints", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + verbose_name="User Referral ID", + ), + ), + ("points", models.IntegerField(blank=True, null=True)), + ( + "reward_configuration", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="reward_point_reward_config", + to="reward.rewardconfig", + ), + ), + ( + "user_account", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="reward_point_user_account", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "db_table": "reward_points", + }, + ), + migrations.AddField( + model_name="userreferrals", + name="referred_by_reward_point", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="user_referral_reward_point", + to="reward.rewardpoints", + ), + ), + ] diff --git a/src/api/marketplace/reward/models.py b/src/api/marketplace/reward/models.py index c16ef2f2..44c08b5c 100644 --- a/src/api/marketplace/reward/models.py +++ b/src/api/marketplace/reward/models.py @@ -36,6 +36,25 @@ class Meta: def __str__(self): return f"{self.count} number of {self.type} of master type {self.reward_type.name}" + +class RewardPoints(models.Model): + id = models.UUIDField( + primary_key=True, + verbose_name="User Referral ID", + default=uuid.uuid4, + editable=False, + ) + user_account = models.ForeignKey( + User, + related_name="reward_point_user_account", + on_delete=models.SET_NULL, + null=True, + blank=True, + ) + points=models.IntegerField(blank=True, null=True) + reward_configuration=models.ForeignKey(RewardConfig, related_name="reward_point_reward_config",on_delete=SET_NULL, null=True, blank=True) + class Meta: + db_table = "reward_points" class UserReferrals(models.Model): id = models.UUIDField( @@ -44,14 +63,23 @@ class UserReferrals(models.Model): default=uuid.uuid4, editable=False, ) - user_account = models.OneToOneField( + user_account = models.ForeignKey( User, related_name="referral_user_account", on_delete=models.SET_NULL, null=True, blank=True, ) - referred_by = models.OneToOneField(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='referred_by_account') + referred_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='referred_by_account') + + # The one who's referral code was used + referred_by_reward_point = models.ForeignKey( + RewardPoints, + related_name="user_referral_reward_point", + on_delete=models.SET_NULL, + null=True, + blank=True, + ) class Meta: db_table = "user_referrals" From ecff68f4fa49ef9872527fb749f2fec2c6e031f0 Mon Sep 17 00:00:00 2001 From: Mudit Mahajan Date: Tue, 26 Mar 2024 16:51:13 +0530 Subject: [PATCH 011/157] Add ManualVerification API --- src/api/marketplace/orders/serializers.py | 12 ++++ src/api/marketplace/orders/services.py | 14 +++++ src/api/marketplace/orders/urls.py | 3 + src/api/marketplace/orders/views.py | 71 ++++++++++++++++++++++- 4 files changed, 97 insertions(+), 3 deletions(-) diff --git a/src/api/marketplace/orders/serializers.py b/src/api/marketplace/orders/serializers.py index 7612cf57..7c0e72dd 100644 --- a/src/api/marketplace/orders/serializers.py +++ b/src/api/marketplace/orders/serializers.py @@ -1,6 +1,7 @@ from email import message from accounts.serializers import BusinessAccountMetaDataSerializer, UserSerializer, WalletCompleteSerializer from accounts.models import BusinessAccountMetaData, User, Wallet +from core.models import Configuration from orders.services import create_order_item_meta_data_field_update_message, create_order_item_publish_date_update_message, create_order_item_status_update_message, create_order_item_tracking from core.serializers import CurrencySerializer from packages.serializers import PackageSerializer, ServiceMasterReadSerializer @@ -20,6 +21,8 @@ ) from django.core.exceptions import ObjectDoesNotExist from django.utils import timezone +from datetime import timedelta + class OrderItemMetaDataSerializer(serializers.ModelSerializer): class Meta: @@ -40,6 +43,7 @@ class OrderItemReadSerializer(serializers.ModelSerializer): currency = CurrencySerializer(read_only=True) order_item_meta_data = serializers.SerializerMethodField() order_id = OrderReadSerializer(read_only=True) + allow_manual_approval = serializers.SerializerMethodField() class Meta: model = OrderItem @@ -53,6 +57,10 @@ def get_order_item_meta_data(self, obj): order_item_meta_data = OrderItemMetaData.objects.filter(order_item=obj) return OrderItemMetaDataSerializer(order_item_meta_data, many=True).data + def get_allow_manual_approval(self, obj): + COUNTDOWN_TIME_FOR_VALIDATION = int(Configuration.objects.get( + key='countdown_time_for_validation').value) + return obj.status == "published" and (not obj.is_verified) and obj.publish_date < (timezone.now() - timedelta(seconds=COUNTDOWN_TIME_FOR_VALIDATION)) # Not being used class ReviewSerializer(serializers.ModelSerializer): @@ -508,3 +516,7 @@ class OrderItemMetricSerializer(serializers.ModelSerializer): class Meta: model = OrderItemMetric fields = '__all__' + + +class ManualVerifyOrderItemSerializer(serializers.Serializer): + published_post_link = serializers.CharField(required=False) diff --git a/src/api/marketplace/orders/services.py b/src/api/marketplace/orders/services.py index 39fffdf5..c020df13 100644 --- a/src/api/marketplace/orders/services.py +++ b/src/api/marketplace/orders/services.py @@ -318,3 +318,17 @@ def create_order_item_meta_data_field_update_message(order_item_meta_data, updat logger.error( "Error creating order item meta data field update message: ", str(e)) return False + +def create_manual_verification_notification(order_item): + try: + buyer = order_item.order_id.buyer + influencer = order_item.package.influencer + + message = f'Order Item: {order_item.package.name} has been manually verified by {buyer.username}.' + title = 'Order Item Manually Verified' + Notification.objects.create( + user=influencer, message=message, title=title, slug=INFLUENCER_DASHBOARD_URL) + except Exception as e: + logger.error( + "Error creating manual verification notification: ", str(e)) + return False \ No newline at end of file diff --git a/src/api/marketplace/orders/urls.py b/src/api/marketplace/orders/urls.py index 53c8e95f..8f3ad77d 100644 --- a/src/api/marketplace/orders/urls.py +++ b/src/api/marketplace/orders/urls.py @@ -3,6 +3,7 @@ ApproveOrderItemView, CancelOrderView, CancelTweetView, + ManualVerifyOrderItemView, OrderItemMetricDetailView, OrderList, OrderDetail, @@ -57,4 +58,6 @@ name="order-item-metrics"), path("approve-ordder-item//", ApproveOrderItemView.as_view(), name="approve-order-item"), + path("order-item/verify//", + ManualVerifyOrderItemView.as_view(), name="verify-order-item"), ] \ No newline at end of file diff --git a/src/api/marketplace/orders/views.py b/src/api/marketplace/orders/views.py index 5b6c98ac..7110a309 100644 --- a/src/api/marketplace/orders/views.py +++ b/src/api/marketplace/orders/views.py @@ -1,6 +1,6 @@ from accounts.models import Wallet -from orders.tasks import cancel_escrow, cancel_tweet, schedule_tweet -from orders.services import create_notification_for_order, create_order_item_approval_notification, create_order_item_tracking, create_order_tracking +from orders.tasks import cancel_escrow, cancel_tweet, check_order_status, schedule_tweet +from orders.services import create_manual_verification_notification, create_notification_for_order, create_order_item_approval_notification, create_order_item_tracking, create_order_tracking from marketplace.authentication import JWTAuthentication from marketplace.services import ( Pagination, @@ -28,6 +28,7 @@ ApproveOrderItemSerializer, CreateOrderMessageSerializer, CreateOrderSerializer, + ManualVerifyOrderItemSerializer, OrderItemListFilterSerializer, OrderItemMetricSerializer, OrderItemReadSerializer, @@ -955,7 +956,7 @@ def get(self, request, pk): orderItem = self.get_object(pk) if orderItem is None: return handleNotFound("Order Item") - serializer = OrderItemSerializer(orderItem) + serializer = OrderItemReadSerializer(orderItem) return Response( { "isSuccess": True, @@ -1562,3 +1563,67 @@ def post(self, request): except Exception as e: return handleServerException(e) + + +class ManualVerifyOrderItemView(APIView): + authentication_classes = [JWTAuthentication] + + @swagger_auto_schema(request_body=ManualVerifyOrderItemSerializer) + def put(self, request, pk): + try: + order_item = OrderItem.objects.get(pk=pk) + if order_item is None: + return handleNotFound("Order Item") + + # Check that the logged in user is the buyer of the order + if request.user_account.role.name != "business_owner" or order_item.order_id.buyer.id != request.user_account.id: + return Response( + { + "isSuccess": False, + "message": "You are not authorized to verify this order item", + "data": None, + "errors": "You are not authorized to verify this order item", + }, + status=status.HTTP_403_FORBIDDEN, + ) + + # Also check that the order_item is in accepted or cancelled state + if order_item.status not in ["published"]: + return Response( + { + "isSuccess": False, + "message": "Order item is already " + order_item.status, + "data": None, + "errors": "Order item is already " + order_item.status, + }, + status=status.HTTP_400_BAD_REQUEST, + ) + + # Get the data from serializer + serializer = ManualVerifyOrderItemSerializer( + instance=order_item, data=request.data, partial=True + ) + if serializer.is_valid(): + order_item.is_verified = True + if serializer.validated_data.get("published_post_link"): + published_link = serializer.validated_data.get( + "published_post_link") + tweet_id = published_link.split('/')[-1] + order_item.published_tweet_id = tweet_id + order_item.save() + + create_manual_verification_notification(order_item) + + check_order_status(pk=order_item.order_id.id) + return Response( + { + "isSuccess": True, + "data": None, + "message": "Order Item verified successfully", + }, + status=status.HTTP_200_OK, + ) + else: + return handleBadRequest(serializer.errors) + except Exception as e: + return handleServerException(e) From c5e96d4de8b5987888d2eda0ed1652e5549d0b22 Mon Sep 17 00:00:00 2001 From: Mudit Mahajan Date: Tue, 26 Mar 2024 16:51:31 +0530 Subject: [PATCH 012/157] Integrate Manual Verification API + UI for Approval Modal --- src/ui/app/business/dashboard/page.tsx | 41 +++- .../manualVerifyModal/index.tsx | 210 ++++++++++++++++++ src/ui/src/utils/types.ts | 1 + 3 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 src/ui/src/components/dashboardComponents/manualVerifyModal/index.tsx diff --git a/src/ui/app/business/dashboard/page.tsx b/src/ui/app/business/dashboard/page.tsx index 2266c346..caa5a9f2 100644 --- a/src/ui/app/business/dashboard/page.tsx +++ b/src/ui/app/business/dashboard/page.tsx @@ -33,6 +33,7 @@ import OpenInNewIcon from "@mui/icons-material/OpenInNew"; import ScheduleSendIcon from "@mui/icons-material/ScheduleSend"; import { Box, + Button, CircularProgress, Grid, IconButton, @@ -62,6 +63,8 @@ import CheckCircleOutlineOutlinedIcon from "@mui/icons-material/CheckCircleOutli import MessageIcon from "@mui/icons-material/Message"; import CheckCircleIcon from "@mui/icons-material/CheckCircle"; import CancelIcon from "@mui/icons-material/Cancel"; +import RuleOutlinedIcon from "@mui/icons-material/RuleOutlined"; +import ManualVerifyModal from "@/src/components/dashboardComponents/manualVerifyModal"; const tabs = [ { @@ -84,6 +87,8 @@ export default function BusinessDashboardPage() { const [loading, setLoading] = useState(false); const [orders, setOrders] = useState([]); const [orderItems, setOrderItems] = useState([]); + const [openVerifyModal, setOpenVerifyModal] = useState(false); + const [selectedOrderItemId, setSelectedOrderItemId] = useState(""); const [selectedCard, setSelectedCard] = React.useState(0); const [cancelLoading, setCancelLoading] = useState(false); const [hasAnOrder, setHasAnOrder] = useState(false); @@ -1177,7 +1182,32 @@ export default function BusinessDashboardPage() { {params?.row?.is_verified ? ( ) : ( - + + {params?.row?.allow_manual_approval ? ( + + + + ) : ( + + )} + )} ); @@ -1236,6 +1266,10 @@ export default function BusinessDashboardPage() { if (!open) getOrders(); }, [open]); + useEffect(() => { + if (!openVerifyModal) getOrderItems(); + }, [openVerifyModal]); + useEffect(() => { const tab = searchParams.get("tab"); const _selectedTab = tabs.find((_tab) => _tab.key === tab); @@ -1472,6 +1506,11 @@ export default function BusinessDashboardPage() { readonly={false} updateState={getOrders} /> + {selectedTab == 0 ? ( >; +}; + +const TWITTER_POST_URL_REGEX = + "^https?:\\/\\/(twitter|x)\\.com\\/(?:#!\\/)?(\\w+)\\/status(es)?\\/(\\d+)$"; + +export default function ManualVerifyModal({ + orderItemId, + open, + setOpen, +}: ManualVerifyModalProps) { + const [orderItem, setOrderItem] = useState(null); + const [loading, setLoading] = useState(true); + const [verificationLoading, setVerificationLoading] = + useState(false); + const [published_post_link, setPublishedPostLink] = useState(); + + const getOrderItemDetails = async () => { + try { + setLoading(true); + const { isSuccess, data, message } = await getService( + `/orders/order-item/${orderItemId}/` + ); + if (isSuccess) { + setOrderItem(data?.data); + } else { + notification( + message ? message : "Something went wrong, please try again later.", + "error" + ); + } + } finally { + setLoading(false); + } + }; + + const verifyOrderItem = async () => { + try { + setVerificationLoading(true); + const regex = new RegExp(TWITTER_POST_URL_REGEX, "gm"); + if (published_post_link && !regex.test(published_post_link)) { + notification("Please enter a correct X Post Link", "error"); + return; + } + if ( + published_post_link && + !published_post_link?.includes( + orderItem?.package?.influencer?.twitter_account?.user_name! + ) + ) { + notification( + "The post link does not match the influencer's twitter account. Please enter the correct post link.", + "error", + 3000 + ); + return; + } + const { isSuccess, data, message } = await putService( + `/orders/order-item/verify/${orderItemId}/`, + { + published_post_link, + } + ); + if (isSuccess) { + notification(message); + setOpen(false); + } else { + notification( + message ? message : "Something went wrong, please try again later.", + "error" + ); + } + } finally { + setVerificationLoading(false); + } + }; + + useEffect(() => { + if (open) { + getOrderItemDetails(); + } + setPublishedPostLink(""); + }, [orderItemId, open]); + + return ( + + + + + + Manual Approval + + setOpen(false)}> + + + + + {loading ? ( + + ) : ( + <> + {orderItem ? ( + + ) : ( + + Order Item not found + + )} + + )} + + + Published Post Link (Optional) + setPublishedPostLink(e.target.value)} + sx={{ + "& .MuiOutlinedInput-root": { + borderRadius: 3, + }, + }} + size="small" + helperText="Enter the link of the published post to track order item analytics." + disabled={verificationLoading} + /> + + + + + + + + ); +} diff --git a/src/ui/src/utils/types.ts b/src/ui/src/utils/types.ts index 8a668e84..7c03c0ca 100644 --- a/src/ui/src/utils/types.ts +++ b/src/ui/src/utils/types.ts @@ -228,6 +228,7 @@ type OrderItemType = { status?: string; approved?: boolean; is_verified?: boolean; + allow_manual_approval?: boolean; }; type TransactionType = { From e42e38e5a14849b8dc4ad711c85a6605c1b77695 Mon Sep 17 00:00:00 2001 From: Mudit Mahajan Date: Tue, 26 Mar 2024 16:52:44 +0530 Subject: [PATCH 013/157] Update BUSINESS_DASHBOARD_URL to BUSINESS_ORDER_ITEM_DASHBOARD_URL --- src/api/marketplace/orders/services.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/api/marketplace/orders/services.py b/src/api/marketplace/orders/services.py index c020df13..a2456b3f 100644 --- a/src/api/marketplace/orders/services.py +++ b/src/api/marketplace/orders/services.py @@ -8,6 +8,8 @@ FRONT_END_URL = config('FRONT_END_URL') ORDERS_DASHBOARD_URL = FRONT_END_URL + 'influencer/orders' BUSINESS_DASHBOARD_URL = FRONT_END_URL + '/business/dashboard/?tab=orders' +BUSINESS_ORDER_ITEM_DASHBOARD_URL = FRONT_END_URL + \ + '/business/dashboard/?tab=order-items' INFLUENCER_DASHBOARD_URL = FRONT_END_URL + '/influencer/dashboard/?tab=orders' @@ -170,14 +172,14 @@ def create_post_verification_failed_notification(order_item): influencer = order_item.package.influencer message = 'Your order item ' + order_item.package.name + 'from order ' + order.order_code + \ - ' has failed verification by Xfluencer. Please review the order.' + ' has failed verification by Xfluencer. Please review the order item.' title = 'Order Item Verification Failed' notifications = [] notifications.append({ 'user_id': buyer.id, 'message': message, 'title': title, - 'slug': BUSINESS_DASHBOARD_URL + 'slug': BUSINESS_ORDER_ITEM_DASHBOARD_URL }) notifications.append({ 'user_id': influencer.id, From 9bee32f91cbaae4da3698e729bdfb1e71c60150f Mon Sep 17 00:00:00 2001 From: Parikshit85 Date: Tue, 26 Mar 2024 18:20:43 +0530 Subject: [PATCH 014/157] Add: UI for referrals and minor changes --- src/api/marketplace/accounts/serializers.py | 4 +- src/api/marketplace/reward/serializers.py | 10 +- src/api/marketplace/reward/urls.py | 3 +- src/api/marketplace/reward/views.py | 33 +++++ .../profile/[id]/_referrals/index.tsx | 113 +++++++++++++++--- src/ui/app/influencer/profile/[id]/page.tsx | 3 +- src/ui/public/no_data.jpg | Bin 0 -> 292246 bytes src/ui/src/utils/types.ts | 14 +++ 8 files changed, 158 insertions(+), 22 deletions(-) create mode 100644 src/ui/public/no_data.jpg diff --git a/src/api/marketplace/accounts/serializers.py b/src/api/marketplace/accounts/serializers.py index 29f47b91..732dd8a2 100644 --- a/src/api/marketplace/accounts/serializers.py +++ b/src/api/marketplace/accounts/serializers.py @@ -2,10 +2,9 @@ from marketplace.services import truncateWalletAddress from rest_framework import serializers -from core.serializers import LanguageMasterSerializer, RegionMasterSerializer +from core.serializers import LanguageMasterSerializer from orders.models import Order, OrderItem, Review from packages.models import Package, Service -from reward.serializers import UserReferralsSerializer from .models import ( AccountLanguage, AccountRegion, @@ -228,7 +227,6 @@ class UserSerializer(serializers.ModelSerializer): region = AccountRegionSerializer( read_only=True, source="region_user_account" ) - referral = UserReferralsSerializer(read_only=True, source="user_referral_account") class Meta: model = User diff --git a/src/api/marketplace/reward/serializers.py b/src/api/marketplace/reward/serializers.py index f863480c..7f107aa0 100644 --- a/src/api/marketplace/reward/serializers.py +++ b/src/api/marketplace/reward/serializers.py @@ -1,8 +1,16 @@ from rest_framework import serializers +from accounts.serializers import UserSerializer +from .models import RewardPoints, UserReferrals -from .models import UserReferrals +class RewardPointsSerializer(serializers.ModelSerializer): + class Meta: + model = RewardPoints + fields = "__all__" class UserReferralsSerializer(serializers.ModelSerializer): + referred_by = UserSerializer(read_only=True) + user_account = UserSerializer(read_only=True) + referred_by_reward_point = RewardPointsSerializer(read_only=True) class Meta: model = UserReferrals fields = "__all__" \ No newline at end of file diff --git a/src/api/marketplace/reward/urls.py b/src/api/marketplace/reward/urls.py index 36d16545..8ea83741 100644 --- a/src/api/marketplace/reward/urls.py +++ b/src/api/marketplace/reward/urls.py @@ -1,8 +1,9 @@ from django.urls import path -from .views import ReferralLink, ReferralValidity +from .views import ReferralLink, ReferralValidity, UserReferralsListView urlpatterns = [ path("check-referral-validity/", ReferralValidity.as_view(), name="referral-validity"), path("referral-link/", ReferralLink.as_view(), name="referral-link"), + path("user-referrals/", UserReferralsListView.as_view(), name="user-referrals"), ] \ No newline at end of file diff --git a/src/api/marketplace/reward/views.py b/src/api/marketplace/reward/views.py index 1b9f2387..78e8824b 100644 --- a/src/api/marketplace/reward/views.py +++ b/src/api/marketplace/reward/views.py @@ -5,8 +5,11 @@ from rest_framework.response import Response from rest_framework import status from marketplace.services import ( + Pagination, handleServerException, ) +from reward.models import UserReferrals +from reward.serializers import UserReferralsSerializer class ReferralLink(APIView): @@ -60,5 +63,35 @@ def get(self, request): }, status=status.HTTP_200_OK, ) + except Exception as e: + return handleServerException(e) + +class UserReferralsListView(APIView): + + def get_user_referrals(self, pk): + try: + return UserReferrals.objects.filter(referred_by=pk) + except Exception as e: + return handleServerException(e) + + + authentication_classes = [JWTAuthentication] + def get(self, request): + try: + user_referrals = self.get_user_referrals(pk=request.user_account.id) + + # Paginate the results + pagination = Pagination(user_referrals, request) + serializer = UserReferralsSerializer(pagination.getData(), many=True) + return Response( + { + "isSuccess": True, + "data": serializer.data, + "message": "User Referrals retrieved successfully", + "pagination": pagination.getPageInfo(), + }, + status=status.HTTP_200_OK, + ) + except Exception as e: return handleServerException(e) \ No newline at end of file diff --git a/src/ui/app/influencer/profile/[id]/_referrals/index.tsx b/src/ui/app/influencer/profile/[id]/_referrals/index.tsx index 32700134..b9dee99c 100644 --- a/src/ui/app/influencer/profile/[id]/_referrals/index.tsx +++ b/src/ui/app/influencer/profile/[id]/_referrals/index.tsx @@ -10,23 +10,47 @@ import TableContainer from "@mui/material/TableContainer"; import TableHead from "@mui/material/TableHead"; import TableRow from "@mui/material/TableRow"; import Paper from "@mui/material/Paper"; +import dayjs from "dayjs"; +import { DISPLAY_DATE_FORMAT } from "@/src/utils/consts"; +import NoData from "@/public/no_data.jpg"; +import Image from "next/image"; type Props = {}; const REFERAL_GUIDE = [ "Share your unique referral link to an influential individual in the Xfluencer community.", - "Your referral joins Xfluencer and establishes their services on the platform.", - "Upon the successful sale of their first service to a business, you earn a 1% commission from the platform fee levied on the purchasing business.", - "Additionally, your referral receives a 0.5% share of the transaction fees for their initial five orders as an Xfluencer, in addition to their listed service price.", + "Your referral joins Xfluencer they'll be receiving the rewards and you as well.", + "The rewards will be based on the number of followers the new user has in the following manner - ", ]; export default function Referrals({}: Props) { const [referralLink, setReferralLink] = React.useState(""); + const [userReferrals, setUserReferrals] = + React.useState(); React.useEffect(() => { getUserReferralLink(); + getUserReferrals(); }, []); + const getUserReferrals = async () => { + const { isSuccess, message, data } = await getService( + `/reward/user-referrals/`, + { + page_size: 15, + page_number: 1, + } + ); + if (isSuccess) { + setUserReferrals(data?.data); + } else { + notification( + message ? message : "Error fetching user referrals", + "error" + ); + } + }; + const getUserReferralLink = async () => { const { isSuccess, message, data } = await getService( `/reward/referral-link/` @@ -50,6 +74,14 @@ export default function Referrals({}: Props) { .catch((err) => console.error("Failed to copy:", err)); }; + const getTotalEarnings = () => { + if (userReferrals) { + return userReferrals.reduce((acc, curr) => { + return acc + curr?.referred_by_reward_point?.points; + }, 0); + } else return 0; + }; + return ( - 0 + {userReferrals ? userReferrals.length : 0} Total Referrals - 0 Tokens + {getTotalEarnings()} Tokens Total Earnings @@ -123,24 +155,65 @@ export default function Referrals({}: Props) { My Referrals - +
Influencer Date of Joining - Rewards + Rewards Points - - - @loremipsumdolor - - 25, sept 2023 - 10 $ - + {!userReferrals?.length ? ( + + + + + + No referral points. Start referring influencers to earn points. + + + + + ) : ( + userReferrals.map((referral) => { + return ( + + + @{referral?.user_account?.username} + + + {dayjs(referral?.user_account?.date_joined).format( + DISPLAY_DATE_FORMAT + )} + + + + {referral?.referred_by_reward_point?.points} + + + ); + }) + )}
@@ -178,6 +251,7 @@ export default function Referrals({}: Props) { ); })} +
    +
  • Upto 100 followers - 100 points
  • +
  • 1000 followers - 200points,
  • +
  • 10000 followers - 500 points
  • +
  • 100000 followers - 1000 points,
  • +
  • 1000000 followers - 10000 points.
  • +
{ + console.log("getServices getting called") try { const { message, data, isSuccess, errors } = await getService( "packages/service", diff --git a/src/ui/public/no_data.jpg b/src/ui/public/no_data.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a1264aedb2adfc24542ca6c16cdf3cf9a90c23e3 GIT binary patch literal 292246 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 literal 0 HcmV?d00001 diff --git a/src/ui/src/utils/types.ts b/src/ui/src/utils/types.ts index 812c1e02..6a90f1be 100644 --- a/src/ui/src/utils/types.ts +++ b/src/ui/src/utils/types.ts @@ -146,6 +146,20 @@ type UserType = { promoted_tweet_id?: string | null; }; +type RewardPointType = { + id: string; + points: number; + reward_configuration: string; + user_account: string; +}; + +type UserReferralsType = { + id: string; + referred_by: UserType; + user_account: UserType; + referred_by_reward_point: RewardPointType; +}; + type ServiceCheckOutType = { serviceItem: ServiceType; quantity: number; From 5baf2005bca1752ff60f114873a6ec76e288b758 Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 13:50:13 +0000 Subject: [PATCH 015/157] boxing escrow account for and vault account --- solana/programs/xfluencer/src/lib.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/solana/programs/xfluencer/src/lib.rs b/solana/programs/xfluencer/src/lib.rs index 954f3752..327c176e 100644 --- a/solana/programs/xfluencer/src/lib.rs +++ b/solana/programs/xfluencer/src/lib.rs @@ -16,7 +16,7 @@ mod processor; use crate::errors::CustomError; -declare_id!("7zNs7f6rJyhvu9k4DZwqeqgBa27GqX12mVeQAS528xEq"); +declare_id!("6mTwD92ynqwDZxjg2ydhAjeX5w7bcsfM7jQUDeHpGM9G"); @@ -136,11 +136,11 @@ pub struct CreateEscrow<'info> { /// CHECK: safe pub initializer: Signer<'info>, /// CHECK: safe - pub business: AccountInfo<'info>, // change name to business + pub business: AccountInfo<'info>, /// CHECK: safe - pub influencer: AccountInfo<'info>, // change name to influencer + pub influencer: AccountInfo<'info>, /// CHECK: safe - pub validation_authority: AccountInfo<'info>, // change name to xfluencer + pub validation_authority: AccountInfo<'info>, pub mint: Account<'info, Mint>, #[account( @@ -157,20 +157,20 @@ pub struct CreateEscrow<'info> { seeds = [b"escrow".as_ref(), order_code.to_string().as_bytes().as_ref()], bump, - )] - pub escrow_account: Account<'info, EscrowAccount>, + )] + pub escrow_account: Box>, #[account( init, seeds = ["vault".as_bytes(), - order_code.to_string().as_bytes().as_ref() ], + order_code.to_string().as_bytes().as_ref() ], payer = initializer, - bump, + bump, token::mint = mint, token::authority = initializer, constraint = amount > 0, )] - pub vault_account: Account<'info, TokenAccount>, + pub vault_account: Box>, /// CHECK: safe pub system_program: AccountInfo<'info>, @@ -229,7 +229,7 @@ pub struct Cancel<'info> { constraint = escrow_account.status == 1 @CustomError::BadEscrowState, close = business )] - pub escrow_account: Account<'info, EscrowAccount>, + pub escrow_account: Box>, /// CHECK: safe pub token_program: AccountInfo<'info>, } From d2c73ac60b60a18498cf9bdb48c9667c6746c5ea Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:00:23 +0000 Subject: [PATCH 016/157] add trace on vault authority --- .../xfluencer/src/processor/initialize_escrow.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/solana/programs/xfluencer/src/processor/initialize_escrow.rs b/solana/programs/xfluencer/src/processor/initialize_escrow.rs index 11966da0..7d3d1503 100644 --- a/solana/programs/xfluencer/src/processor/initialize_escrow.rs +++ b/solana/programs/xfluencer/src/processor/initialize_escrow.rs @@ -8,7 +8,8 @@ use anchor_spl::token; use crate::CreateEscrow; -pub fn process(ctx: Context, _vault_account_bump: u8, +pub fn process(ctx: Context, + _vault_account_bump: u8, amount: u64, order_code: u64) -> ProgramResult { @@ -32,18 +33,19 @@ pub fn process(ctx: Context, _vault_account_bump: u8, // Set the Vault Authority to the Escrow PDA let (vault_authority, _vault_authority_bump) = Pubkey::find_program_address(&[escrow_pda_seed], ctx.program_id); + msg!("Vault Authority {}",vault_authority); // Set the Authority of the Vault to the Escrow PDA token::set_authority( - ctx.accounts.into_set_authority_context(), - AuthorityType::AccountOwner, - Some(vault_authority), + ctx.accounts.into_set_authority_context(), + AuthorityType::AccountOwner, + Some(vault_authority), )?; // Transfer Tokens to the Vault token::transfer( - ctx.accounts.into_transfer_to_pda_context(), - ctx.accounts.escrow_account.amount, + ctx.accounts.into_transfer_to_pda_context(), + ctx.accounts.escrow_account.amount, )?; Ok(()) From f05a2c5e432b263895455f10cf45c3211ce09e02 Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:04:31 +0000 Subject: [PATCH 017/157] update program id to 6mTwD92 --- solana/Anchor.toml | 2 +- solana/close_program.sh | 2 +- solana/target/deploy/xfluencer-keypair.json | 2 +- solana/target/idl/xfluencer.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/solana/Anchor.toml b/solana/Anchor.toml index 870928d7..55856cf3 100644 --- a/solana/Anchor.toml +++ b/solana/Anchor.toml @@ -5,7 +5,7 @@ seeds = false skip-lint = false [programs.localnet] -xfluencer = "7zNs7f6rJyhvu9k4DZwqeqgBa27GqX12mVeQAS528xEq" +xfluencer = "6mTwD92ynqwDZxjg2ydhAjeX5w7bcsfM7jQUDeHpGM9G" [registry] url = "https://api.apr.dev" diff --git a/solana/close_program.sh b/solana/close_program.sh index bfdfdce4..4b317c33 100644 --- a/solana/close_program.sh +++ b/solana/close_program.sh @@ -1 +1 @@ -solana program close 7zNs7f6rJyhvu9k4DZwqeqgBa27GqX12mVeQAS528xEq +solana program close 6mTwD92ynqwDZxjg2ydhAjeX5w7bcsfM7jQUDeHpGM9G diff --git a/solana/target/deploy/xfluencer-keypair.json b/solana/target/deploy/xfluencer-keypair.json index 7f945ad8..395e78b9 100644 --- a/solana/target/deploy/xfluencer-keypair.json +++ b/solana/target/deploy/xfluencer-keypair.json @@ -1 +1 @@ -[194,57,31,4,221,65,142,154,148,121,222,54,182,101,109,189,162,160,1,57,96,76,249,46,187,56,89,223,105,88,204,55,8,21,157,248,81,102,82,0,86,239,43,235,17,254,69,225,5,174,96,209,135,186,201,249,13,150,207,13,88,195,59,52] \ No newline at end of file +[62,196,192,47,68,197,71,170,92,187,27,70,62,160,108,206,211,121,57,209,7,130,86,50,45,111,201,179,54,200,14,27,85,174,114,1,17,193,209,100,118,56,115,146,27,86,250,207,151,200,181,218,111,184,231,22,131,100,80,254,221,183,31,55] \ No newline at end of file diff --git a/solana/target/idl/xfluencer.json b/solana/target/idl/xfluencer.json index d3f733d8..2e873d76 100644 --- a/solana/target/idl/xfluencer.json +++ b/solana/target/idl/xfluencer.json @@ -474,6 +474,6 @@ } ], "metadata": { - "address": "7zNs7f6rJyhvu9k4DZwqeqgBa27GqX12mVeQAS528xEq" + "address": "6mTwD92ynqwDZxjg2ydhAjeX5w7bcsfM7jQUDeHpGM9G" } } \ No newline at end of file From 0aed02e79f1b4d2d2fd3a2348be73da7c8decb9a Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:17:19 +0000 Subject: [PATCH 018/157] move scripts, rpcs docs and backups of keypairs --- ...ackup-programid-keypair-Devnet-6mTwD9.json | 1 + .../backup-programid-keypair-FhUMFA.json | 1 + ...ckup-programid-keypair-Testnet-4kuiEx.json | 1 + ...ckup-programid-keypair-Testnet-YZP2k8.json | 0 solana/backups/xfluencer-types-backup.ts | 531 ++++++++++++++++++ solana/rpcs/helius-rpc-ruben.txt | 1 + solana/rpcs/set-solana-network-to-devnet.sh | 1 + solana/{ => scripts}/close_program.sh | 0 solana/{ => scripts}/copy_idl_client.sh | 0 solana/{ => scripts}/show_programs.sh | 0 10 files changed, 536 insertions(+) create mode 100644 solana/backups/backup-programid-keypair-Devnet-6mTwD9.json create mode 100644 solana/backups/backup-programid-keypair-FhUMFA.json create mode 100644 solana/backups/backup-programid-keypair-Testnet-4kuiEx.json rename solana/{ => backups}/backup-programid-keypair-Testnet-YZP2k8.json (100%) create mode 100644 solana/backups/xfluencer-types-backup.ts create mode 100644 solana/rpcs/helius-rpc-ruben.txt create mode 100644 solana/rpcs/set-solana-network-to-devnet.sh rename solana/{ => scripts}/close_program.sh (100%) rename solana/{ => scripts}/copy_idl_client.sh (100%) rename solana/{ => scripts}/show_programs.sh (100%) diff --git a/solana/backups/backup-programid-keypair-Devnet-6mTwD9.json b/solana/backups/backup-programid-keypair-Devnet-6mTwD9.json new file mode 100644 index 00000000..395e78b9 --- /dev/null +++ b/solana/backups/backup-programid-keypair-Devnet-6mTwD9.json @@ -0,0 +1 @@ +[62,196,192,47,68,197,71,170,92,187,27,70,62,160,108,206,211,121,57,209,7,130,86,50,45,111,201,179,54,200,14,27,85,174,114,1,17,193,209,100,118,56,115,146,27,86,250,207,151,200,181,218,111,184,231,22,131,100,80,254,221,183,31,55] \ No newline at end of file diff --git a/solana/backups/backup-programid-keypair-FhUMFA.json b/solana/backups/backup-programid-keypair-FhUMFA.json new file mode 100644 index 00000000..aee4c382 --- /dev/null +++ b/solana/backups/backup-programid-keypair-FhUMFA.json @@ -0,0 +1 @@ +[251,128,73,198,15,11,230,40,69,118,86,251,53,16,168,75,111,135,46,228,3,191,233,186,120,196,6,228,1,145,201,13,218,97,197,226,100,244,88,171,212,172,155,186,3,57,207,247,164,171,161,239,183,178,68,54,67,4,177,37,81,151,220,237] \ No newline at end of file diff --git a/solana/backups/backup-programid-keypair-Testnet-4kuiEx.json b/solana/backups/backup-programid-keypair-Testnet-4kuiEx.json new file mode 100644 index 00000000..25fc66f9 --- /dev/null +++ b/solana/backups/backup-programid-keypair-Testnet-4kuiEx.json @@ -0,0 +1 @@ +[220,76,86,116,90,230,202,53,43,168,250,37,224,216,237,78,222,5,142,60,151,163,82,178,86,89,143,200,131,67,172,41,55,210,162,217,219,127,171,225,69,51,73,36,91,114,213,145,22,208,127,106,78,145,207,96,222,103,169,199,181,68,131,13] diff --git a/solana/backup-programid-keypair-Testnet-YZP2k8.json b/solana/backups/backup-programid-keypair-Testnet-YZP2k8.json similarity index 100% rename from solana/backup-programid-keypair-Testnet-YZP2k8.json rename to solana/backups/backup-programid-keypair-Testnet-YZP2k8.json diff --git a/solana/backups/xfluencer-types-backup.ts b/solana/backups/xfluencer-types-backup.ts new file mode 100644 index 00000000..e9096bcd --- /dev/null +++ b/solana/backups/xfluencer-types-backup.ts @@ -0,0 +1,531 @@ +export type Xfluencer = { + "version": "0.1.0", + "name": "xfluencer", + "instructions": [ + { + "name": "initialize", + "accounts": [ + { + "name": "initializer", + "isMut": true, + "isSigner": true + }, + { + "name": "buyer", + "isMut": false, + "isSigner": false + }, + { + "name": "seller", + "isMut": false, + "isSigner": false + }, + { + "name": "judge", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "buyerDepositTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "sellerReceiveTokenAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "escrowAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "vaultAccountBump", + "type": "u8" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "orderCode", + "type": "u64" + } + ] + }, + { + "name": "cancel", + "accounts": [ + { + "name": "buyer", + "isMut": true, + "isSigner": true + }, + { + "name": "buyerDepositTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "escrowAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "orderCode", + "type": "u64" + } + ] + }, + { + "name": "createEscrow", + "accounts": [ + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "from", + "isMut": true, + "isSigner": true + }, + { + "name": "to", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "orderCode", + "type": "u64" + } + ] + }, + { + "name": "claimEscrow", + "accounts": [ + { + "name": "influencer", + "isMut": true, + "isSigner": true + }, + { + "name": "business", + "isMut": true, + "isSigner": false + }, + { + "name": "escrowAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "orderCode", + "type": "u64" + } + ] + } + ], + "accounts": [ + { + "name": "escrowAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "buyerKey", + "type": "publicKey" + }, + { + "name": "buyerDepositTokenAccount", + "type": "publicKey" + }, + { + "name": "sellerKey", + "type": "publicKey" + }, + { + "name": "sellerReceiveTokenAccount", + "type": "publicKey" + }, + { + "name": "judgeKey", + "type": "publicKey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "orderCode", + "type": "u64" + }, + { + "name": "status", + "docs": [ + "status\n 0: New\n 1: Shipping\n 2: Delivered" + ], + "type": "u8" + }, + { + "name": "deliveryTime", + "type": "i64" + }, + { + "name": "trialDay", + "type": "u16" + } + ] + } + }, + { + "name": "escrowAccountSolana", + "type": { + "kind": "struct", + "fields": [ + { + "name": "from", + "type": "publicKey" + }, + { + "name": "to", + "type": "publicKey" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + } + ] +}; + +export const IDL: Xfluencer = { + "version": "0.1.0", + "name": "xfluencer", + "instructions": [ + { + "name": "initialize", + "accounts": [ + { + "name": "initializer", + "isMut": true, + "isSigner": true + }, + { + "name": "buyer", + "isMut": false, + "isSigner": false + }, + { + "name": "seller", + "isMut": false, + "isSigner": false + }, + { + "name": "judge", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "buyerDepositTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "sellerReceiveTokenAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "escrowAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "vaultAccountBump", + "type": "u8" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "orderCode", + "type": "u64" + } + ] + }, + { + "name": "cancel", + "accounts": [ + { + "name": "buyer", + "isMut": true, + "isSigner": true + }, + { + "name": "buyerDepositTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "escrowAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "orderCode", + "type": "u64" + } + ] + }, + { + "name": "createEscrow", + "accounts": [ + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "from", + "isMut": true, + "isSigner": true + }, + { + "name": "to", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "orderCode", + "type": "u64" + } + ] + }, + { + "name": "claimEscrow", + "accounts": [ + { + "name": "influencer", + "isMut": true, + "isSigner": true + }, + { + "name": "business", + "isMut": true, + "isSigner": false + }, + { + "name": "escrowAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "orderCode", + "type": "u64" + } + ] + } + ], + "accounts": [ + { + "name": "escrowAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "buyerKey", + "type": "publicKey" + }, + { + "name": "buyerDepositTokenAccount", + "type": "publicKey" + }, + { + "name": "sellerKey", + "type": "publicKey" + }, + { + "name": "sellerReceiveTokenAccount", + "type": "publicKey" + }, + { + "name": "judgeKey", + "type": "publicKey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "orderCode", + "type": "u64" + }, + { + "name": "status", + "docs": [ + "status\n 0: New\n 1: Shipping\n 2: Delivered" + ], + "type": "u8" + }, + { + "name": "deliveryTime", + "type": "i64" + }, + { + "name": "trialDay", + "type": "u16" + } + ] + } + }, + { + "name": "escrowAccountSolana", + "type": { + "kind": "struct", + "fields": [ + { + "name": "from", + "type": "publicKey" + }, + { + "name": "to", + "type": "publicKey" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + } + ] +}; diff --git a/solana/rpcs/helius-rpc-ruben.txt b/solana/rpcs/helius-rpc-ruben.txt new file mode 100644 index 00000000..254597ef --- /dev/null +++ b/solana/rpcs/helius-rpc-ruben.txt @@ -0,0 +1 @@ +https://devnet.helius-rpc.com/?api-key=8e0e9a34-2648-421a-8f22-6460b4a68705 diff --git a/solana/rpcs/set-solana-network-to-devnet.sh b/solana/rpcs/set-solana-network-to-devnet.sh new file mode 100644 index 00000000..6ea0de21 --- /dev/null +++ b/solana/rpcs/set-solana-network-to-devnet.sh @@ -0,0 +1 @@ +solana config set --url https://devnet.helius-rpc.com/?api-key=8e0e9a34-2648-421a-8f22-6460b4a68705 diff --git a/solana/close_program.sh b/solana/scripts/close_program.sh similarity index 100% rename from solana/close_program.sh rename to solana/scripts/close_program.sh diff --git a/solana/copy_idl_client.sh b/solana/scripts/copy_idl_client.sh similarity index 100% rename from solana/copy_idl_client.sh rename to solana/scripts/copy_idl_client.sh diff --git a/solana/show_programs.sh b/solana/scripts/show_programs.sh similarity index 100% rename from solana/show_programs.sh rename to solana/scripts/show_programs.sh From 92276d5adf7a67c5066d75f7d07cdd9323b32290 Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:20:18 +0000 Subject: [PATCH 019/157] fix assertions on launchers tester to create escrow for sol --- solana-python/launch_create_escrow.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/solana-python/launch_create_escrow.py b/solana-python/launch_create_escrow.py index e8ae4b65..2141f7e4 100644 --- a/solana-python/launch_create_escrow.py +++ b/solana-python/launch_create_escrow.py @@ -29,9 +29,10 @@ async def main(): business, business_pk = get_local_keypair_pubkey(path=keypair_paths.bussines_keypair) _, influencer_pk = get_local_keypair_pubkey(path=keypair_paths.influencer_keypair) - assert str(validation_authority_pk) == configuration["platform"] - assert str(business_pk) == configuration["business"] - assert str(influencer_pk) == configuration["influencer"] + assert str(validation_authority_pk) == configuration["platform"] + + assert str(business_pk) == configuration["business"]["pubkey"] + assert str(influencer_pk) == configuration["influencer"]["pubkey"] amount = configuration["lamports"] order_code = configuration["order_code"] From 9faed21289ce618f2ea045daaf040e0bdefe9bf8 Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:41:25 +0000 Subject: [PATCH 020/157] update python client to version 1.1.0 --- solana-python/pyxfluencer/__init__.py | 16 +++++++++++++--- solana-python/pyxfluencer/program_id.py | 2 +- solana-python/pyxfluencer/utils.py | 10 ++++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/solana-python/pyxfluencer/__init__.py b/solana-python/pyxfluencer/__init__.py index 4e191403..c8a30a09 100644 --- a/solana-python/pyxfluencer/__init__.py +++ b/solana-python/pyxfluencer/__init__.py @@ -9,11 +9,21 @@ from .utils import sign_and_send_transaction from .program_id import PROGRAM_ID -xfluencer_solana_python_client_version="1.0.2" +xfluencer_solana_python_client_version="1.1.0" + +################### +# Version: 1.1.0 +# Bump: Minor +# Updated: 25.03.2024 +################### +# Issues +# - Support and launcher script to initialize escrow for SPL +# - Checker configured ATA addresses on initialization and amount +# ################### # Version: 1.0.2 # Bump: Patch -# Updated: 6.03.2024 +# Updated: 06.03.2024 ################### # Issues # -Return signature status upon validation @@ -22,7 +32,7 @@ ################### # Version: 1.0.1 # Bump: Patch -# Updated: 5.03.2024 +# Updated: 05.03.2024 ################### # Issues # -Fix bug on setup.py about including requirements diff --git a/solana-python/pyxfluencer/program_id.py b/solana-python/pyxfluencer/program_id.py index e0dcf52a..a5183915 100644 --- a/solana-python/pyxfluencer/program_id.py +++ b/solana-python/pyxfluencer/program_id.py @@ -1,3 +1,3 @@ from solders.pubkey import Pubkey -PROGRAM_ID = Pubkey.from_string("7zNs7f6rJyhvu9k4DZwqeqgBa27GqX12mVeQAS528xEq") +PROGRAM_ID = Pubkey.from_string("6mTwD92ynqwDZxjg2ydhAjeX5w7bcsfM7jQUDeHpGM9G") diff --git a/solana-python/pyxfluencer/utils.py b/solana-python/pyxfluencer/utils.py index 845ef025..48fb9e58 100644 --- a/solana-python/pyxfluencer/utils.py +++ b/solana-python/pyxfluencer/utils.py @@ -30,6 +30,7 @@ def get_local_keypair_pubkey(keypair_file="id.json", path=None): + def select_client(network = None, async_client = False): if network is None: print("[WARN] Client network selected is None, set 'heliux' as default") @@ -62,16 +63,17 @@ async def sign_and_send_transaction(ix, signers, opts, network, async_client: b client = select_client(network=network, async_client=async_client) tx = Transaction().add(ix) - print("Sending transactions with options",opts) + print("Sending transactions with options", opts) tx_res = await client.send_transaction(tx, *signers, opts=opts) - print("Client Response tx signature: ",tx_res) + print("Client Response tx signature: ", tx_res) print("Waiting for transaction confirmation") signature_status = await client.confirm_transaction(tx_res.value) - print("Confirm Transaction Status Value:",signature_status) + print("Confirm Transaction Status Value:", signature_status) return signature_status.to_json() except RPCException as e: - raise RPCException(f"RPC exception happened: {e}") \ No newline at end of file + raise RPCException(f"RPC exception happened: {e}") + From 20fee990d12437433789c40e8687c237c79077bb Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:42:19 +0000 Subject: [PATCH 021/157] update python client to version 1.1.0 --- .../pyxfluencer/accounts/__init__.py | 1 - .../pyxfluencer/accounts/escrow_account.py | 66 ++++++++++--------- solana-python/pyxfluencer/errors/custom.py | 66 +++++++++++++++++++ .../pyxfluencer/instructions/__init__.py | 5 +- .../pyxfluencer/instructions/cancel.py | 8 +-- .../pyxfluencer/instructions/initialize.py | 22 ++++--- .../instructions/validate_escrow_sol.py | 4 +- 7 files changed, 121 insertions(+), 51 deletions(-) diff --git a/solana-python/pyxfluencer/accounts/__init__.py b/solana-python/pyxfluencer/accounts/__init__.py index e913b34f..764c0546 100644 --- a/solana-python/pyxfluencer/accounts/__init__.py +++ b/solana-python/pyxfluencer/accounts/__init__.py @@ -1,3 +1,2 @@ from .escrow_account import EscrowAccount, EscrowAccountJSON from .escrow_account_solana import EscrowAccountSolana, EscrowAccountSolanaJSON -from .fees_config import FeesConfig, FeesConfigJSON diff --git a/solana-python/pyxfluencer/accounts/escrow_account.py b/solana-python/pyxfluencer/accounts/escrow_account.py index ec04ea38..cc0124a3 100644 --- a/solana-python/pyxfluencer/accounts/escrow_account.py +++ b/solana-python/pyxfluencer/accounts/escrow_account.py @@ -12,11 +12,11 @@ class EscrowAccountJSON(typing.TypedDict): - buyer_key: str - buyer_deposit_token_account: str - seller_key: str - seller_receive_token_account: str - judge_key: str + business_key: str + business_deposit_token_account: str + influencer_key: str + influencer_receive_token_account: str + validation_authority: str amount: int order_code: int status: int @@ -28,22 +28,22 @@ class EscrowAccountJSON(typing.TypedDict): class EscrowAccount: discriminator: typing.ClassVar = b"$E0\x12\x80\xe1}\x87" layout: typing.ClassVar = borsh.CStruct( - "buyer_key" / BorshPubkey, - "buyer_deposit_token_account" / BorshPubkey, - "seller_key" / BorshPubkey, - "seller_receive_token_account" / BorshPubkey, - "judge_key" / BorshPubkey, + "business_key" / BorshPubkey, + "business_deposit_token_account" / BorshPubkey, + "influencer_key" / BorshPubkey, + "influencer_receive_token_account" / BorshPubkey, + "validation_authority" / BorshPubkey, "amount" / borsh.U64, "order_code" / borsh.U64, "status" / borsh.U8, "delivery_time" / borsh.I64, "trial_day" / borsh.U16, ) - buyer_key: Pubkey - buyer_deposit_token_account: Pubkey - seller_key: Pubkey - seller_receive_token_account: Pubkey - judge_key: Pubkey + business_key: Pubkey + business_deposit_token_account: Pubkey + influencer_key: Pubkey + influencer_receive_token_account: Pubkey + validation_authority: Pubkey amount: int order_code: int status: int @@ -94,11 +94,11 @@ def decode(cls, data: bytes) -> "EscrowAccount": ) dec = EscrowAccount.layout.parse(data[ACCOUNT_DISCRIMINATOR_SIZE:]) return cls( - buyer_key=dec.buyer_key, - buyer_deposit_token_account=dec.buyer_deposit_token_account, - seller_key=dec.seller_key, - seller_receive_token_account=dec.seller_receive_token_account, - judge_key=dec.judge_key, + business_key=dec.business_key, + business_deposit_token_account=dec.business_deposit_token_account, + influencer_key=dec.influencer_key, + influencer_receive_token_account=dec.influencer_receive_token_account, + validation_authority=dec.validation_authority, amount=dec.amount, order_code=dec.order_code, status=dec.status, @@ -108,11 +108,13 @@ def decode(cls, data: bytes) -> "EscrowAccount": def to_json(self) -> EscrowAccountJSON: return { - "buyer_key": str(self.buyer_key), - "buyer_deposit_token_account": str(self.buyer_deposit_token_account), - "seller_key": str(self.seller_key), - "seller_receive_token_account": str(self.seller_receive_token_account), - "judge_key": str(self.judge_key), + "business_key": str(self.business_key), + "business_deposit_token_account": str(self.business_deposit_token_account), + "influencer_key": str(self.influencer_key), + "influencer_receive_token_account": str( + self.influencer_receive_token_account + ), + "validation_authority": str(self.validation_authority), "amount": self.amount, "order_code": self.order_code, "status": self.status, @@ -123,15 +125,15 @@ def to_json(self) -> EscrowAccountJSON: @classmethod def from_json(cls, obj: EscrowAccountJSON) -> "EscrowAccount": return cls( - buyer_key=Pubkey.from_string(obj["buyer_key"]), - buyer_deposit_token_account=Pubkey.from_string( - obj["buyer_deposit_token_account"] + business_key=Pubkey.from_string(obj["business_key"]), + business_deposit_token_account=Pubkey.from_string( + obj["business_deposit_token_account"] ), - seller_key=Pubkey.from_string(obj["seller_key"]), - seller_receive_token_account=Pubkey.from_string( - obj["seller_receive_token_account"] + influencer_key=Pubkey.from_string(obj["influencer_key"]), + influencer_receive_token_account=Pubkey.from_string( + obj["influencer_receive_token_account"] ), - judge_key=Pubkey.from_string(obj["judge_key"]), + validation_authority=Pubkey.from_string(obj["validation_authority"]), amount=obj["amount"], order_code=obj["order_code"], status=obj["status"], diff --git a/solana-python/pyxfluencer/errors/custom.py b/solana-python/pyxfluencer/errors/custom.py index 26176a28..6d82fc9c 100644 --- a/solana-python/pyxfluencer/errors/custom.py +++ b/solana-python/pyxfluencer/errors/custom.py @@ -85,6 +85,60 @@ def __init__(self) -> None: msg = "Missmatch Authority" +class PercentageFeeOutOfrange(ProgramError): + def __init__(self) -> None: + super().__init__(6009, "Percengate Fee Out of Range") + + code = 6009 + name = "PercentageFeeOutOfrange" + msg = "Percengate Fee Out of Range" + + +class NumericalProblemFoundCalculatingFees(ProgramError): + def __init__(self) -> None: + super().__init__(6010, "Numerical Problem Found Calculating Fees") + + code = 6010 + name = "NumericalProblemFoundCalculatingFees" + msg = "Numerical Problem Found Calculating Fees" + + +class BusinessHasInsufficientAmountOfTokens(ProgramError): + def __init__(self) -> None: + super().__init__(6011, "Busines Has Insufficient Amount Of Tokens") + + code = 6011 + name = "BusinessHasInsufficientAmountOfTokens" + msg = "Busines Has Insufficient Amount Of Tokens" + + +class MissmatchBusinessTokenAccount(ProgramError): + def __init__(self) -> None: + super().__init__(6012, "Missmatch Business Token Account") + + code = 6012 + name = "MissmatchBusinessTokenAccount" + msg = "Missmatch Business Token Account" + + +class MissmatchInfluencerTokenAccount(ProgramError): + def __init__(self) -> None: + super().__init__(6013, "Missmatch Influencer Token Account") + + code = 6013 + name = "MissmatchInfluencerTokenAccount" + msg = "Missmatch Influencer Token Account" + + +class MissmatchOrderCode(ProgramError): + def __init__(self) -> None: + super().__init__(6014, "Missmatch Order Code") + + code = 6014 + name = "MissmatchOrderCode" + msg = "Missmatch Order Code" + + CustomError = typing.Union[ CannotClaim, AlreadyClaim, @@ -95,6 +149,12 @@ def __init__(self) -> None: MissmatchInfluencer, BadEscrowState, MissmatchAuthority, + PercentageFeeOutOfrange, + NumericalProblemFoundCalculatingFees, + BusinessHasInsufficientAmountOfTokens, + MissmatchBusinessTokenAccount, + MissmatchInfluencerTokenAccount, + MissmatchOrderCode, ] CUSTOM_ERROR_MAP: dict[int, CustomError] = { 6000: CannotClaim(), @@ -106,6 +166,12 @@ def __init__(self) -> None: 6006: MissmatchInfluencer(), 6007: BadEscrowState(), 6008: MissmatchAuthority(), + 6009: PercentageFeeOutOfrange(), + 6010: NumericalProblemFoundCalculatingFees(), + 6011: BusinessHasInsufficientAmountOfTokens(), + 6012: MissmatchBusinessTokenAccount(), + 6013: MissmatchInfluencerTokenAccount(), + 6014: MissmatchOrderCode(), } diff --git a/solana-python/pyxfluencer/instructions/__init__.py b/solana-python/pyxfluencer/instructions/__init__.py index ad4c51b9..e6c0605e 100644 --- a/solana-python/pyxfluencer/instructions/__init__.py +++ b/solana-python/pyxfluencer/instructions/__init__.py @@ -1,12 +1,11 @@ from .initialize import initialize, InitializeArgs, InitializeAccounts +from .claim import claim, ClaimAccounts from .cancel import cancel, CancelArgs, CancelAccounts from .create_escrow import create_escrow, CreateEscrowArgs, CreateEscrowAccounts from .claim_escrow import claim_escrow, ClaimEscrowArgs, ClaimEscrowAccounts -from .cancel_escrow_sol import cancel_escrow_sol, CancelEscrowSolAccounts -from .create_fees import create_fees, CreateFeesArgs, CreateFeesAccounts -from .update_fees import update_fees, UpdateFeesArgs, UpdateFeesAccounts from .validate_escrow_sol import ( validate_escrow_sol, ValidateEscrowSolArgs, ValidateEscrowSolAccounts, ) +from .cancel_escrow_sol import cancel_escrow_sol, CancelEscrowSolAccounts diff --git a/solana-python/pyxfluencer/instructions/cancel.py b/solana-python/pyxfluencer/instructions/cancel.py index 9689a9f0..a2a208d5 100644 --- a/solana-python/pyxfluencer/instructions/cancel.py +++ b/solana-python/pyxfluencer/instructions/cancel.py @@ -15,8 +15,8 @@ class CancelArgs(typing.TypedDict): class CancelAccounts(typing.TypedDict): - buyer: Pubkey - buyer_deposit_token_account: Pubkey + business: Pubkey + business_deposit_token_account: Pubkey vault_account: Pubkey vault_authority: Pubkey escrow_account: Pubkey @@ -29,9 +29,9 @@ def cancel( remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, ) -> Instruction: keys: list[AccountMeta] = [ - AccountMeta(pubkey=accounts["buyer"], is_signer=True, is_writable=True), + AccountMeta(pubkey=accounts["business"], is_signer=True, is_writable=True), AccountMeta( - pubkey=accounts["buyer_deposit_token_account"], + pubkey=accounts["business_deposit_token_account"], is_signer=False, is_writable=True, ), diff --git a/solana-python/pyxfluencer/instructions/initialize.py b/solana-python/pyxfluencer/instructions/initialize.py index aa8a7467..8ea5c686 100644 --- a/solana-python/pyxfluencer/instructions/initialize.py +++ b/solana-python/pyxfluencer/instructions/initialize.py @@ -22,12 +22,12 @@ class InitializeArgs(typing.TypedDict): class InitializeAccounts(typing.TypedDict): initializer: Pubkey - buyer: Pubkey - seller: Pubkey - judge: Pubkey + business: Pubkey + influencer: Pubkey + validation_authority: Pubkey mint: Pubkey - buyer_deposit_token_account: Pubkey - seller_receive_token_account: Pubkey + business_deposit_token_account: Pubkey + influencer_receive_token_account: Pubkey escrow_account: Pubkey vault_account: Pubkey @@ -40,17 +40,19 @@ def initialize( ) -> Instruction: keys: list[AccountMeta] = [ AccountMeta(pubkey=accounts["initializer"], is_signer=True, is_writable=True), - AccountMeta(pubkey=accounts["buyer"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["seller"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["judge"], is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["business"], is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["influencer"], is_signer=False, is_writable=False), + AccountMeta( + pubkey=accounts["validation_authority"], is_signer=False, is_writable=False + ), AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False), AccountMeta( - pubkey=accounts["buyer_deposit_token_account"], + pubkey=accounts["business_deposit_token_account"], is_signer=False, is_writable=True, ), AccountMeta( - pubkey=accounts["seller_receive_token_account"], + pubkey=accounts["influencer_receive_token_account"], is_signer=False, is_writable=False, ), diff --git a/solana-python/pyxfluencer/instructions/validate_escrow_sol.py b/solana-python/pyxfluencer/instructions/validate_escrow_sol.py index 5ef484d1..78268da1 100644 --- a/solana-python/pyxfluencer/instructions/validate_escrow_sol.py +++ b/solana-python/pyxfluencer/instructions/validate_escrow_sol.py @@ -8,9 +8,10 @@ class ValidateEscrowSolArgs(typing.TypedDict): target_state: int + percentage_fee: int -layout = borsh.CStruct("target_state" / borsh.U8) +layout = borsh.CStruct("target_state" / borsh.U8, "percentage_fee" / borsh.U16) class ValidateEscrowSolAccounts(typing.TypedDict): @@ -42,6 +43,7 @@ def validate_escrow_sol( encoded_args = layout.build( { "target_state": args["target_state"], + "percentage_fee": args["percentage_fee"], } ) data = identifier + encoded_args From 3f7cce64c8837f072369713443e6ce3de2a99133 Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:42:43 +0000 Subject: [PATCH 022/157] update python client wheel --- .../dist/pyxfluencer-1.1.0-py3-none-any.whl | Bin 0 -> 30937 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 solana-python/dist/pyxfluencer-1.1.0-py3-none-any.whl diff --git a/solana-python/dist/pyxfluencer-1.1.0-py3-none-any.whl b/solana-python/dist/pyxfluencer-1.1.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..457105b6af3a0eaa698f3c4f21ae34b6eb59ff35 GIT binary patch literal 30937 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 Date: Tue, 26 Mar 2024 14:49:37 +0000 Subject: [PATCH 023/157] new script launcher to test create escrows with spl --- solana-python/launch_create_escrow_spl.py | 136 ++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 solana-python/launch_create_escrow_spl.py diff --git a/solana-python/launch_create_escrow_spl.py b/solana-python/launch_create_escrow_spl.py new file mode 100644 index 00000000..c635d7d1 --- /dev/null +++ b/solana-python/launch_create_escrow_spl.py @@ -0,0 +1,136 @@ +from solders.pubkey import Pubkey +from solana.rpc.types import TxOpts + +from pyxfluencer.utils import get_local_keypair_pubkey +from pyxfluencer.utils import sign_and_send_transaction +from pyxfluencer.utils import select_client + +from pyxfluencer.program_id import PROGRAM_ID +from pyxfluencer.instructions import initialize + +from config import KeypairPaths, load_configuration + +from anchorpy import utils, Wallet, Provider +from anchorpy.utils import token +from anchorpy.utils.rpc import AccountInfo + +async def get_token_account_info( ata_address: str, network : str) -> AccountInfo: + connection = select_client(network=network, async_client=True) + wallet = Wallet.local() + provider = Provider(connection, wallet) + print(ata_address) + print(wallet) + pubkey_token_account = Pubkey.from_string(ata_address) + + print("ata",pubkey_token_account) + account_info = await token.get_token_account(provider, pubkey_token_account) + return account_info + +async def main(): + + msg = "Create Escrow for Business and Influencer on order code with an amount of SPL tokens" + print(len(msg)*"*") + print(msg) + print(len(msg)*"*") + + configuration = load_configuration() + + ### inputs + network = "devnet" #"localnet" #configuration["network"] + ata_selector = "usdc_ata" + mint = "usdc_mint_address" + type_of_asset = "usdc" + + + ################################################ + + print(f"Network: {network}") + print(f"Program ID: {PROGRAM_ID}") + + keypair_paths = KeypairPaths() + + ata = configuration["payer"][ata_selector] + account_info = await get_token_account_info(ata, network) + print(account_info) + #assert account_info.amount >= 1000000 # 1 USDC + #exit() + + #influencer_ata = "EUzxNecLcmpeMfun8zyTKs372vyy7bREmqzq6QXaZTuB" + #account_info = await get_token_account_info(influencer_ata) + #print(account_info) + #assert account_info.amount >= 1000000 # 1 USDC + + business, business_pk = get_local_keypair_pubkey(path=keypair_paths.bussines_keypair) + _, influencer_pk = get_local_keypair_pubkey(path=keypair_paths.influencer_keypair) + _, validation_authority_pk = get_local_keypair_pubkey(path=keypair_paths.validation_authority) + + + business_ata = configuration["business"][ata_selector] + influencer_ata = configuration["influencer"][ata_selector] + + + #assert str(validation_authority_pk) == configuration["platform"] + #assert str(business_pk) == configuration["business"]["pubkey"] + #assert str(business_ata) == configuration["business"]["usdc_ata"] + #assert str(influencer_pk) == configuration["influencer"]["pubkey"] + #assert str(influencer_ata) == configuration["influencer"]["usdc_ata"] + + + + + #### find pdas + order_code = configuration["order_code"] + + SEEDS = [b"vault", + bytes(str(order_code),"UTF-8")] + + vault_pda, vault_account_bump = Pubkey.find_program_address(SEEDS, PROGRAM_ID) + + amount = configuration["amount"][type_of_asset] + order_code = configuration["order_code"] + + args = {"amount":int(amount), + "order_code":int(order_code), + "vault_account_bump":vault_account_bump} + + SEEDS = [b"escrow", + bytes(str(order_code),"UTF-8")] + + escrow_pda, _ = Pubkey.find_program_address(SEEDS, PROGRAM_ID) + + + mint_pk = Pubkey.from_string(configuration["spl_tokens"][mint]) + business_deposit_token_account_pk = Pubkey.from_string(business_ata) + influencer_deposit_token_account_pk = Pubkey.from_string(influencer_ata) + + + + accounts = { + "initializer": business_pk, + "business": business_pk, + "influencer": influencer_pk, + "validation_authority": validation_authority_pk, + "mint": mint_pk, + "business_deposit_token_account": business_deposit_token_account_pk, + "influencer_receive_token_account": influencer_deposit_token_account_pk, + "escrow_account": escrow_pda, + "vault_account": vault_pda + } + + opts = TxOpts(skip_confirmation = True, + skip_preflight = True, + preflight_commitment="processed") + + ix = initialize(args, accounts, program_id=PROGRAM_ID) + + #print(ix) + signers = [business] + + sign_status = await sign_and_send_transaction(ix, signers, opts, network) + + print(sign_status) + +import asyncio + +asyncio.run(main()) + \ No newline at end of file From 4c56fa09905ab869c9be741de329addbd6188093 Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:49:56 +0000 Subject: [PATCH 024/157] new instructions for python client on claim --- .../pyxfluencer/instructions/claim.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 solana-python/pyxfluencer/instructions/claim.py diff --git a/solana-python/pyxfluencer/instructions/claim.py b/solana-python/pyxfluencer/instructions/claim.py new file mode 100644 index 00000000..afcc311c --- /dev/null +++ b/solana-python/pyxfluencer/instructions/claim.py @@ -0,0 +1,45 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from spl.token.constants import TOKEN_PROGRAM_ID +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class ClaimAccounts(typing.TypedDict): + influencer: Pubkey + influencer_deposit_token_account: Pubkey + vault_account: Pubkey + vault_authority: Pubkey + escrow_account: Pubkey + + +def claim( + accounts: ClaimAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["influencer"], is_signer=True, is_writable=True), + AccountMeta( + pubkey=accounts["influencer_deposit_token_account"], + is_signer=False, + is_writable=True, + ), + AccountMeta( + pubkey=accounts["vault_account"], is_signer=False, is_writable=True + ), + AccountMeta( + pubkey=accounts["vault_authority"], is_signer=False, is_writable=False + ), + AccountMeta( + pubkey=accounts["escrow_account"], is_signer=False, is_writable=True + ), + AccountMeta(pubkey=TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b">\xc6\xd6\xc1\xd5\x9fl\xd2" + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) From 13ff3c6485f74b8eab07044f6f3a937793b12d5d Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:50:30 +0000 Subject: [PATCH 025/157] helper script to show spl tokens on address --- solana-python/show-spl-token-address.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 solana-python/show-spl-token-address.sh diff --git a/solana-python/show-spl-token-address.sh b/solana-python/show-spl-token-address.sh new file mode 100644 index 00000000..852f6fbf --- /dev/null +++ b/solana-python/show-spl-token-address.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# +## spl-token address --owner --token --verbose +spl-token address --owner 6suvWCcjg5o7xgHrDGc4MXQxraK9PnZyEXzjhhQN6HUK --token 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU --verbose From 9203529d838c2f4a9772fac835ccb14133d7cbf2 Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:50:43 +0000 Subject: [PATCH 026/157] helper script to transer spl tokens to a wallet --- solana-python/spl-token-transfer-amount-to-wallet.sh | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 solana-python/spl-token-transfer-amount-to-wallet.sh diff --git a/solana-python/spl-token-transfer-amount-to-wallet.sh b/solana-python/spl-token-transfer-amount-to-wallet.sh new file mode 100644 index 00000000..12ae8379 --- /dev/null +++ b/solana-python/spl-token-transfer-amount-to-wallet.sh @@ -0,0 +1,3 @@ +#!/bin/bash +## spl-token transfer +spl-token transfer J1TV42qEYjVCBGvFDr7dBczT2t48i9uDCj8ySrpncEu3 1000 94fznXq73oweXLrg2zL75XAMy9xNEbqtb191Xcrq97QA --allow-unfunded-recipient --fund-recipient From e5f614f86c8983f42ea5adbadb5d660ca3ab4d8a Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:51:32 +0000 Subject: [PATCH 027/157] add additional test configuration addresses for launchers --- solana-python/config.json | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/solana-python/config.json b/solana-python/config.json index ad565558..32ba5e9f 100644 --- a/solana-python/config.json +++ b/solana-python/config.json @@ -1,13 +1,34 @@ { "network": "devnet", - "business": "6suvWCcjg5o7xgHrDGc4MXQxraK9PnZyEXzjhhQN6HUK", - "influencer": "94fznXq73oweXLrg2zL75XAMy9xNEbqtb191Xcrq97QA", + "payer": { + "pubkey": "GQRDv58u1dULSyHSYWPqNTjcWjsFHHi763mbqDaEEgQ3", + "usdc_ata":"EN2jBpee542UCU7k8p83qziyvHiYcavddz9kqV7eFZ6C", + "usdc_ata_localnet": "8TkaDh6WGExnXpvBeoCPwY9XNNd7dK3Nn5W3y1Q2djJV" + }, + "business": { + "pubkey":"6suvWCcjg5o7xgHrDGc4MXQxraK9PnZyEXzjhhQN6HUK", + "usdc_ata": "B8LnJbxHoDzcA7GCTz3C9H819sqVe8Ro51WMJDGLZHCe", + "usdc_ata_localnet": "75toiYLG8AiEktDnYMwToHZ3RJrkpiSyapWknv9yTVs9" + }, + "influencer":{ + "pubkey":"94fznXq73oweXLrg2zL75XAMy9xNEbqtb191Xcrq97QA", + "usdc_ata":"EUzxNecLcmpeMfun8zyTKs372vyy7bREmqzq6QXaZTuB", + "usdc_ata_localnet": "9isib6eHBfo7G89kLiJmFtoBPM3RC8zsBGwiW6Qahjk" + }, + "spl_tokens":{ + "usdc_mint_address": "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr", + "usdc_mint_localnet": "J1TV42qEYjVCBGvFDr7dBczT2t48i9uDCj8ySrpncEu3" + }, "platform": "EsYxpj9ADJyGEjMv3tyDpADv33jDPkv9uLymXWwQCiwH", - "lamports": "10000000", - "order_code": 1241, + "amount":{ + "lamports":10000000, + "usdc": 100 + }, + "order_code": 1244, "program_id":{ "localnet": "7zNs7f6rJyhvu9k4DZwqeqgBa27GqX12mVeQAS528xEq", "devnet": "7zNs7f6rJyhvu9k4DZwqeqgBa27GqX12mVeQAS528xEq", + "devnet_alt": "6mTwD92ynqwDZxjg2ydhAjeX5w7bcsfM7jQUDeHpGM9G", "testnet": "EV31JFJxt28CoRUGm2UAsTHndmpYvpuMFH9Y8JZBJcYd" } } \ No newline at end of file From bc2c78c5e703f9a9efd2aa99a206d5ebb6c52c9b Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:52:18 +0000 Subject: [PATCH 028/157] fix configuration on test script to create escrows for sol --- solana-python/launch_create_escrow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solana-python/launch_create_escrow.py b/solana-python/launch_create_escrow.py index 2141f7e4..61d58e7c 100644 --- a/solana-python/launch_create_escrow.py +++ b/solana-python/launch_create_escrow.py @@ -34,7 +34,7 @@ async def main(): assert str(business_pk) == configuration["business"]["pubkey"] assert str(influencer_pk) == configuration["influencer"]["pubkey"] - amount = configuration["lamports"] + amount = configuration["amount"]["lamports"] order_code = configuration["order_code"] args = {"amount":int(amount), "order_code":int(order_code) } From 941cd18b1163ac535269917c427000187430796a Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:52:52 +0000 Subject: [PATCH 029/157] update program id on target idl --- solana/target/idl/xfluencer.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/solana/target/idl/xfluencer.json b/solana/target/idl/xfluencer.json index 2e873d76..38c5f21d 100644 --- a/solana/target/idl/xfluencer.json +++ b/solana/target/idl/xfluencer.json @@ -472,8 +472,5 @@ "name": "MissmatchOrderCode", "msg": "Missmatch Order Code" } - ], - "metadata": { - "address": "6mTwD92ynqwDZxjg2ydhAjeX5w7bcsfM7jQUDeHpGM9G" - } + ] } \ No newline at end of file From 05d5ab95ecc3d19d7487d44337f5a4adc536692a Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 14:54:53 +0000 Subject: [PATCH 030/157] new file to hold logic of the instruction to validte deliveries for influencer --- .../xfluencer/src/processor/validate_influencer_delivery.rs | 1 + 1 file changed, 1 insertion(+) create mode 100644 solana/programs/xfluencer/src/processor/validate_influencer_delivery.rs diff --git a/solana/programs/xfluencer/src/processor/validate_influencer_delivery.rs b/solana/programs/xfluencer/src/processor/validate_influencer_delivery.rs new file mode 100644 index 00000000..b5b819da --- /dev/null +++ b/solana/programs/xfluencer/src/processor/validate_influencer_delivery.rs @@ -0,0 +1 @@ +// Add validation instruction to validate an influencer delivery \ No newline at end of file From eaa91e133c630e4351b5964e01fde06a4bebff94 Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 15:17:31 +0000 Subject: [PATCH 031/157] add ata account checks and logs --- solana-python/launch_create_escrow_spl.py | 32 +++++++++-------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/solana-python/launch_create_escrow_spl.py b/solana-python/launch_create_escrow_spl.py index c635d7d1..7be4c09a 100644 --- a/solana-python/launch_create_escrow_spl.py +++ b/solana-python/launch_create_escrow_spl.py @@ -18,31 +18,28 @@ async def get_token_account_info( ata_address: str, network : str) -> AccountInf connection = select_client(network=network, async_client=True) wallet = Wallet.local() provider = Provider(connection, wallet) - print(ata_address) - print(wallet) pubkey_token_account = Pubkey.from_string(ata_address) - - print("ata",pubkey_token_account) account_info = await token.get_token_account(provider, pubkey_token_account) return account_info async def main(): - msg = "Create Escrow for Business and Influencer on order code with an amount of SPL tokens" - print(len(msg)*"*") + msg = "Create Escrow for a pair of Business and Influencer on an Order Code with SPL tokens" + print(len(msg) * "*") print(msg) - print(len(msg)*"*") + print(len(msg) * "*") configuration = load_configuration() - ### inputs - network = "devnet" #"localnet" #configuration["network"] + ### select network + network = "devnet" + + ### select SPL token ata_selector = "usdc_ata" mint = "usdc_mint_address" type_of_asset = "usdc" - - ################################################ + ### verify ata accounts print(f"Network: {network}") print(f"Program ID: {PROGRAM_ID}") @@ -51,14 +48,11 @@ async def main(): ata = configuration["payer"][ata_selector] account_info = await get_token_account_info(ata, network) - print(account_info) - #assert account_info.amount >= 1000000 # 1 USDC - #exit() - - #influencer_ata = "EUzxNecLcmpeMfun8zyTKs372vyy7bREmqzq6QXaZTuB" - #account_info = await get_token_account_info(influencer_ata) - #print(account_info) - #assert account_info.amount >= 1000000 # 1 USDC + print(f"ATA account {ata} --> amount tokens = {account_info.amount}") + + ata = configuration["business"][ata_selector] + account_info = await get_token_account_info(ata, network) + print(f"ATA account {ata} --> amount tokens = {account_info.amount}") business, business_pk = get_local_keypair_pubkey(path=keypair_paths.bussines_keypair) _, influencer_pk = get_local_keypair_pubkey(path=keypair_paths.influencer_keypair) From d15fe6ae710c88d8a23b75b3c71d51b3ba47594c Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 15:17:54 +0000 Subject: [PATCH 032/157] update amount of usdc to transfer to escrow --- solana-python/config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solana-python/config.json b/solana-python/config.json index 32ba5e9f..04fbaead 100644 --- a/solana-python/config.json +++ b/solana-python/config.json @@ -22,9 +22,9 @@ "platform": "EsYxpj9ADJyGEjMv3tyDpADv33jDPkv9uLymXWwQCiwH", "amount":{ "lamports":10000000, - "usdc": 100 + "usdc": 1000000 }, - "order_code": 1244, + "order_code": 1235, "program_id":{ "localnet": "7zNs7f6rJyhvu9k4DZwqeqgBa27GqX12mVeQAS528xEq", "devnet": "7zNs7f6rJyhvu9k4DZwqeqgBa27GqX12mVeQAS528xEq", From feb39b202cd4d070b2ecdf1ae5cf4f4dc5485736 Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 15:25:25 +0000 Subject: [PATCH 033/157] enable assertions to check configuration --- solana-python/config.json | 2 +- solana-python/launch_create_escrow_spl.py | 24 +++++++++-------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/solana-python/config.json b/solana-python/config.json index 04fbaead..bde6ee47 100644 --- a/solana-python/config.json +++ b/solana-python/config.json @@ -24,7 +24,7 @@ "lamports":10000000, "usdc": 1000000 }, - "order_code": 1235, + "order_code": 2001, "program_id":{ "localnet": "7zNs7f6rJyhvu9k4DZwqeqgBa27GqX12mVeQAS528xEq", "devnet": "7zNs7f6rJyhvu9k4DZwqeqgBa27GqX12mVeQAS528xEq", diff --git a/solana-python/launch_create_escrow_spl.py b/solana-python/launch_create_escrow_spl.py index 7be4c09a..db785b97 100644 --- a/solana-python/launch_create_escrow_spl.py +++ b/solana-python/launch_create_escrow_spl.py @@ -39,7 +39,7 @@ async def main(): mint = "usdc_mint_address" type_of_asset = "usdc" - ### verify ata accounts + # verify ata accounts print(f"Network: {network}") print(f"Program ID: {PROGRAM_ID}") @@ -62,17 +62,14 @@ async def main(): business_ata = configuration["business"][ata_selector] influencer_ata = configuration["influencer"][ata_selector] - - #assert str(validation_authority_pk) == configuration["platform"] - #assert str(business_pk) == configuration["business"]["pubkey"] - #assert str(business_ata) == configuration["business"]["usdc_ata"] - #assert str(influencer_pk) == configuration["influencer"]["pubkey"] - #assert str(influencer_ata) == configuration["influencer"]["usdc_ata"] - - - - - #### find pdas + # check configuration matches local keypairs + assert str(validation_authority_pk) == configuration["platform"] + assert str(business_pk) == configuration["business"]["pubkey"] + assert str(business_ata) == configuration["business"]["usdc_ata"] + assert str(influencer_pk) == configuration["influencer"]["pubkey"] + assert str(influencer_ata) == configuration["influencer"]["usdc_ata"] + + # find pdas for create escrow with spl order_code = configuration["order_code"] SEEDS = [b"vault", @@ -92,12 +89,10 @@ async def main(): escrow_pda, _ = Pubkey.find_program_address(SEEDS, PROGRAM_ID) - mint_pk = Pubkey.from_string(configuration["spl_tokens"][mint]) business_deposit_token_account_pk = Pubkey.from_string(business_ata) influencer_deposit_token_account_pk = Pubkey.from_string(influencer_ata) - accounts = { "initializer": business_pk, @@ -117,7 +112,6 @@ async def main(): ix = initialize(args, accounts, program_id=PROGRAM_ID) - #print(ix) signers = [business] sign_status = await sign_and_send_transaction(ix, signers, opts, network) From b8282a42b50ce0ba0f2a977717bcf0ba2021276d Mon Sep 17 00:00:00 2001 From: Ruben Colomina Date: Tue, 26 Mar 2024 15:34:10 +0000 Subject: [PATCH 034/157] move and exec permissions for scripts --- solana/scripts/close_program.sh | 0 solana/scripts/set-solana-network-to-devnet.sh | 1 + 2 files changed, 1 insertion(+) mode change 100644 => 100755 solana/scripts/close_program.sh create mode 100644 solana/scripts/set-solana-network-to-devnet.sh diff --git a/solana/scripts/close_program.sh b/solana/scripts/close_program.sh old mode 100644 new mode 100755 diff --git a/solana/scripts/set-solana-network-to-devnet.sh b/solana/scripts/set-solana-network-to-devnet.sh new file mode 100644 index 00000000..6ea0de21 --- /dev/null +++ b/solana/scripts/set-solana-network-to-devnet.sh @@ -0,0 +1 @@ +solana config set --url https://devnet.helius-rpc.com/?api-key=8e0e9a34-2648-421a-8f22-6460b4a68705 From be2777caf4022dc9753917071fb3ae62aeaa66f7 Mon Sep 17 00:00:00 2001 From: Mudit Mahajan Date: Wed, 27 Mar 2024 12:14:47 +0530 Subject: [PATCH 035/157] Add migration file for user.referral_code --- .../migrations/0024_user_referral_code.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/api/marketplace/accounts/migrations/0024_user_referral_code.py diff --git a/src/api/marketplace/accounts/migrations/0024_user_referral_code.py b/src/api/marketplace/accounts/migrations/0024_user_referral_code.py new file mode 100644 index 00000000..7c0cda29 --- /dev/null +++ b/src/api/marketplace/accounts/migrations/0024_user_referral_code.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2024-03-27 06:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("accounts", "0023_user_promoted_tweet_id"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="referral_code", + field=models.CharField(blank=True, max_length=16, null=True, unique=True), + ), + ] From 1bc03491b99229a6fe75c703ea993ff030bedefa Mon Sep 17 00:00:00 2001 From: Mudit Mahajan Date: Wed, 27 Mar 2024 14:06:15 +0530 Subject: [PATCH 036/157] Add StatusChip component to OrderItemForm --- .../components/checkoutComponents/orderItemForm/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ui/src/components/checkoutComponents/orderItemForm/index.tsx b/src/ui/src/components/checkoutComponents/orderItemForm/index.tsx index 7c448749..3332bcb4 100644 --- a/src/ui/src/components/checkoutComponents/orderItemForm/index.tsx +++ b/src/ui/src/components/checkoutComponents/orderItemForm/index.tsx @@ -38,6 +38,7 @@ import { renderTimeViewClock } from "@mui/x-date-pickers/timeViewRenderers"; import ArrayItem from "../arrayItem"; import CancelScheduleSendIcon from "@mui/icons-material/CancelScheduleSend"; import ScheduleSendIcon from "@mui/icons-material/ScheduleSend"; +import StatusChip from "../../shared/statusChip"; type OrderItemFormProps = { orderItem: any; @@ -362,10 +363,7 @@ export default function OrderItemForm({ }} > {!orderItem?.service_id && ( - + )} {!disableDelete && ( Date: Wed, 27 Mar 2024 14:06:21 +0530 Subject: [PATCH 037/157] Refactor order item validation and authorization in views.py --- src/api/marketplace/orders/serializers.py | 3 +- src/api/marketplace/orders/services.py | 80 ++++++++++++++++++++++- src/api/marketplace/orders/views.py | 44 ++++++++++++- 3 files changed, 123 insertions(+), 4 deletions(-) diff --git a/src/api/marketplace/orders/serializers.py b/src/api/marketplace/orders/serializers.py index 7c0e72dd..ed450137 100644 --- a/src/api/marketplace/orders/serializers.py +++ b/src/api/marketplace/orders/serializers.py @@ -519,4 +519,5 @@ class Meta: class ManualVerifyOrderItemSerializer(serializers.Serializer): - published_post_link = serializers.CharField(required=False) + published_post_link = serializers.CharField( + required=False, allow_blank=True) diff --git a/src/api/marketplace/orders/services.py b/src/api/marketplace/orders/services.py index a2456b3f..be27b457 100644 --- a/src/api/marketplace/orders/services.py +++ b/src/api/marketplace/orders/services.py @@ -1,5 +1,5 @@ import logging -from orders.models import Order, OrderItem, OrderItemTracking, OrderMessage, OrderTracking +from orders.models import Order, OrderItem, OrderItemMetaData, OrderItemTracking, OrderMessage, OrderTracking from notifications.models import Notification from decouple import config @@ -333,4 +333,80 @@ def create_manual_verification_notification(order_item): except Exception as e: logger.error( "Error creating manual verification notification: ", str(e)) - return False \ No newline at end of file + return False + + +def validate_order_item_meta_data(order_item: OrderItem): + is_valid = True + validation_error = "" + + order_item_meta_datas = OrderItemMetaData.objects.filter( + order_item=order_item) + service_type = order_item.service_master.twitter_service_type + + text = '' + tweet_id = '' + in_reply_to_tweet_id = '' + poll_options = [] + poll_duration_minutes = 0 + for order_item_meta_data in order_item_meta_datas: + if order_item_meta_data.field_name == 'text': + if order_item_meta_data.value and len(order_item_meta_data.value) > 0: + text = order_item_meta_data.value + elif order_item_meta_data.field_name == 'tweet_id': + # Split the tweet_id and get the last part + if order_item_meta_data.value and len(order_item_meta_data.value) > 0: + tweet_id = order_item_meta_data.value.split('/')[-1] + elif order_item_meta_data.field_name == 'in_reply_to_tweet_id': + # Split the tweet_id and get the last part + if order_item_meta_data.value and len(order_item_meta_data.value) > 0: + in_reply_to_tweet_id = order_item_meta_data.value.split( + '/')[-1] + elif order_item_meta_data.field_name == 'poll_options': + # This will be a comma separated string, convert to list + if order_item_meta_data.value and len(order_item_meta_data.value) > 0: + options = order_item_meta_data.value.split(',') + poll_options = [option.strip() for option in options] + elif order_item_meta_data.field_name == 'poll_duration_minutes': + # Convert to integer + if order_item_meta_data.value and len(order_item_meta_data.value) > 0: + poll_duration_minutes = int(order_item_meta_data.value) + + if service_type == 'tweet' and (not text or len(text) == 0): + is_valid = False + validation_error = "Content cannot be empty for the post" + elif service_type == 'like_tweet' and (not tweet_id or len(tweet_id) == 0): + is_valid = False + validation_error = "Post Link cannot be empty for the post" + elif service_type == 'reply_to_tweet': + if not text or len(text) == 0: + is_valid = False + validation_error = "Content cannot be empty for the post" + elif not in_reply_to_tweet_id or len(in_reply_to_tweet_id) == 0: + is_valid = False + validation_error = "Reply to post link cannot be empty for the post" + elif service_type == 'quote_tweet': + if not text or len(text) == 0: + is_valid = False + validation_error = "Content cannot be empty for the post" + elif not tweet_id or len(tweet_id) == 0: + is_valid = False + validation_error = "Quote post link cannot be empty for the post" + elif service_type == 'poll': + if not text or len(text) == 0: + is_valid = False + validation_error = "Content cannot be empty for the post" + elif len(poll_options) < 2: + is_valid = False + validation_error = "Minimum of 2 poll options are required for the post" + elif poll_duration_minutes < 5: + is_valid = False + validation_error = "Poll duration should be at least 5 minutes" + elif service_type == 'retweet' and (not tweet_id or len(tweet_id) == 0): + is_valid = False + validation_error = "Post Link cannot be empty for the post" + elif service_type == 'thread' and (not text or len(text) == 0): + is_valid = False + validation_error = "Content cannot be empty for the post" + + return is_valid, validation_error diff --git a/src/api/marketplace/orders/views.py b/src/api/marketplace/orders/views.py index 7110a309..ff6b5343 100644 --- a/src/api/marketplace/orders/views.py +++ b/src/api/marketplace/orders/views.py @@ -1,6 +1,6 @@ from accounts.models import Wallet from orders.tasks import cancel_escrow, cancel_tweet, check_order_status, schedule_tweet -from orders.services import create_manual_verification_notification, create_notification_for_order, create_order_item_approval_notification, create_order_item_tracking, create_order_tracking +from orders.services import create_manual_verification_notification, create_notification_for_order, create_order_item_approval_notification, create_order_item_tracking, create_order_tracking, validate_order_item_meta_data from marketplace.authentication import JWTAuthentication from marketplace.services import ( Pagination, @@ -1423,6 +1423,7 @@ def delete(self, request, pk): class SendTweetView(APIView): + authentication_classes = [JWTAuthentication] @swagger_auto_schema(request_body=SendTweetSerializer) def post(self, request): try: @@ -1430,6 +1431,33 @@ def post(self, request): if serializer.is_valid(): # Get the order_item_id order_item_id = serializer.validated_data["order_item_id"] + order_item = OrderItem.objects.get(pk=order_item_id) + + # Check that the logged in user is the influencer of the order + if order_item.package.influencer.id != request.user_account.id: + return Response( + { + "isSuccess": False, + "message": "You are not authorized to schedule this order item", + "data": None, + "errors": "You are not authorized to schedule this order item", + }, + status=status.HTTP_403_FORBIDDEN, + ) + + is_valid, validation_error = validate_order_item_meta_data( + order_item) + + if not is_valid: + return Response( + { + "isSuccess": False, + "message": validation_error, + "data": None, + "errors": validation_error, + }, + status=status.HTTP_400_BAD_REQUEST, + ) # Schedule the tweet schedule_tweet(order_item_id) @@ -1449,6 +1477,7 @@ def post(self, request): class CancelTweetView(APIView): + authentication_classes = [JWTAuthentication] @swagger_auto_schema(request_body=SendTweetSerializer) def post(self, request): try: @@ -1456,6 +1485,19 @@ def post(self, request): if serializer.is_valid(): # Get the order_item_id order_item_id = serializer.validated_data["order_item_id"] + order_item = OrderItem.objects.get(pk=order_item_id) + + # Check that the logged in user is the influencer of the order + if order_item.package.influencer.id != request.user_account.id: + return Response( + { + "isSuccess": False, + "message": "You are not authorized to cancel this order item", + "data": None, + "errors": "You are not authorized to cancel this order item", + }, + status=status.HTTP_403_FORBIDDEN, + ) # Schedule the tweet cancel_tweet(order_item_id) From 502ee0254aab3d01bc2988c10835f68bed7f968e Mon Sep 17 00:00:00 2001 From: Parikshit85 Date: Wed, 27 Mar 2024 14:45:36 +0530 Subject: [PATCH 038/157] minor: referral page design fixes --- .../profile/[id]/_referrals/index.tsx | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/ui/app/influencer/profile/[id]/_referrals/index.tsx b/src/ui/app/influencer/profile/[id]/_referrals/index.tsx index b9dee99c..eb898d25 100644 --- a/src/ui/app/influencer/profile/[id]/_referrals/index.tsx +++ b/src/ui/app/influencer/profile/[id]/_referrals/index.tsx @@ -19,7 +19,7 @@ type Props = {}; const REFERAL_GUIDE = [ "Share your unique referral link to an influential individual in the Xfluencer community.", - "Your referral joins Xfluencer they'll be receiving the rewards and you as well.", + "Your referral joins Xfluencer, they will receive rewards, and so will you.", "The rewards will be based on the number of followers the new user has in the following manner - ", ]; @@ -176,7 +176,7 @@ export default function Referrals({}: Props) { display: "flex", flexDirection: "column", justifyContent: "center", - alignItems:'center' + alignItems: "center", }} > no referrals - - No referral points. Start referring influencers to earn points. + + No referral points. Start referring influencers to + earn points. @@ -277,6 +281,7 @@ export default function Referrals({}: Props) { sx={{ borderRadius: "100%", px: 1, + mt:0.5, color: "white", backgroundColor: "black", height: "fit-content", @@ -289,12 +294,12 @@ export default function Referrals({}: Props) { ); })} -