diff --git a/README.md b/README.md index 299ffa8..2dcc1cb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ **DeCleanup Rewards** is a decentralized application (dApp) build on steller network and designed to: a) **tokenize IRL impact created via global/local cleanup efforts** -b) **incentivize environmental actions via DeFi mechanics**. +b) **incentivize environmental actions via DeFi mechanics**. The platform promotes environmental stewardship globally, with plans to activate global/local communities and scale across multiple blockchain ecosystems. ![Screenshot of the Main Page](https://beige-defiant-spoonbill-537.mypinata.cloud/ipfs/QmWjckBnwWkidWtTQwR17TrQWoo9j3FX5LLwRg8s3n12cN) @@ -11,13 +11,13 @@ The platform promotes environmental stewardship globally, with plans to activate **Smart Contract GitHub Repository** - πŸ”—πŸ‘‰ https://github.com/DeCleanUp-DCU/Smart-Contract -**Smart Contract onchain** [Arbiscan](https://arbiscan.io/address/0xf21389b64e0eb749fd150d0c44742692e19a69c8) +**Smart Contract onchain** [Arbiscan](https://arbiscan.io/address/0xf21389b64e0eb749fd150d0c44742692e19a69c8) ## Uniqueness **How DeCleanup dApp works**: Participants engage in cleanups, submit proof via photos, and earn dynamic NFTs that evolve based on their contributions. More details in our [Mirror Article](https://mirror.xyz/decleanupnet.eth/ZzncKRu-Q-leEZkQ48Txm-NQRxG_hH3V3wyHaYUKfYI). -**User-Centric Design & Functionality**: The DeCleanup dApp offers a seamless user experience with an intuitive interface, allowing individuals and communities to easily submit proof of cleanups and earn dynamic NFTs, reflecting real-world impact on-chain. The verification system ensures quick approval for rewards, making the process simple and efficient. +**User-Centric Design & Functionality**: The DeCleanup dApp offers a seamless user experience with an intuitive interface, allowing individuals and communities to easily submit proof of cleanups and earn dynamic NFTs, reflecting real-world impact on-chain. The verification system ensures quick approval for rewards, making the process simple and efficient. More details in our [Guide](https://mirror.xyz/decleanupnet.eth/A5uzOpx9HUgXZEopCKUsbgffw9SajL4Q9eECgrguq-4). **Gasless Approach**: We chose Arbitrum for its low gas fees, with only the first claim requiring gas payment, while the rest of the experience is gasless. This eliminates barriers to onboarding non-tech-savvy users into Web3, enhancing real-world impact - our key KPI. Gasless transactions make Web3 more accessible to everyday people. @@ -33,19 +33,22 @@ More details in our [Guide](https://mirror.xyz/decleanupnet.eth/A5uzOpx9HUgXZEop ![9 Levels NFTs](https://beige-defiant-spoonbill-537.mypinata.cloud/ipfs/QmZELVjF8H5VvG1BxhunXK4n6LuK17RBuis5yRepEqxARk) The collection items have common traits that remain consistent with each NFT upgrade: + - **Category**: Cleanup Impact NFT - **Type**: Dynamic - **Impact**: Environmental There are dynamic traits, which grow every three cleanups: + - **Rarity**: Common (1-3 cleanups), Rare (4-6 cleanups), Legendary (7-9 cleanups), Unique (10 cleanups) - **Level**: Newbie (1-3 cleanups), Pro (4-6 cleanups), Hero (7-9 cleanups), Guardian (10 cleanups) And dynamic traits which grow with every cleanup done: + - **Impact Value**: 1 (first cleanup) to 10 (last cleanup) - **DCU Points**: 10 (first cleanup) to 100 (last cleanup) -While the first two categories of traits determine the general information required for this impact product’s classification on the Impact Marketplace, the last categories are mainly dynamic traits, which will determine the rewards value via Impact Marketplace DeFi utility and will contribute for user’s IRL impact rank. +While the first two categories of traits determine the general information required for this impact product’s classification on the Impact Marketplace, the last categories are mainly dynamic traits, which will determine the rewards value via Impact Marketplace DeFi utility and will contribute for user’s IRL impact rank. Read more on this topic in the article by EcoSynthesisX on [Mirror](https://mirror.xyz/ecosynthesisx.eth/zOdeuaeFfJUFScZZKu1OGF7cWCiRgUHQSGE-14cf8fo). ![Example of Traits](https://beige-defiant-spoonbill-537.mypinata.cloud/ipfs/QmfUA1PomqfsXPZod2oo79nrMq17xT1Rxo8EdWxwFFVHxM) diff --git a/package.json b/package.json index db329b0..9d2d16b 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "react-medium-image-zoom": "^5.2.12", "tailwind-merge": "^2.5.5", "tailwindcss-animate": "^1.0.7", - "thirdweb": "^5.0.3", + "thirdweb": "^5.0.3", "viem": "^2.26.2", "wagmi": "^2.14.16" }, @@ -54,10 +54,10 @@ "@types/react": "^19.0.1", "@types/react-dom": "^19", "@types/react-window": "^1.8.8", - "@typescript-eslint/eslint-plugin": "7.18.0", - "@typescript-eslint/parser": "7.18.0", + "@typescript-eslint/eslint-plugin": "7.18.0", + "@typescript-eslint/parser": "7.18.0", "autoprefixer": "^10.4.20", - "eslint": "8.57.1", + "eslint": "8.57.1", "eslint-config-airbnb-typescript": "^18.0.0", "eslint-config-next": "^15.1.0", "eslint-config-prettier": "^9.1.0", @@ -76,4 +76,4 @@ "tailwindcss": "^3.4.16", "typescript": "^5.7.2" } -} \ No newline at end of file +} diff --git a/src/app/api/auth/[...nextauth].ts b/src/app/api/auth/[...nextauth].ts index 0b57621..f9b9053 100644 --- a/src/app/api/auth/[...nextauth].ts +++ b/src/app/api/auth/[...nextauth].ts @@ -1,27 +1,31 @@ -import NextAuth from "next-auth"; -import GoogleProvider from "next-auth/providers/google"; +import NextAuth from 'next-auth' +import GoogleProvider from 'next-auth/providers/google' // Extend the Session and User types to include the idToken -declare module "next-auth" { +declare module 'next-auth' { interface User { - idToken?: string; + idToken?: string } interface Session { - idToken?: string | undefined; + idToken?: string | undefined } } export const { handlers, signIn, signOut, auth } = NextAuth({ providers: [ GoogleProvider({ - clientId: process.env.GOOGLE_CLIENT_ID ?? "", - clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? (() => { throw new Error("GOOGLE_CLIENT_SECRET is not defined"); })(), + clientId: process.env.GOOGLE_CLIENT_ID ?? '', + clientSecret: + process.env.GOOGLE_CLIENT_SECRET ?? + (() => { + throw new Error('GOOGLE_CLIENT_SECRET is not defined') + })(), authorization: { params: { - prompt: "consent", - access_type: "offline", - response_type: "code", - scope: "openid profile email", + prompt: 'consent', + access_type: 'offline', + response_type: 'code', + scope: 'openid profile email', }, }, }), @@ -30,19 +34,19 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ // Store the Google id_token in the JWT async jwt({ token, account }) { if (account) { - token.idToken = account.id_token; + token.idToken = account.id_token } - return token; + return token }, // Add the id_token to the session async session({ session, token }) { - session.idToken = token.idToken as string; - return session; + session.idToken = token.idToken as string + return session }, }, session: { - strategy: "jwt", + strategy: 'jwt', }, -}); +}) -export default handlers; \ No newline at end of file +export default handlers diff --git a/src/app/api/auth/login.ts b/src/app/api/auth/login.ts index d3876d6..383e35e 100644 --- a/src/app/api/auth/login.ts +++ b/src/app/api/auth/login.ts @@ -1,71 +1,81 @@ -"use client" -import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth"; -import { createThirdwebClient, generatePayload, getUser, verifyPayload } from "thirdweb"; -import { inMemoryWallet } from "thirdweb/wallets"; -import { auth } from "./[...nextauth]"; -import { serialize } from "cookie"; +'use client' +import type { NextApiRequest, NextApiResponse } from 'next' +import { getServerSession } from 'next-auth' +import { + createThirdwebClient, + generatePayload, + getUser, + verifyPayload, +} from 'thirdweb' +import { inMemoryWallet } from 'thirdweb/wallets' +import { auth } from './[...nextauth]' +import { serialize } from 'cookie' // Initialize ThirdWeb client const client = createThirdwebClient({ clientId: process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID, -}); +}) -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== "POST") { - return res.status(405).json({ error: "Method not allowed" }); +export default async function handler( + req: NextApiRequest, + res: NextApiResponse, +) { + if (req.method !== 'POST') { + return res.status(405).json({ error: 'Method not allowed' }) } try { // Get the NextAuth session (Google OAuth) - const session = await getServerSession(req, res, auth); + const session = await getServerSession(req, res, auth) if (!session || !session.idToken) { - return res.status(401).json({ error: "Unauthorized" }); + return res.status(401).json({ error: 'Unauthorized' }) } // Use the Google id_token as the identifier - const idToken = session.idToken; + const idToken = session.idToken // Generate a ThirdWeb Auth payload const payload = await generatePayload({ client, address: idToken, // Use id_token as the unique identifier - }); + }) // Sign the payload with an in-memory wallet (for demo purposes) - const wallet = inMemoryWallet(); - const signedPayload = await wallet.signPayload(payload); + const wallet = inMemoryWallet() + const signedPayload = await wallet.signPayload(payload) // Verify the signed payload const verified = await verifyPayload({ client, payload: signedPayload, - }); + }) if (!verified) { - return res.status(401).json({ error: "Verification failed" }); + return res.status(401).json({ error: 'Verification failed' }) } // Get the ThirdWeb user (includes wallet address) - const thirdwebUser = await getUser({ client, address: idToken }); + const thirdwebUser = await getUser({ client, address: idToken }) // Store the ThirdWeb session in a cookie const sessionData = { address: thirdwebUser.address, idToken, - }; - const encryptedSessionData = JSON.stringify(sessionData); // In production, encrypt this - const cookie = serialize("thirdweb_session", encryptedSessionData, { + } + const encryptedSessionData = JSON.stringify(sessionData) // In production, encrypt this + const cookie = serialize('thirdweb_session', encryptedSessionData, { httpOnly: true, - secure: process.env.NODE_ENV === "production", + secure: process.env.NODE_ENV === 'production', maxAge: 60 * 60 * 24 * 7, // 1 week - path: "/", - }); + path: '/', + }) - res.setHeader("Set-Cookie", cookie); - res.status(200).json({ message: "Login successful", address: thirdwebUser.address }); + res.setHeader('Set-Cookie', cookie) + res + .status(200) + .json({ message: 'Login successful', address: thirdwebUser.address }) } catch (error) { - console.error("Login error:", error); - res.status(500).json({ error: "Internal server error" }); + console.error('Login error:', error) + res.status(500).json({ error: 'Internal server error' }) } -} \ No newline at end of file +} diff --git a/src/app/client.ts b/src/app/client.ts index 4b43bf4..3ace347 100644 --- a/src/app/client.ts +++ b/src/app/client.ts @@ -1,12 +1,14 @@ -import { createThirdwebClient } from "thirdweb"; +import { createThirdwebClient } from 'thirdweb' + const clientId = process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID; + if (!clientId) { - throw new Error("No client ID provided"); + throw new Error('No client ID provided') } export const client = createThirdwebClient({ clientId: clientId, -}); \ No newline at end of file +}) diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index fcf0829..f7cb0cd 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -24,14 +24,12 @@ export default function Page() { console.log('Uploaded images:', images) } return ( -
+
- -
{/* 24 WEEKS STREAK*/}
-
+

CLEANUPS DONE

-

0

+

+ 0 +

REFERRALS

@@ -166,7 +166,7 @@ export default function Page() {
-
+
@@ -202,7 +202,7 @@ function LongButton({ text, isNotBlack }: LongButtonProps) {
- + {/* Bottom Section */} -
+
{/* Before/After Images */} -
-
- +
+
+ 1. BEFORE -
- Before cleanup + Before cleanup
- -
- + +
+ 2. AFTER -
- After cleanup + After cleanup
- + {/* Review Status */} -
-
- +
+
+ AFTER THE TEAM REVIEW THE PROOF OF CLEANUP, COME BACK TO CLAIM YOUR NEW LEVEL. USUALLY THE PROCESS TAKES FROM 2 TO 12 HOURS. CONTACT US IN TELEGRAM GROUP IF YOU HAVE QUESTIONS OR FOR TROUBLESHOOTING - -
+ +
IN REVIEW
diff --git a/src/app/globals.css b/src/app/globals.css index 779b340..a519283 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -10,6 +10,4 @@ html { @apply text-black; } - } - diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 9744810..c9df785 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -24,6 +24,7 @@ export default function RootLayout({
+ {children}