Add cursor and privy provider

This commit is contained in:
2025-03-05 22:40:24 +07:00
parent 9498696d6d
commit ead68573a5
14 changed files with 698 additions and 72 deletions

82
.cursor/rules/backend.mdc Normal file
View File

@@ -0,0 +1,82 @@
---
description: Guideline for .NET C# backend
globs:
alwaysApply: false
---
# .NET Development Rules for Quantitative Finance
You are a senior .NET backend developer and experimental quant with deep expertise in financial mathematics, algorithmic trading, and market indicators.
## Quantitative Finance Core Principles
- Prioritize numerical precision (use `decimal` for monetary calculations)
- Implement proven financial mathematics (e.g., Black-Scholes, Monte Carlo methods)
- Optimize time-series processing for tick data/OHLCV series
- Validate models with historical backtesting frameworks
- Maintain audit trails for financial calculations
## Code Style and Structure
- Write concise, idiomatic C# code with accurate examples.
- Follow .NET and ASP.NET Core conventions and best practices.
- Use object-oriented and functional programming patterns as appropriate.
- Prefer LINQ and lambda expressions for collection operations.
- Use descriptive variable and method names (e.g., 'IsUserSignedIn', 'CalculateTotal').
- Structure files according to .NET conventions (Controllers, Models, Services, etc.).
## Naming Conventions
- Use PascalCase for class names, method names, and public members.
- Use camelCase for local variables and private fields.
- Use UPPERCASE for constants.
- Prefix interface names with "I" (e.g., 'IUserService').
## C# and .NET Usage
- Use C# 10+ features when appropriate (e.g., record types, pattern matching, null-coalescing assignment).
- Leverage built-in ASP.NET Core features and middleware.
- Use Entity Framework Core effectively for database operations.
## Syntax and Formatting
- Follow the C# Coding Conventions (https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions)
- Use C#'s expressive syntax (e.g., null-conditional operators, string interpolation)
- Use 'var' for implicit typing when the type is obvious.
## Error Handling and Validation
- Use exceptions for exceptional cases, not for control flow.
- Implement proper error logging using built-in .NET logging or a third-party logger.
- Use Data Annotations or Fluent Validation for model validation.
- Implement global exception handling middleware.
- Return appropriate HTTP status codes and consistent error responses.
## API Design
- Follow RESTful API design principles.
- Use attribute routing in controllers.
- Implement versioning for your API.
- Use action filters for cross-cutting concerns.
## Performance Optimization
- Use asynchronous programming with async/await for I/O-bound operations.
- Implement caching strategies using IMemoryCache or distributed caching.
- Use efficient LINQ queries and avoid N+1 query problems.
- Implement pagination for large data sets.
## Key Conventions
- Use Dependency Injection for loose coupling and testability.
- Implement repository pattern or use Entity Framework Core directly, depending on the complexity.
- Use AutoMapper for object-to-object mapping if needed.
- Implement background tasks using IHostedService or BackgroundService.
## Testing
- Write unit tests using xUnit, NUnit, or MSTest.
- Use Moq or NSubstitute for mocking dependencies.
- Implement integration tests for API endpoints.
## Security
- Use Authentication and Authorization middleware.
- Implement JWT authentication for stateless API authentication.
- Use HTTPS and enforce SSL.
- Implement proper CORS policies.
## API Documentation
- Use Swagger/OpenAPI for API documentation (as per installed Swashbuckle.AspNetCore package).
- Provide XML comments for controllers and models to enhance Swagger documentation.
Follow the official Microsoft documentation and ASP.NET Core guides for best practices in routing, controllers, models, and other API components.

View File

