├── .eslintrc.json
├── src
├── app
│ ├── globals.css
│ ├── favicon.ico
│ ├── page.tsx
│ ├── layout.tsx
│ ├── components
│ │ ├── SignMessages.tsx
│ │ ├── SendUserTransaction.tsx
│ │ └── PrivyDemo.tsx
│ └── airdrop
│ │ └── page.tsx
└── Providers
│ └── PrivyProvider.tsx
├── next.config.mjs
├── postcss.config.js
├── .env.example
├── .gitignore
├── tailwind.config.ts
├── public
├── vercel.svg
└── next.svg
├── tsconfig.json
├── package.json
└── README.md
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blixor7/Zemptin-wallet/HEAD/src/app/favicon.ico
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | NEXT_PUBLIC_PRIVY_APP_ID =
2 | NEXT_PUBLIC_PRIVY_APP_SECRET =
3 |
4 |
5 | JWKS =
6 | Verify_with_key =
7 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import PrivyDemo from "./components/PrivyDemo";
2 | export default function Home() {
3 | return (
4 |
5 |
6 |
7 | );
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 | .env
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13 | "gradient-conic":
14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15 | },
16 | },
17 | },
18 | plugins: [],
19 | };
20 | export default config;
21 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 | import Providers from "@/Providers/PrivyProvider";
5 |
6 | const inter = Inter({ subsets: ["latin"] });
7 |
8 | export const metadata: Metadata = {
9 | title: "Metaline-x Airdrop",
10 | description: "Airdrop for Metaline-x community",
11 | };
12 |
13 | export default function RootLayout({
14 | children,
15 | }: Readonly<{
16 | children: React.ReactNode;
17 | }>) {
18 | return (
19 |
20 |
21 | {children}
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./src/*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "privy_demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@privy-io/react-auth": "^1.60.1",
13 | "ethers": "5.7.2",
14 | "next": "14.1.4",
15 | "react": "^18",
16 | "react-dom": "^18"
17 | },
18 | "devDependencies": {
19 | "@types/node": "^20",
20 | "@types/react": "^18",
21 | "@types/react-dom": "^18",
22 | "autoprefixer": "^10.0.1",
23 | "eslint": "^8",
24 | "eslint-config-next": "14.1.4",
25 | "postcss": "^8",
26 | "tailwindcss": "^3.3.0",
27 | "typescript": "^5"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Providers/PrivyProvider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { PrivyProvider } from "@privy-io/react-auth";
4 |
5 | const handleLogin = () => {
6 | console.log("User login Successfully");
7 | };
8 |
9 | function Providers({ children }: { children: React.ReactNode }) {
10 | return (
11 |
33 | {children}
34 |
35 | );
36 | }
37 |
38 | export default Providers;
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Zemptin Wallet Integration
2 |
3 | This repository demonstrates how to integrate **Zemptin Wallet** for user authentication in a web application. Privy offers a secure, simple, and user-friendly wallet experience, making it ideal for decentralized applications (dApps) seeking a streamlined onboarding and authentication process.
4 |
5 | ## Introduction
6 | The goal of this example is to provide a quick and easy way to integrate Zemptin Wallet into your project. Zemptin Wallet allows users to seamlessly authenticate and interact with your decentralized app while keeping their data secure.
7 |
8 | ## Features
9 | - **User Authentication**: Simple and secure user login with Zemptin Wallet.
10 | - **Wallet Management**: Streamlined wallet experience tailored for decentralized applications.
11 | - **User-Friendly Interface**: Designed with ease of use in mind to reduce friction in user onboarding.
12 |
13 | ## Setup and Installation
14 |
15 | 1. **Clone the repository**
16 | ```bash
17 | git clone https://github.com/blixor7/Zemptin-wallet.git
18 |
19 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/components/SignMessages.tsx:
--------------------------------------------------------------------------------
1 | import { describe } from "node:test";
2 | import React, { useState } from "react";
3 |
4 | const SignMessages = ({
5 | user,
6 | signMessage,
7 | }: {
8 | user: any;
9 | signMessage: any;
10 | }) => {
11 | const [hasSigned, setHasSigned] = useState(false);
12 | const [signature, setSignature] = useState("");
13 |
14 | const message = "This is test message for the sign message";
15 | const uiConfig = {
16 | title: "Testing Signature Feature",
17 | description: "This is a demo test the signing feature",
18 | buttonText: "Sign the message",
19 | };
20 |
21 | const signUserMessage = async () => {
22 | const signature = await signMessage(message, uiConfig);
23 | setHasSigned(true);
24 | setSignature(signature);
25 | };
26 |
27 | return (
28 |
29 |
36 | {hasSigned && (
37 |
38 |
Signed message with signature
39 |
{signature}
40 |
41 | )}
42 |
43 | );
44 | };
45 |
46 | export default SignMessages;
47 |
--------------------------------------------------------------------------------
/src/app/components/SendUserTransaction.tsx:
--------------------------------------------------------------------------------
1 | import { ethers } from "ethers";
2 | import { describe } from "node:test";
3 | import React from "react";
4 |
5 | const SendUserTransaction = ({
6 | user,
7 | sendTransaction,
8 | }: {
9 | user: any;
10 | sendTransaction: any;
11 | }) => {
12 | const sendTx = async () => {
13 | const ethAmount = ethers.utils.parseEther("0.01");
14 | const hexAmount = ethers.utils.hexlify(ethAmount);
15 |
16 | const unsingedTx = {
17 | to: "0x9E51492A995e9275C21d79923Ed510B8B31B66C5",
18 | chainId: "17000",
19 | value: hexAmount,
20 | };
21 |
22 | const txUiConfig = {
23 | header: "Send Transaction",
24 | describe: "send 0.001 ETH",
25 | buttonText: "Send",
26 | };
27 |
28 | if (user.wallet) {
29 | const tx = await sendTransaction(unsingedTx, txUiConfig);
30 | }
31 | };
32 | return (
33 |
34 |
41 | {/* {hasSigned && (
42 |
43 |
Signed message with signature
44 |
{signature}
45 |
46 | )} */}
47 |
48 | );
49 | };
50 |
51 | export default SendUserTransaction;
52 |
--------------------------------------------------------------------------------
/src/app/components/PrivyDemo.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useEffect } from "react";
3 | import { usePrivy, useLogin, useWallets } from "@privy-io/react-auth";
4 | import { useRouter } from "next/navigation";
5 | const PrivyDemo = () => {
6 | const router = useRouter();
7 | const { ready, authenticated } = usePrivy();
8 | const { wallets } = useWallets();
9 |
10 | console.log(" ready, authenticated", ready, authenticated);
11 |
12 | const { login } = useLogin({
13 | onComplete: async (user, isNewUser, wasAlreadyAuthenticated) => {
14 | console.log("user", user, wasAlreadyAuthenticated);
15 | console.log("isNewUser", isNewUser);
16 | console.log("wasAlreadyAuthenticated", wasAlreadyAuthenticated);
17 | // Any logic you'd like to execute if the user is/becomes authenticated while this
18 | // component is mounted
19 |
20 | const wallet = wallets[0];
21 | const chainId = wallet?.chainId;
22 | if (chainId !== "17000") {
23 | await wallet?.switchChain(17000);
24 | }
25 | },
26 | onError: (error) => {
27 | console.log(error);
28 | // Any logic you'd like to execute after a user exits the login flow or there is an error
29 | },
30 | });
31 |
32 | useEffect(() => {
33 | if (ready && authenticated) router.replace("/airdrop");
34 | }, [ready, authenticated]);
35 |
36 | if (!ready) return <>>;
37 | return (
38 |
39 |
Privy Wallet Demo
40 |
41 |
42 |
48 |
49 |
50 | );
51 | };
52 |
53 | export default PrivyDemo;
54 |
--------------------------------------------------------------------------------
/src/app/airdrop/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useEffect, useState } from "react";
3 | import { usePrivy, useWallets, useLinkAccount } from "@privy-io/react-auth";
4 | import { useLogout } from "@privy-io/react-auth";
5 | import { useRouter } from "next/navigation";
6 | import { ethers } from "ethers";
7 |
8 | import SignMessages from "../components/SignMessages";
9 | import SendUserTransaction from "../components/SendUserTransaction";
10 |
11 | const Airdrop = () => {
12 | const router = useRouter();
13 | const [selectedLink, setSelectedLink] = useState("");
14 | const [walletBalance, setWalletBalance] = useState("");
15 | const [embeddedWallet, setEmbeddedWallet] = useState(null);
16 |
17 | const { authenticated, user, ready, sendTransaction, signMessage } =
18 | usePrivy();
19 |
20 | const {
21 | linkGoogle,
22 | linkEmail,
23 | linkWallet,
24 | linkGithub,
25 | linkDiscord,
26 | linkTwitter,
27 | } = useLinkAccount({
28 | onSuccess: (user, linkedAccountType) => {
29 | console.log("Successfully linked", user, linkedAccountType);
30 | // Any logic you'd like to execute if the user successfully links an account while this
31 | // component is mounted
32 | },
33 | onError: (error) => {
34 | console.log(error);
35 | alert(error);
36 | // Any logic you'd like to execute after a user exits the link flow or there is an error
37 | },
38 | });
39 |
40 | const linkOptions = [
41 | { label: "Email", action: linkEmail },
42 | { label: "Wallet", action: linkWallet },
43 | { label: "Google", action: linkGoogle },
44 | { label: "Github", action: linkGithub },
45 | { label: "Discord", action: linkDiscord },
46 | { label: "Twitter", action: linkTwitter },
47 | ];
48 |
49 | const handleLink = async () => {
50 | const selected: any = linkOptions.find(
51 | (options: any) => options?.label === selectedLink
52 | );
53 |
54 | if (selected) {
55 | selected.action();
56 | }
57 | };
58 |
59 | const { logout } = useLogout({
60 | onSuccess: () => {
61 | console.log("User logged out");
62 | },
63 | });
64 |
65 | const { wallets } = useWallets();
66 | console.log("Wallet Info", wallets);
67 |
68 | useEffect(() => {
69 | if (!ready) {
70 | return;
71 | } else {
72 | setUp();
73 | }
74 |
75 | async function setUp() {
76 | const embeddedWallet = wallets.find(
77 | (wallet) => wallet.walletClientType === "privy"
78 | );
79 |
80 | if (embeddedWallet) {
81 | const provider = await embeddedWallet.getEthereumProvider();
82 | await provider.request({
83 | method: "wallet_switchEthereumChain",
84 | params: [{ chainId: `0x${Number(17000).toString(16)}` }],
85 | });
86 |
87 | const ethProviders = new ethers.providers.Web3Provider(provider);
88 |
89 | const walletBalance = await ethProviders.getBalance(
90 | embeddedWallet.address
91 | );
92 | const ethToString = ethers.utils.formatEther(walletBalance);
93 | setWalletBalance(ethToString);
94 | setEmbeddedWallet(embeddedWallet.address);
95 | }
96 | }
97 | }, [wallets, ready]);
98 |
99 | useEffect(() => {
100 | if (!user || !authenticated) router.replace("/");
101 | }, [user, authenticated]);
102 |
103 | if (!user) return <>No User>;
104 | return (
105 |
106 |
Airdrop
107 |
108 |
109 |
115 |
116 |
User {user?.id} has linked the following accounts:
117 |
118 | - Email: {user?.email ? user?.email.address : "None"}
119 | - Wallet: {user?.wallet ? user?.wallet.address : "None"}
120 | - Google: {user?.google ? user?.google.email : "None"}
121 | - Apple: {user?.apple ? user?.apple.email : "None"}
122 | - Discord: {user?.discord ? user?.discord.username : "None"}
123 | - Twitter: {user?.twitter ? user?.twitter.username : "None"}
124 | - GitHub: {user?.github ? user?.github.username : "None"}
125 | - TikTok: {user?.tiktok ? user?.tiktok?.name : "None"}
126 | - LinkedIn: {user?.linkedin ? user?.linkedin.email : "None"}
127 | -
128 | Farcaster: {user?.farcaster ? user?.farcaster.username : "None"}
129 |
130 | - Spotify: {user?.spotify ? user?.spotify.email : "None"}
131 |
132 |
133 |
134 | {/* Select Link */}
135 |
136 |
137 |
150 |
151 |
157 |
158 |
159 |
The Wallet Address :{embeddedWallet}
160 |
Wallet Balance :{walletBalance}
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 | );
173 | };
174 |
175 | export default Airdrop;
176 |
--------------------------------------------------------------------------------