@@ -0,0 +1,72 @@
---
description: Guideline for React Vite app frontend
globs:
alwaysApply: false
---
You are an expert in Solidity, TypeScript, Node.js, Next.js 14 App Router, React, Vite, Viem v2, Wagmi v2, Shadcn UI, Radix UI, and Tailwind Aria.
Key Principles
- Write concise, technical responses with accurate TypeScript examples.
- Use functional, declarative programming. Avoid classes.
- Prefer iteration and modularization over duplication.
- Use descriptive variable names with auxiliary verbs (e.g., isLoading).
- Use lowercase with dashes for directories (e.g., components/auth-wizard).
- Favor named exports for components.
- Use the Receive an Object, Return an Object (RORO) pattern.
JavaScript/TypeScript
- Use "function" keyword for pure functions. Omit semicolons.
- Use TypeScript for all code. Prefer interfaces over types. Avoid enums, use maps.
- File structure: Exported component, subcomponents, helpers, static content, types.
- Avoid unnecessary curly braces in conditional statements.
- For single-line statements in conditionals, omit curly braces.
- Use concise, one-line syntax for simple conditional statements (e.g., if (condition) doSomething()).
Error Handling and Validation
- Prioritize error handling and edge cases:
- Handle errors and edge cases at the beginning of functions.
- Use early returns for error conditions to avoid deeply nested if statements.
- Place the happy path last in the function for improved readability.
- Avoid unnecessary else statements; use if-return pattern instead.
- Use guard clauses to handle preconditions and invalid states early.
- Implement proper error logging and user-friendly error messages.
- Consider using custom error types or error factories for consistent error handling.
React/Next.js
- Use functional components and TypeScript interfaces.
- Use declarative JSX.
- Use function, not const, for components.
- Use Shadcn UI, Radix, and Tailwind Aria for components and styling.
- Implement responsive design with Tailwind CSS.
- Use mobile-first approach for responsive design.
- Place static content and interfaces at file end.
- Use content variables for static content outside render functions.
- Minimize 'use client', 'useEffect', and 'setState'. Favor RSC.
- Use Zod for form validation.
- Wrap client components in Suspense with fallback.
- Use dynamic loading for non-critical components.
- Optimize images: WebP format, size data, lazy loading.
- Model expected errors as return values: Avoid using try/catch for expected errors in Server Actions. Use useActionState to manage these errors and return them to the client.
- Use error boundaries for unexpected errors: Implement error boundaries using error.tsx and global-error.tsx files to handle unexpected errors and provide a fallback UI.
- Use useActionState with react-hook-form for form validation.
- Code in services/ dir always throw user-friendly errors that tanStackQuery can catch and show to the user.
- Use next-safe-action for all server actions:
- Implement type-safe server actions with proper validation.
- Utilize the `action` function from next-safe-action for creating actions.
- Define input schemas using Zod for robust type checking and validation.
- Handle errors gracefully and return appropriate responses.
- Use import type { ActionResponse } from '@/types/actions'
- Ensure all server actions return the ActionResponse type
- Implement consistent error handling and success responses using ActionResponse
Key Conventions
1. Rely on Next.js App Router for state changes.
2. Prioritize Web Vitals (LCP, CLS, FID).
3. Minimize 'use client' usage:
- Prefer server components and Next.js SSR features.
- Use 'use client' only for Web API access in small components.
- Avoid using 'use client' for data fetching or state management.
Refer to Next.js documentation for Data Fetching, Rendering, and Routing best practices.

141
.cursorban Normal file
View File

@@ -0,0 +1,141 @@
# Prevent sending sensitive configuration files to AI services
#######################
# GENERAL RULES
#######################
# App settings files
**/appsettings*.json
**/appSettings*.json
**/app.settings*.json
**/app.config
# Environment files
**/.env*
**/*.env
# Key files and certificates
**/*.pfx
**/*.key
**/*.pem
**/*.p12
**/*.cer
**/*.crt
# Credentials and tokens
**/credentials.json
**/token.json
**/secrets.json
**/auth.json
# Config files that might contain secrets
**/connection.json
**/database.json
**/firebase.json
**/aws.json
**/azure.json
**/google-services.json
**/googleservices.json
# Local development overrides
**/local.settings.json
**/launchSettings.json
# User-specific files
**/*.user
**/*.pubxml
# Other potentially sensitive files
**/privatekey.json
**/private_key.json
**/service-account.json
**/firebaserc
**/firebase-adminsdk.json
**/firebase-service-account.json
# Log files (may contain sensitive info)
**/logs/
**/*.log
#######################
# BACKEND RULES (.NET)
#######################
# Database migrations and sensitive data models
**/Migrations/
**/bin/
**/obj/
# .NET specific configuration
**/Properties/PublishProfiles/
**/Properties/ServiceDependencies/
**/*.Development.json
**/*.Production.json
**/*.Staging.json
# Sensitive backend services
**/Services/Auth/
**/Services/Payment/
**/Infrastructure/Security/
**/Infrastructure/Database/Configurations/
# API keys and connection strings
**/Managing.Infrastructure.Database/MongoDb/Configurations/
**/Managing.Infrastructure.Messengers/Discord/
#######################
# FRONTEND RULES
#######################
# Build artifacts
**/node_modules/
**/dist/
**/build/
# Frontend configuration with sensitive data
**/src/config/api.ts
**/src/config/auth.ts
**/src/config/keys.ts
**/src/config/endpoints.ts
# Authentication related components
**/src/services/auth/
**/src/hooks/useAuth.ts
**/src/stores/authStore.ts
# Web3 wallet configurations
**/src/config/wallet.ts
**/src/config/web3.ts
**/src/config/chains.ts
# Large generated files
**/src/generated/
**/*.generated.ts
**/*.graphql.ts
# Test data with potentially sensitive information
**/src/mocks/
**/src/__tests__/fixtures/
**/cypress/fixtures/
#######################
# PROJECT SPECIFIC
#######################
# Discord bot configuration
**/Managing.Infrastructure.Messengers/Discord/DiscordBotConfig.cs
# Web3 wallet integration
**/Managing.Infrastructure.Web3/Wallets/
# Trading strategies with proprietary algorithms
**/Managing.Application/Trading/Strategies/
# User data and analytics
**/Managing.Application/Users/
**/Managing.Application/Analytics/
# Database schemas with sensitive fields
**/Managing.Domain/Entities/User.cs
**/Managing.Domain/Entities/Wallet.cs
**/Managing.Domain/Entities/ApiKey.cs

View File

@@ -148,3 +148,56 @@ Bot types availables :
| ScalpingBot | This bot will open position and wait before opening a new one |
| FlippingBot | The flipping bot flipping the position only when a strategy trigger an opposite signal |
## Privy Integration
This project uses [Privy](https://privy.io/) for wallet authentication and management. Privy provides a seamless authentication experience with support for both embedded wallets and external wallet connections.
### Supported Networks
- Ethereum Mainnet
- Arbitrum One
### Configuration
To use Privy in this project, you need to:
1. Create a Privy account at [https://console.privy.io/](https://console.privy.io/)
2. Create a new app in the Privy dashboard
3. Copy your Privy App ID to the `.env` file:
```
VITE_PRIVY_APP_ID=your-privy-app-id
```
### Features
- **Multi-chain support**: Connect to Ethereum and Arbitrum networks
- **Embedded wallets**: Create and manage wallets directly in the browser
- **Social login**: Authenticate with email, Google, and other providers
- **Seamless integration**: Works with wagmi for blockchain interactions
### Usage
The Privy provider is configured in `src/main.tsx` and can be used throughout the application with the `usePrivy` hook:
```tsx
import { usePrivy } from '@privy-io/react-auth';
function MyComponent() {
const { user, login, logout, authenticated } = usePrivy();
if (!authenticated) {
return <button onClick={login}>Login</button>;
}
return (
<div>
<p>Welcome, {user.email}</p>
<button onClick={logout}>Logout</button>
</div>
);
}
```
For more information, see the [Privy documentation](https://docs.privy.io/).

View File

@@ -2,6 +2,7 @@
using Managing.Application.Abstractions.Services;
using Managing.Domain.Accounts;
using Managing.Domain.Users;
using Microsoft.Extensions.Logging;
namespace Managing.Application.Users;
@@ -10,24 +11,28 @@ public class UserService : IUserService
private readonly IEvmManager _evmManager;
private readonly IUserRepository _userRepository;
private readonly IAccountService _accountService;
private readonly ILogger<UserService> _logger;
private string[] authorizedAddresses = [
private string[] authorizedAddresses =
[
"0x6781920674dA695aa5120d95D80c4B1788046806", // Macbook
"0xA2B43AFF0992a47838DF2e6099A8439981f0B717", // Phone
"0xAD4bcf258852e9d47E580798d312E1a52D59E721", // Razil
"0xAd6D6c80910096b40e45690506a9f1052e072dCB", // Teru
"0x309b9235edbe1C6f840816771c6C21aDa6c275EE", // Cowchain
"0x23AA99254cfaA2c374bE2bA5B55C68018cCdFCb3" // Local optiflex
"0x23AA99254cfaA2c374bE2bA5B55C68018cCdFCb3", // Local optiflex
"0x932167388dD9aad41149b3cA23eBD489E2E2DD78" // Embedded wallet
];
public UserService(
IEvmManager evmManager,
IUserRepository userRepository,
IAccountService accountService)
IAccountService accountService, ILogger<UserService> logger)
{
_evmManager = evmManager;
_userRepository = userRepository;
_accountService = accountService;
_logger = logger;
}
public async Task<User> Authenticate(string name, string address, string message, string signature)
@@ -35,10 +40,16 @@ public class UserService : IUserService
var recoveredAddress = _evmManager.VerifySignature(signature, message);
if (!authorizedAddresses.Contains(recoveredAddress))
{
_logger.LogWarning($"Address {recoveredAddress} not authorized");
throw new Exception("Address not authorized");
}
if (recoveredAddress == null || !recoveredAddress.Equals(address))
{
_logger.LogWarning($"Address {recoveredAddress} not corresponding");
throw new Exception("Address not corresponding");
}
// Check if account exist
var account = await _accountService.GetAccountByKey(recoveredAddress, true, false);

View File

@@ -4,3 +4,4 @@ VITE_WORKER_URL_LOCAL=https://localhost:5002
VITE_WORKER_URL_SERVER=https://dev-managing-worker.apps.managing.live
ALCHEMY_ID=Bao7OirVe4bmYiDbPh0l8cs5gYb5D4_9
WALLET_CONNECT_PROJECT_ID=363bf09c10fec2293b21ee199b2ce8d5
VITE_PRIVY_APP_ID=cm7u09v0u002zrkuf2yjjr58p

View File

@@ -18,11 +18,15 @@
"dependencies": {
"@heroicons/react": "^1.0.6",
"@microsoft/signalr": "^6.0.5",
"@tanstack/react-query": "^5.36.1",
"@privy-io/react-auth": "^2.6.1",
"@privy-io/wagmi": "^1.0.3",
"@tailwindcss/typography": "^0.5.0",
"@tanstack/react-query": "^5.67.1",
"@wagmi/chains": "^0.2.9",
"@wagmi/connectors": "^5.7.3",
"@wagmi/core": "^2.16.3",
"@walletconnect/universal-provider": "^2.8.6",
"autoprefixer": "^10.4.7",
"axios": "^0.27.2",
"classnames": "^2.3.1",
"connectkit": "^1.8.2",
@@ -32,6 +36,7 @@
"lightweight-charts": "git+https://github.com/ntf/lightweight-charts.git",
"moment": "^2.29.3",
"plotly.js": "^2.18.1",
"postcss": "^8.4.13",
"react": "^18.1.0",
"react-cookie": "^4.1.1",
"react-dom": "^18.1.0",
@@ -46,14 +51,11 @@
"react-table": "^7.8.0",
"react-toastify": "^9.0.1",
"reactflow": "^11.8.3",
"tailwindcss": "^3.0.23",
"viem": "2.x",
"wagmi": "^2.14.9",
"wagmi": "^2.14.12",
"web3": "^4.16.0",
"zustand": "^4.4.1",
"postcss": "^8.4.13",
"autoprefixer": "^10.4.7",
"@tailwindcss/typography": "^0.5.0",
"tailwindcss": "^3.0.23"
"zustand": "^4.4.1"
},
"devDependencies": {
"@types/react": "^18.0.9",

View File

@@ -0,0 +1,95 @@
import { usePrivy } from '@privy-io/react-auth'
import { useAccount, useBalance, useChainId, useSwitchChain } from 'wagmi'
import { arbitrum, mainnet } from 'viem/chains'
import { useCallback } from 'react'
/**
* Component to display wallet information and test the Privy integration
*/
export const WalletInfo = () => {
const { user, ready, authenticated, login, logout } = usePrivy()
const { address, isConnected } = useAccount()
const chainId = useChainId()
const { data: balance } = useBalance({
address,
})
const { switchChainAsync } = useSwitchChain()
const switchToEthereum = useCallback(async () => {
if (chainId !== mainnet.id) {
try {
await switchChainAsync({ chainId: mainnet.id })
} catch (error) {
console.error('Failed to switch to Ethereum:', error)
}
}
}, [chainId, switchChainAsync])
const switchToArbitrum = useCallback(async () => {
if (chainId !== arbitrum.id) {
try {
await switchChainAsync({ chainId: arbitrum.id })
} catch (error) {
console.error('Failed to switch to Arbitrum:', error)
}
}
}, [chainId, switchChainAsync])
if (!ready) {
return <div>Loading...</div>
}
if (!authenticated) {
return (
<div className="p-4 border rounded-lg shadow-sm">
<h2 className="text-xl font-bold mb-4">Wallet Connection</h2>
<p className="mb-4">Please connect your wallet to continue.</p>
<button
onClick={login}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Connect Wallet
</button>
</div>
)
}
return (
<div className="p-4 border rounded-lg shadow-sm">
<h2 className="text-xl font-bold mb-4">Wallet Information</h2>
<div className="mb-4">
<p><strong>Connected:</strong> {isConnected ? 'Yes' : 'No'}</p>
<p><strong>Address:</strong> {address ? address : 'Not connected'}</p>
<p><strong>Chain:</strong> {chainId === mainnet.id ? 'Ethereum' : chainId === arbitrum.id ? 'Arbitrum' : chainId}</p>
<p><strong>Balance:</strong> {balance ? `${balance.formatted} ${balance.symbol}` : 'Unknown'}</p>
</div>
<div className="flex space-x-2 mb-4">
<button
onClick={switchToEthereum}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
disabled={chainId === mainnet.id}
>
Switch to Ethereum
</button>
<button
onClick={switchToArbitrum}
className="px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600"
disabled={chainId === arbitrum.id}
>
Switch to Arbitrum
</button>
</div>
<button
onClick={logout}
className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
>
Disconnect
</button>
</div>
)
}
export default WalletInfo

View File

@@ -1,7 +1,7 @@
import { StatusOfflineIcon } from '@heroicons/react/solid'
import type { SubmitHandler } from 'react-hook-form'
import { useForm } from 'react-hook-form'
import { useAccount, useDisconnect, useSignMessage } from 'wagmi'
import { usePrivy, useSignMessage } from '@privy-io/react-auth'
import useApiUrlStore from '../../../app/store/apiStore'
import { UserClient } from '../../../generated/ManagingApi'
@@ -13,38 +13,66 @@ import Toast from '../Toast/Toast'
const LogIn = () => {
const { apiUrl } = useApiUrlStore()
const { register, handleSubmit } = useForm<ILoginFormInput>()
const { disconnect } = useDisconnect()
const { address } = useAccount()
const { signMessageAsync } = useSignMessage({})
const { user, logout, ready, authenticated } = usePrivy()
const { signMessage } = useSignMessage()
const { setCookie } = useCookie()
const onSubmit: SubmitHandler<ILoginFormInput> = async (form) => {
const message = 'wagmi'
const signature = await signMessageAsync({ message })
const t = new Toast('Creating token')
if (!authenticated || !user || !user.wallet?.address) {
const t = new Toast('Error: Not authenticated')
t.update('error', 'Please connect your wallet first')
return
}
if (signature && address) {
try {
const message = 'wagmi'
const t = new Toast('Signing message...')
// Use Privy's signMessage function - returns { signature: string }
const { signature } = await signMessage({ message })
t.update('info', 'Creating token...')
// Use the Privy embedded wallet address
const walletAddress = user.linkedAccounts[1]?.address
if (signature && walletAddress) {
const userClient = new UserClient({}, apiUrl)
await userClient
.user_CreateToken({
address: address.toString(),
address: walletAddress,
message: message,
name: form.name,
signature: signature,
})
.then((data) => {
setCookie('token', data, 1)
t.update('success', 'Login successful!')
setTimeout(() => {
location.assign("/");
}, 1000);
})
.catch((err: any) => {
t.update('error', 'Error : Some thing bad happen')
console.error('Login error:', err)
t.update('error', 'Error: Something went wrong')
})
} else {
t.update('error', 'Error : No signature')
t.update('error', 'Error: No signature or address')
}
} catch (error) {
console.error('Signing error:', error)
const t = new Toast('Error')
t.update('error', `Error signing message: ${error instanceof Error ? error.message : 'Unknown error'}`)
}
}
if (!ready) {
return <div>Loading...</div>
}
if (!authenticated) {
return <div>Please connect your wallet first</div>
}
return (
@@ -87,7 +115,8 @@ const LogIn = () => {
Sign and login
</button>
<button
onClick={() => disconnect}
onClick={() => logout()}
type="button"
className="btn bg-primary w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
>
Disconnect wallet{' '}

View File

@@ -1,5 +1,5 @@
import { useIsFetching } from '@tanstack/react-query'
import { ConnectKitButton } from 'connectkit'
import { usePrivy } from '@privy-io/react-auth'
import type { ReactNode } from 'react'
import { useState } from 'react'
import { Link } from 'react-router-dom'
@@ -44,6 +44,38 @@ const GlobalLoader = () => {
return isFetching ? <Loader size="xs"></Loader> : null
}
// Custom Privy wallet button component
const PrivyWalletButton = () => {
const { login, logout, authenticated, user } = usePrivy()
if (!authenticated) {
return (
<button
onClick={login}
className="btn btn-primary btn-sm"
>
Connect Wallet
</button>
)
}
// Display wallet address or user info if authenticated
const displayAddress = user?.wallet?.address
? `${user.wallet.address.slice(0, 6)}...${user.wallet.address.slice(-4)}`
: 'Connected'
return (
<div className="dropdown dropdown-end">
<label tabIndex={0} className="btn btn-primary btn-sm">
{displayAddress}
</label>
<ul tabIndex={0} className="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52">
<li><a onClick={logout}>Disconnect</a></li>
</ul>
</div>
)
}
export function SecondaryNavbar() {
const { toggleApiUrl, isProd } = useApiUrlStore()
@@ -61,7 +93,7 @@ export function SecondaryNavbar() {
/>
</label>
</div>
<ConnectKitButton />
<PrivyWalletButton />
</div>
)
}

View File

@@ -0,0 +1,19 @@
import { arbitrum, mainnet } from 'viem/chains'
import { createConfig } from '@privy-io/wagmi'
import { http } from 'wagmi'
// Define the Privy App ID - this should be added to your .env file
const PRIVY_APP_ID = import.meta.env.VITE_PRIVY_APP_ID || 'your-privy-app-id'
// Create the Privy Wagmi config with Ethereum and Arbitrum chains
export const privyWagmiConfig = createConfig({
chains: [mainnet, arbitrum],
transports: {
// You can customize RPC providers here if needed
[mainnet.id]: http(`https://ethereum.publicnode.com`),
[arbitrum.id]: http(`https://arbitrum-one.publicnode.com`),
},
})
// Export the supported chains for use elsewhere in the app
export const supportedChains = [mainnet, arbitrum]

View File

@@ -0,0 +1,65 @@
import { usePrivy } from '@privy-io/react-auth'
import { useAccount, useConnect, useDisconnect } from 'wagmi'
import { useCallback, useEffect } from 'react'
/**
* Custom hook to integrate Privy wallet with wagmi
* This hook handles connecting and disconnecting the Privy wallet
* and provides a simplified interface for wallet interactions
*/
export const usePrivyWallet = () => {
const { user, ready, authenticated, login, logout } = usePrivy()
const { isConnected } = useAccount()
const { connectAsync, connectors } = useConnect()
const { disconnectAsync } = useDisconnect()
// Connect the wallet when the user is authenticated
useEffect(() => {
const connectWallet = async () => {
if (ready && authenticated && user && !isConnected) {
try {
// Get the embedded wallet if available
const embeddedWallet = user.wallet?.address ? user.wallet : null
if (embeddedWallet && connectors.length > 0) {
// Connect using the Privy connector
// The Privy connector is automatically added by the Privy WagmiProvider
const privyConnector = connectors.find(c => c.name === 'Privy') || connectors[0]
await connectAsync({
connector: privyConnector,
chainId: 1 // Default to Ethereum mainnet
})
}
} catch (error) {
console.error('Failed to connect wallet:', error)
}
}
}
connectWallet()
}, [ready, authenticated, user, isConnected, connectAsync, connectors])
// Disconnect the wallet when the user logs out
const handleLogout = useCallback(async () => {
if (isConnected) {
try {
await disconnectAsync()
} catch (error) {
console.error('Failed to disconnect wallet:', error)
}
}
logout()
}, [isConnected, disconnectAsync, logout])
return {
isAuthenticated: authenticated,
isConnected,
isReady: ready,
user,
login,
logout: handleLogout,
}
}
export default usePrivyWallet

View File

@@ -1,35 +1,47 @@
import './styles/globals.css'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ConnectKitProvider, getDefaultConfig } from 'connectkit'
import { PrivyProvider } from '@privy-io/react-auth'
import { WagmiProvider } from '@privy-io/wagmi'
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import { WagmiConfig, createConfig } from 'wagmi'
import App from './app'
import { privyWagmiConfig, supportedChains } from './config/privy'
import 'react-grid-layout/css/styles.css'
import 'react-resizable/css/styles.css'
import 'react-toastify/dist/ReactToastify.css'
import { ToastContainer } from 'react-toastify'
const config = createConfig(
getDefaultConfig({
alchemyId: import.meta.env.VITE_ALCHEMY_ID,
appName: 'Managing App',
walletConnectProjectId: import.meta.env.VITE_WALLET_CONNECT_PROJECT_ID,
})
)
const element = document.getElementById('root') as HTMLElement
const root = createRoot(element)
const queryClient = new QueryClient()
// Configure Privy login methods and appearance
const privyConfig = {
appearance: {
theme: 'light' as const,
accentColor: '#3B82F6' as `#${string}`, // Customize this to match your app's theme
logo: <img src="/src/assets/logo.svg" alt="logo" />,
},
loginMethods: ['wallet', 'email', 'google'] as Array<'wallet' | 'email' | 'google'>,
embeddedWallets: {
ethereum: {
createOnLogin: 'all-users' as const
},
noPromptOnSignature: false
},
}
root.render(
<QueryClientProvider client={queryClient}>
<WagmiConfig config={config}>
<ConnectKitProvider theme="auto">
<PrivyProvider
appId={import.meta.env.VITE_PRIVY_APP_ID}
config={privyConfig}
>
<WagmiProvider config={privyWagmiConfig}>
<BrowserRouter>
<App />
<ToastContainer
@@ -44,7 +56,7 @@ root.render(
pauseOnHover
/>
</BrowserRouter>
</ConnectKitProvider>
</WagmiConfig>
</WagmiProvider>
</PrivyProvider>
</QueryClientProvider>
)

View File

@@ -1,37 +1,50 @@
import { ConnectKitButton } from 'connectkit'
import { usePrivy } from '@privy-io/react-auth'
import { useAccount } from 'wagmi'
import LogIn from '../../components/mollecules/LogIn/LogIn'
import useCookie from '../../hooks/useCookie'
import { useEffect, useState } from 'react'
export const Auth = ({ children }: any) => {
const { getCookie, deleteCookie } = useCookie()
const { isConnected } = useAccount()
const { login, ready, authenticated, user } = usePrivy()
const token = getCookie('token')
console.log('token', token)
console.log('isConnected', isConnected)
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (ready) {
const timeout = setTimeout(() => {
setIsLoading(false);
}, 2000); // Adjust the timeout duration as needed
}, 1000);
return () => clearTimeout(timeout);
}, []);
}
}, [ready]);
if (isLoading) {
if (!ready || isLoading) {
return <div>Loading...</div>;
}
if (!isConnected) {
if (!authenticated) {
deleteCookie('token')
return (
<div style={{ ...styles }}>
<ConnectKitButton />
<button
onClick={login}
style={{
padding: '10px 20px',
backgroundColor: '#3B82F6',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '16px'
}}
>
Login with Privy
</button>
</div>
)
} else if (!token) {
@@ -41,7 +54,6 @@ export const Auth = ({ children }: any) => {
}
}
const styles = {
alignItems: 'center',
display: 'flex',