├── .env.example ├── .eslintignore ├── .eslintrc.json ├── .github ├── dependabot.yml └── workflows │ └── frontend.yml ├── .gitignore ├── .prettierrc ├── Dockerfile ├── LICENSE ├── README.md ├── halp ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── ai16z.png ├── bar-chart.svg ├── blue_flame.svg ├── bronze.png ├── clock.svg ├── copy-icon.svg ├── degenai.png ├── discord.svg ├── discordLogo.svg ├── favicon.ico ├── fonts │ └── Bootzy TM.ttf ├── gold.png ├── green_flame.svg ├── heart.svg ├── lock-03.svg ├── logo.svg ├── logo_old.svg ├── logo_solana.svg ├── menu_medal.svg ├── noname.svg ├── null.png ├── null.svg ├── profile_default.png ├── search-icon.svg ├── silver.png ├── solanaLogo.png ├── telegramLogo.svg ├── thumb.svg ├── token.svg ├── vercel.svg └── yellow_flame.svg ├── src ├── components │ ├── ContentContainer.module.css │ ├── ContentContainer.tsx │ ├── HomeView.tsx │ ├── NetworkSwitcher.tsx │ ├── Notification.tsx │ ├── RequestAirdrop.tsx │ ├── SendTransaction.tsx │ ├── SendVersionedTransaction.tsx │ ├── SignMessage.tsx │ ├── Text.tsx │ ├── eliza │ │ ├── AvatarUpload.module.css │ │ ├── AvatarUpload.tsx │ │ ├── CreateSection.module.css │ │ ├── CreateSection.tsx │ │ ├── NameInput.module.css │ │ ├── NameInput.tsx │ │ ├── PersonalitySelector.module.css │ │ ├── PersonalitySelector.tsx │ │ ├── PreviewCard.module.css │ │ ├── PreviewCard.tsx │ │ ├── PublishButton.module.css │ │ ├── PublishButton.tsx │ │ ├── SkillsList.module.css │ │ ├── SkillsList.tsx │ │ ├── SkillsSelector.module.css │ │ ├── SkillsSelector.tsx │ │ ├── SocialLinks.module.css │ │ ├── SocialLinks.tsx │ │ ├── TelegramSection.module.css │ │ └── TelegramSection.tsx │ ├── leaderboard │ │ ├── AvatarImage.tsx │ │ ├── LeaderboardHoldingTotals.module.css │ │ ├── LeaderboardHoldingTotals.tsx │ │ ├── LeaderboardHoldings.module.css │ │ ├── LeaderboardHoldings.tsx │ │ ├── LeaderboardMedals.module.css │ │ ├── LeaderboardMedals.tsx │ │ ├── LeaderboardPartners.module.css │ │ ├── LeaderboardPartners.tsx │ │ ├── LeaderboardTotals.module.css │ │ └── LeaderboardTotals.tsx │ ├── nav-element │ │ ├── Footer.tsx │ │ ├── NavBar.module.css │ │ ├── NavBar.tsx │ │ ├── PageSwitcher.tsx │ │ └── index.tsx │ ├── profile │ │ ├── ApiSection.module.css │ │ ├── ApiSection.tsx │ │ ├── ProfileData.module.css │ │ ├── ProfileData.tsx │ │ ├── ProfileHoldings.module.css │ │ ├── ProfileHoldings.tsx │ │ ├── ProfileTotals.module.css │ │ ├── ProfileTotals.tsx │ │ ├── ProfileWallets.module.css │ │ ├── ProfileWallets.tsx │ │ ├── Socials.module.css │ │ └── Socials.tsx │ ├── saas │ │ ├── BuyCredits.module.css │ │ ├── BuyCredits.tsx │ │ ├── GetStarted.module.css │ │ ├── GetStarted.tsx │ │ ├── Pricing.module.css │ │ └── Pricing.tsx │ ├── trades │ │ ├── Trades.module.css │ │ └── Trades.tsx │ └── trust │ │ ├── RecomendationsList.module.css │ │ ├── RecomendationsList.tsx │ │ ├── ScoreCard.module.css │ │ ├── ScoreCard.tsx │ │ ├── TrustScoreChart.module.css │ │ └── TrustScoreChart.tsx ├── contexts │ ├── AutoConnectProvider.tsx │ ├── ContextProvider.tsx │ └── NetworkConfigurationProvider.tsx ├── hooks │ ├── useDashboard.ts │ └── useQueryContext.tsx ├── models │ └── types.ts ├── pages │ ├── _app.tsx │ ├── _document.tsx │ ├── api │ │ ├── auth │ │ │ └── [...nextauth].ts │ │ ├── daoHoldings.ts │ │ ├── dashboard.ts │ │ ├── hello.ts │ │ ├── leaderboard.ts │ │ ├── partners.ts │ │ ├── proxy │ │ │ └── imageProxy.ts │ │ ├── tokenPrice.ts │ │ ├── trades │ │ │ └── getDaoTrades.ts │ │ ├── trustScore.ts │ │ ├── user │ │ │ ├── getProfile.ts │ │ │ └── getUsers.ts │ │ └── userHoldings.ts │ ├── auth │ │ ├── signin.module.css │ │ └── signin.tsx │ ├── credits.tsx │ ├── eliza.tsx │ ├── explorer.tsx │ ├── index.tsx │ ├── profile.tsx │ ├── saas.tsx │ └── trades.tsx ├── services │ ├── useTrades.tsx │ └── useUsers.tsx ├── stores │ ├── useNotificationStore.tsx │ └── useUserSOLBalanceStore.tsx ├── styles │ ├── globals.css │ └── profile.css ├── types │ ├── dashboard.ts │ ├── trust-score.d.ts │ └── user.ts ├── utils │ ├── axios.ts │ ├── explorer.ts │ ├── helpers.ts │ ├── index.tsx │ └── notifications.tsx └── views │ ├── credits │ ├── index.module.css │ └── index.tsx │ ├── eliza │ ├── index.module.css │ └── index.tsx │ ├── explorer │ ├── index.module.css │ └── index.tsx │ ├── home │ ├── index.module.css │ └── index.tsx │ ├── index.tsx │ ├── profile │ ├── index.module.css │ └── index.tsx │ ├── saas │ ├── index.module.css │ └── index.tsx │ └── trades │ └── index.tsx ├── tailwind.config.js └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SOLANA_API='' 2 | TWITTER_CLIENT_ID='' 3 | TWITTER_CLIENT_SECRET='' 4 | DISCORD_CLIENT_ID='' 5 | DISCORD_CLIENT_SECRET='' 6 | NEXTAUTH_SECRET= 7 | NEXTAUTH_URL='' -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | __generated__ -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "next" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | 13 | - package-ecosystem: "cargo" 14 | directory: "/program" 15 | schedule: 16 | interval: "daily" 17 | -------------------------------------------------------------------------------- /.github/workflows/frontend.yml: -------------------------------------------------------------------------------- 1 | name: Frontend 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/cache@v2 15 | with: 16 | path: "**/node_modules" 17 | key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} 18 | - uses: actions/setup-node@v2 19 | with: 20 | node-version: "16" 21 | - run: yarn install 22 | - name: Build 23 | run: yarn build 24 | -------------------------------------------------------------------------------- /.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 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env 29 | .env.local 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | 40 | # logs 41 | *.log -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "singleQuote": false, 5 | "semi": false, 6 | "trailingComma": "es5", 7 | "bracketSpacing": true, 8 | "bracketSameLine": false, 9 | "arrowParens": "always", 10 | "plugins": ["prettier-plugin-tailwindcss"] 11 | } 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12-alpine 2 | 3 | WORKDIR /opt/app 4 | 5 | ENV NODE_ENV production 6 | 7 | COPY package*.json ./ 8 | 9 | RUN npm ci 10 | 11 | COPY . /opt/app 12 | 13 | RUN npm install --dev && npm run build 14 | 15 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /halp: -------------------------------------------------------------------------------- 1 | feature/add-api-key 2 | feature/add-jupiter 3 | feature/collab 4 | feature/explorer 5 | feature/jupiter-modal 6 | * feature/saas 7 | fix-user-holdings 8 | main 9 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | images: { 5 | domains: [ 6 | "avatars.githubusercontent.com", 7 | "cdn.discordapp.com", 8 | "pbs.twimg.com", 9 | "images.ladbible.com", 10 | "t.me", 11 | "telegram.org", 12 | ], 13 | remotePatterns: [ 14 | { 15 | protocol: "https", 16 | hostname: "**.ipfs.nftstorage.link", 17 | }, 18 | ], 19 | }, 20 | } 21 | 22 | module.exports = nextConfig 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ai16z-partners-lounge", 3 | "version": "0.1.5", 4 | "author": "ai16z Partners ", 5 | "license": "MIT", 6 | "private": false, 7 | "scripts": { 8 | "dev": "next dev", 9 | "build": "next build", 10 | "start": "next start", 11 | "lint": "next lint" 12 | }, 13 | "dependencies": { 14 | "@babel/runtime": "^7.x", 15 | "@heroicons/react": "^1.0.5", 16 | "@noble/ed25519": "^1.7.1", 17 | "@solana/spl-token": "^0.4.9", 18 | "@solana/wallet-adapter-base": "^0.9.22", 19 | "@solana/wallet-adapter-react": "^0.15.35", 20 | "@solana/wallet-adapter-react-ui": "^0.9.35", 21 | "@solana/wallet-adapter-wallets": "^0.19.16", 22 | "@solana/web3.js": "^1.95.4", 23 | "@tailwindcss/typography": "^0.5.9", 24 | "axios": "^1.7.7", 25 | "bs58": "^4.0.1", 26 | "chart.js": "^4.4.6", 27 | "daisyui": "^1.24.3", 28 | "date-fns": "^2.29.3", 29 | "fastestsmallesttextencoderdecoder": "^1.0.22", 30 | "immer": "^9.0.12", 31 | "next": "^13.1.5", 32 | "next-auth": "^4.24.10", 33 | "next-compose-plugins": "^2.2.1", 34 | "next-transpile-modules": "^10.0.0", 35 | "node-cache": "^5.1.2", 36 | "react": "^18.2.0", 37 | "react-chartjs-2": "^5.2.0", 38 | "react-dom": "^18.2.0", 39 | "react-icons": "^5.3.0", 40 | "react-responsive": "^10.0.0", 41 | "tslib": "^2.6.2", 42 | "zustand": "^3.6.9" 43 | }, 44 | "devDependencies": { 45 | "@types/node": "^22.9.0", 46 | "@types/react": "^18.0.27", 47 | "autoprefixer": "^10.4.2", 48 | "eslint": "8.7.0", 49 | "eslint-config-next": "^13.1.5", 50 | "postcss": "^8.4.5", 51 | "prettier": "^3.3.3", 52 | "prettier-plugin-tailwindcss": "^0.6.8", 53 | "tailwindcss": "^3.4.14", 54 | "typescript": "^5.0.0" 55 | }, 56 | "engines": { 57 | "node": "20.x" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/ai16z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elizaOS/trust_scoreboard/34d89eb4387aa9f98cf5b5e0e0ab1d49ed0f5f8c/public/ai16z.png -------------------------------------------------------------------------------- /public/bar-chart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/blue_flame.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/bronze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elizaOS/trust_scoreboard/34d89eb4387aa9f98cf5b5e0e0ab1d49ed0f5f8c/public/bronze.png -------------------------------------------------------------------------------- /public/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/copy-icon.svg: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/degenai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elizaOS/trust_scoreboard/34d89eb4387aa9f98cf5b5e0e0ab1d49ed0f5f8c/public/degenai.png -------------------------------------------------------------------------------- /public/discord.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/discordLogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elizaOS/trust_scoreboard/34d89eb4387aa9f98cf5b5e0e0ab1d49ed0f5f8c/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/Bootzy TM.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elizaOS/trust_scoreboard/34d89eb4387aa9f98cf5b5e0e0ab1d49ed0f5f8c/public/fonts/Bootzy TM.ttf -------------------------------------------------------------------------------- /public/gold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elizaOS/trust_scoreboard/34d89eb4387aa9f98cf5b5e0e0ab1d49ed0f5f8c/public/gold.png -------------------------------------------------------------------------------- /public/green_flame.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/lock-03.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/logo_old.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /public/menu_medal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/noname.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/null.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elizaOS/trust_scoreboard/34d89eb4387aa9f98cf5b5e0e0ab1d49ed0f5f8c/public/null.png -------------------------------------------------------------------------------- /public/null.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/profile_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elizaOS/trust_scoreboard/34d89eb4387aa9f98cf5b5e0e0ab1d49ed0f5f8c/public/profile_default.png -------------------------------------------------------------------------------- /public/search-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/silver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elizaOS/trust_scoreboard/34d89eb4387aa9f98cf5b5e0e0ab1d49ed0f5f8c/public/silver.png -------------------------------------------------------------------------------- /public/solanaLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elizaOS/trust_scoreboard/34d89eb4387aa9f98cf5b5e0e0ab1d49ed0f5f8c/public/solanaLogo.png -------------------------------------------------------------------------------- /public/telegramLogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/thumb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/token.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /public/yellow_flame.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ContentContainer.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | flex: 1; 3 | width: 100%; 4 | padding: 0 140px; 5 | margin: 0 auto; 6 | } 7 | 8 | .content { 9 | height: 100%; 10 | width: 100%; 11 | display: flex; 12 | flex-direction: column; 13 | padding-top: 24px; 14 | } 15 | 16 | @media (max-width: 768px) { 17 | .container { 18 | padding: 0 16px; 19 | } 20 | 21 | .content { 22 | padding-top: 16px; 23 | } 24 | } -------------------------------------------------------------------------------- /src/components/ContentContainer.tsx: -------------------------------------------------------------------------------- 1 | import { FC, ReactNode } from "react" 2 | import styles from "./ContentContainer.module.css" 3 | 4 | interface Props { 5 | children: React.ReactNode 6 | } 7 | 8 | export const ContentContainer: FC<{ children: ReactNode }> = ({ children }) => { 9 | return ( 10 |
11 |
{children}
12 |
13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /src/components/HomeView.tsx: -------------------------------------------------------------------------------- 1 | // FILE: src/views/HomeView.tsx 2 | import { FC, useState } from "react" 3 | import LeaderboardTotals from "../components/leaderboard/LeaderboardTotals" 4 | import LeaderboardPartners from "../components/leaderboard/LeaderboardPartners" 5 | import LeaderboardHoldings from "../components/leaderboard/LeaderboardHoldings" 6 | 7 | export const HomeView: FC = () => { 8 | const [activeView, setActiveView] = useState<"partners" | "holdings">( 9 | "partners" 10 | ) 11 | 12 | return ( 13 |
14 |
15 |
16 | {/* Leaderboard Section */} 17 |
18 |

19 | Leaderboard 20 |

21 |
22 | 32 | 42 |
43 | 44 | {/*
45 | {activeView === 'partners' ? : } 46 |
*/} 47 |
48 |
49 |
50 |
51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /src/components/NetworkSwitcher.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import dynamic from 'next/dynamic'; 3 | import { useNetworkConfiguration } from '../contexts/NetworkConfigurationProvider'; 4 | 5 | const NetworkSwitcher: FC = () => { 6 | const { networkConfiguration, setNetworkConfiguration } = useNetworkConfiguration(); 7 | 8 | console.log(networkConfiguration); 9 | 10 | return ( 11 | 23 | ); 24 | }; 25 | 26 | export default dynamic(() => Promise.resolve(NetworkSwitcher), { 27 | ssr: false 28 | }) -------------------------------------------------------------------------------- /src/components/RequestAirdrop.tsx: -------------------------------------------------------------------------------- 1 | import { useConnection, useWallet } from '@solana/wallet-adapter-react'; 2 | import { LAMPORTS_PER_SOL, TransactionSignature } from '@solana/web3.js'; 3 | import { FC, useCallback } from 'react'; 4 | import { notify } from "../utils/notifications"; 5 | import useUserSOLBalanceStore from '../stores/useUserSOLBalanceStore'; 6 | 7 | export const RequestAirdrop: FC = () => { 8 | const { connection } = useConnection(); 9 | const { publicKey } = useWallet(); 10 | const { getUserSOLBalance } = useUserSOLBalanceStore(); 11 | 12 | const onClick = useCallback(async () => { 13 | if (!publicKey) { 14 | console.log('error', 'Wallet not connected!'); 15 | notify({ type: 'error', message: 'error', description: 'Wallet not connected!' }); 16 | return; 17 | } 18 | 19 | let signature: TransactionSignature = ''; 20 | 21 | try { 22 | signature = await connection.requestAirdrop(publicKey, LAMPORTS_PER_SOL); 23 | 24 | // Get the lates block hash to use on our transaction and confirmation 25 | let latestBlockhash = await connection.getLatestBlockhash() 26 | await connection.confirmTransaction({ signature, ...latestBlockhash }, 'confirmed'); 27 | 28 | notify({ type: 'success', message: 'Airdrop successful!', txid: signature }); 29 | 30 | getUserSOLBalance(publicKey, connection); 31 | } catch (error: any) { 32 | notify({ type: 'error', message: `Airdrop failed!`, description: error?.message, txid: signature }); 33 | console.log('error', `Airdrop failed! ${error?.message}`, signature); 34 | } 35 | }, [publicKey, connection, getUserSOLBalance]); 36 | 37 | return ( 38 | 39 |
40 |
41 |
43 | 44 | 51 |
52 |
53 | 54 | 55 | ); 56 | }; 57 | 58 | -------------------------------------------------------------------------------- /src/components/SendTransaction.tsx: -------------------------------------------------------------------------------- 1 | import { useConnection, useWallet } from '@solana/wallet-adapter-react'; 2 | import { Keypair, SystemProgram, Transaction, TransactionMessage, TransactionSignature, VersionedTransaction } from '@solana/web3.js'; 3 | import { FC, useCallback } from 'react'; 4 | import { notify } from "../utils/notifications"; 5 | 6 | export const SendTransaction: FC = () => { 7 | const { connection } = useConnection(); 8 | const { publicKey, sendTransaction } = useWallet(); 9 | 10 | const onClick = useCallback(async () => { 11 | if (!publicKey) { 12 | notify({ type: 'error', message: `Wallet not connected!` }); 13 | console.log('error', `Send Transaction: Wallet not connected!`); 14 | return; 15 | } 16 | 17 | let signature: TransactionSignature = ''; 18 | try { 19 | 20 | // Create instructions to send, in this case a simple transfer 21 | const instructions = [ 22 | SystemProgram.transfer({ 23 | fromPubkey: publicKey, 24 | toPubkey: Keypair.generate().publicKey, 25 | lamports: 1_000_000, 26 | }), 27 | ]; 28 | 29 | // Get the lates block hash to use on our transaction and confirmation 30 | let latestBlockhash = await connection.getLatestBlockhash() 31 | 32 | // Create a new TransactionMessage with version and compile it to legacy 33 | const messageLegacy = new TransactionMessage({ 34 | payerKey: publicKey, 35 | recentBlockhash: latestBlockhash.blockhash, 36 | instructions, 37 | }).compileToLegacyMessage(); 38 | 39 | // Create a new VersionedTransacction which supports legacy and v0 40 | const transation = new VersionedTransaction(messageLegacy) 41 | 42 | // Send transaction and await for signature 43 | signature = await sendTransaction(transation, connection); 44 | 45 | // Send transaction and await for signature 46 | await connection.confirmTransaction({ signature, ...latestBlockhash }, 'confirmed'); 47 | 48 | console.log(signature); 49 | notify({ type: 'success', message: 'Transaction successful!', txid: signature }); 50 | } catch (error: any) { 51 | notify({ type: 'error', message: `Transaction failed!`, description: error?.message, txid: signature }); 52 | console.log('error', `Transaction failed! ${error?.message}`, signature); 53 | return; 54 | } 55 | }, [publicKey, connection, sendTransaction]); 56 | 57 | return ( 58 |
59 |
60 |
62 | 73 |
74 |
75 | ); 76 | }; 77 | -------------------------------------------------------------------------------- /src/components/SendVersionedTransaction.tsx: -------------------------------------------------------------------------------- 1 | import { useConnection, useWallet } from '@solana/wallet-adapter-react'; 2 | import { Keypair, SystemProgram, TransactionMessage, TransactionSignature, VersionedTransaction } from '@solana/web3.js'; 3 | import { FC, useCallback } from 'react'; 4 | import { notify } from "../utils/notifications"; 5 | 6 | export const SendVersionedTransaction: FC = () => { 7 | const { connection } = useConnection(); 8 | const { publicKey, sendTransaction } = useWallet(); 9 | 10 | const onClick = useCallback(async () => { 11 | if (!publicKey) { 12 | notify({ type: 'error', message: `Wallet not connected!` }); 13 | console.log('error', `Send Transaction: Wallet not connected!`); 14 | return; 15 | } 16 | 17 | let signature: TransactionSignature = ''; 18 | try { 19 | 20 | // Create instructions to send, in this case a simple transfer 21 | const instructions = [ 22 | SystemProgram.transfer({ 23 | fromPubkey: publicKey, 24 | toPubkey: Keypair.generate().publicKey, 25 | lamports: 1_000_000, 26 | }), 27 | ]; 28 | 29 | // Get the lates block hash to use on our transaction and confirmation 30 | let latestBlockhash = await connection.getLatestBlockhash() 31 | 32 | // Create a new TransactionMessage with version and compile it to version 0 33 | const messageV0 = new TransactionMessage({ 34 | payerKey: publicKey, 35 | recentBlockhash: latestBlockhash.blockhash, 36 | instructions, 37 | }).compileToV0Message(); 38 | 39 | // Create a new VersionedTransacction to support the v0 message 40 | const transation = new VersionedTransaction(messageV0) 41 | 42 | // Send transaction and await for signature 43 | signature = await sendTransaction(transation, connection); 44 | 45 | // Await for confirmation 46 | await connection.confirmTransaction({ signature, ...latestBlockhash }, 'confirmed'); 47 | 48 | console.log(signature); 49 | notify({ type: 'success', message: 'Transaction successful!', txid: signature }); 50 | } catch (error: any) { 51 | notify({ type: 'error', message: `Transaction failed!`, description: error?.message, txid: signature }); 52 | console.log('error', `Transaction failed! ${error?.message}`, signature); 53 | return; 54 | } 55 | }, [publicKey, connection, sendTransaction]); 56 | 57 | return ( 58 |
59 |
60 |
62 | 73 |
74 |
75 | ); 76 | }; -------------------------------------------------------------------------------- /src/components/SignMessage.tsx: -------------------------------------------------------------------------------- 1 | // TODO: SignMessage 2 | import { verify } from '@noble/ed25519'; 3 | import { useWallet } from '@solana/wallet-adapter-react'; 4 | import bs58 from 'bs58'; 5 | import { FC, useCallback } from 'react'; 6 | import { notify } from "../utils/notifications"; 7 | 8 | export const SignMessage: FC = () => { 9 | const { publicKey, signMessage } = useWallet(); 10 | 11 | const onClick = useCallback(async () => { 12 | try { 13 | // `publicKey` will be null if the wallet isn't connected 14 | if (!publicKey) throw new Error('Wallet not connected!'); 15 | // `signMessage` will be undefined if the wallet doesn't support it 16 | if (!signMessage) throw new Error('Wallet does not support message signing!'); 17 | // Encode anything as bytes 18 | const message = new TextEncoder().encode('Hello, world!'); 19 | // Sign the bytes using the wallet 20 | const signature = await signMessage(message); 21 | // Verify that the bytes were signed using the private key that matches the known public key 22 | if (!verify(signature, message, publicKey.toBytes())) throw new Error('Invalid signature!'); 23 | notify({ type: 'success', message: 'Sign message successful!', txid: bs58.encode(signature) }); 24 | } catch (error: any) { 25 | notify({ type: 'error', message: `Sign Message failed!`, description: error?.message }); 26 | console.log('error', `Sign Message failed! ${error?.message}`); 27 | } 28 | }, [publicKey, signMessage]); 29 | 30 | return ( 31 |
32 |
33 |
35 | 46 |
47 |
48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /src/components/Text.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link" 2 | import React from "react" 3 | import { cn } from "@/utils" 4 | 5 | /** 6 | * Properties for a card component. 7 | */ 8 | type TextProps = { 9 | variant: 10 | | "big-heading" 11 | | "heading" 12 | | "sub-heading" 13 | | "nav-heading" 14 | | "nav" 15 | | "input" 16 | | "label" 17 | className?: string 18 | href?: string 19 | children?: React.ReactNode 20 | id?: string 21 | } 22 | 23 | /** 24 | * Pre-defined styling, according to agreed-upon design-system. 25 | */ 26 | const variants = { 27 | heading: "text-3xl font-medium", 28 | "sub-heading": "text-2xl font-medium", 29 | "nav-heading": "text-lg font-medium sm:text-xl", 30 | nav: "font-medium", 31 | paragraph: "text-lg", 32 | "sub-paragraph": "text-base font-medium text-inherit", 33 | input: "text-sm uppercase tracking-wide", 34 | label: "text-xs uppercase tracking-wide", 35 | } 36 | 37 | /** 38 | * Definition of a card component,the main purpose of 39 | * which is to neatly display information. Can be both 40 | * interactive and static. 41 | * 42 | * @param variant Variations relating to pre-defined styling of the element. 43 | * @param className Custom classes to be applied to the element. 44 | * @param children Child elements to be rendered within the component. 45 | */ 46 | const Text = ({ variant, className, href, children }: TextProps) => ( 47 |

48 | {href ? ( 49 | 53 | {children} 54 | 55 | ) : ( 56 | children 57 | )} 58 |

59 | ) 60 | 61 | export default Text 62 | -------------------------------------------------------------------------------- /src/components/eliza/AvatarUpload.module.css: -------------------------------------------------------------------------------- 1 | .avatarUpload { 2 | width: 120px; 3 | height: 120px; 4 | background: rgba(255, 255, 255, 0.1); 5 | border-radius: 60px; 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | cursor: pointer; 10 | transition: background-color 0.2s ease; 11 | } 12 | 13 | .avatarUpload:hover { 14 | background: rgba(255, 255, 255, 0.15); 15 | } 16 | 17 | .uploadButton { 18 | width: 40px; 19 | height: 40px; 20 | background: rgba(255, 255, 255, 0.2); 21 | border-radius: 20px; 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | font-size: 24px; 26 | color: #fff; 27 | transition: transform 0.2s ease; 28 | } 29 | 30 | .avatarUpload:hover .uploadButton { 31 | transform: scale(1.1); 32 | } -------------------------------------------------------------------------------- /src/components/eliza/AvatarUpload.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import styles from './AvatarUpload.module.css'; 3 | 4 | export const AvatarUpload: FC = () => { 5 | return ( 6 |
7 |
8 | + 9 |
10 |
11 | ); 12 | }; -------------------------------------------------------------------------------- /src/components/eliza/CreateSection.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import Image from 'next/image'; 3 | import styles from './CreateSection.module.css'; 4 | 5 | interface CreateSectionProps { 6 | characterName: string; 7 | setCharacterName: (name: string) => void; 8 | selectedPersonality: string; 9 | setSelectedPersonality: (personality: string) => void; 10 | selectedSkills: string[]; 11 | setSelectedSkills: (skills: string[]) => void; 12 | } 13 | 14 | export const CreateSection: FC = ({ 15 | characterName, 16 | setCharacterName, 17 | selectedPersonality, 18 | setSelectedPersonality, 19 | selectedSkills, 20 | setSelectedSkills, 21 | }) => { 22 | return ( 23 |
24 |

Create an NPC

25 |

Deploy and run your own NPC for your community

26 | 27 |
28 |
29 | + 30 |
31 |
32 | 33 |
34 | 35 | setCharacterName(e.target.value)} 40 | className={styles.input} 41 | /> 42 |
43 | 44 |
45 | 46 |
47 | 53 | 54 | 55 |
56 | Custom personalities coming soon. 57 |
58 | 59 |
60 | 61 |
62 | 68 | 69 | 70 | 71 |
72 |
73 | 74 | 75 |
76 |
77 | 78 |
79 |
80 | 81 |
82 | 83 | 84 |
85 | 86 | 89 |
90 | ); 91 | }; -------------------------------------------------------------------------------- /src/components/eliza/NameInput.module.css: -------------------------------------------------------------------------------- 1 | .formGroup { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 8px; 5 | width: 100%; 6 | } 7 | 8 | .formGroup label { 9 | font-size: 14px; 10 | font-weight: 500; 11 | color: rgba(255, 255, 255, 0.8); 12 | } 13 | 14 | .input { 15 | background: rgba(255, 255, 255, 0.1); 16 | border: 1px solid rgba(255, 255, 255, 0.2); 17 | border-radius: 8px; 18 | padding: 12px 16px; 19 | color: #fff; 20 | font-size: 16px; 21 | width: 100%; 22 | transition: border-color 0.2s ease; 23 | } 24 | 25 | .input:focus { 26 | border-color: rgba(255, 255, 255, 0.3); 27 | outline: none; 28 | } 29 | 30 | .input::placeholder { 31 | color: rgba(255, 255, 255, 0.4); 32 | } -------------------------------------------------------------------------------- /src/components/eliza/NameInput.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import styles from './NameInput.module.css'; 3 | 4 | interface NameInputProps { 5 | value: string; 6 | onChange: (value: string) => void; 7 | } 8 | 9 | export const NameInput: FC = ({ value, onChange }) => { 10 | return ( 11 |
12 | 13 | onChange(e.target.value)} 18 | className={styles.input} 19 | /> 20 |
21 | ); 22 | }; -------------------------------------------------------------------------------- /src/components/eliza/PersonalitySelector.module.css: -------------------------------------------------------------------------------- 1 | .formGroup { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 12px; 5 | width: 100%; 6 | } 7 | 8 | .formGroup label { 9 | font-size: 14px; 10 | font-weight: 500; 11 | color: rgba(255, 255, 255, 0.8); 12 | } 13 | 14 | .buttonGroup { 15 | display: flex; 16 | gap: 8px; 17 | flex-wrap: wrap; 18 | } 19 | 20 | .button { 21 | background: rgba(255, 255, 255, 0.1); 22 | border: 1px solid rgba(255, 255, 255, 0.2); 23 | border-radius: 8px; 24 | padding: 8px 16px; 25 | color: #fff; 26 | font-size: 14px; 27 | cursor: pointer; 28 | transition: all 0.2s ease; 29 | } 30 | 31 | .button:hover:not(:disabled) { 32 | background: rgba(255, 255, 255, 0.15); 33 | border-color: rgba(255, 255, 255, 0.25); 34 | } 35 | 36 | .button.selected { 37 | background: rgba(255, 255, 255, 0.2); 38 | border-color: rgba(255, 255, 255, 0.3); 39 | } 40 | 41 | .button:disabled { 42 | opacity: 0.5; 43 | cursor: not-allowed; 44 | } 45 | 46 | .helperText { 47 | font-size: 14px; 48 | color: rgba(255, 255, 255, 0.5); 49 | font-style: italic; 50 | } -------------------------------------------------------------------------------- /src/components/eliza/PersonalitySelector.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import styles from './PersonalitySelector.module.css'; 3 | 4 | interface PersonalitySelectorProps { 5 | selected: string; 6 | onSelect: (personality: string) => void; 7 | } 8 | 9 | export const PersonalitySelector: FC = ({ 10 | selected, 11 | onSelect 12 | }) => { 13 | return ( 14 |
15 | 16 |
17 | 23 | 24 | 25 |
26 | Custom personalities coming soon. 27 |
28 | ); 29 | }; -------------------------------------------------------------------------------- /src/components/eliza/PreviewCard.module.css: -------------------------------------------------------------------------------- 1 | .previewSection { 2 | display: flex; 3 | justify-content: center; 4 | align-items: flex-start; 5 | padding: 24px; 6 | } 7 | 8 | .previewCard { 9 | background: #fff; 10 | border-radius: 24px; 11 | padding: 32px; 12 | width: 100%; 13 | max-width: 400px; 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | gap: 16px; 18 | box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); 19 | } 20 | 21 | .previewAvatar { 22 | width: 80px; 23 | height: 80px; 24 | border-radius: 40px; 25 | overflow: hidden; 26 | background: #f5f5f5; 27 | display: flex; 28 | align-items: center; 29 | justify-content: center; 30 | } 31 | 32 | .previewAvatar img { 33 | width: 100%; 34 | height: 100%; 35 | object-fit: cover; 36 | } 37 | 38 | .previewTitle { 39 | font-size: 24px; 40 | font-weight: 600; 41 | color: #000; 42 | margin: 0; 43 | text-align: center; 44 | } 45 | 46 | .previewDescription { 47 | font-size: 16px; 48 | line-height: 1.5; 49 | color: rgba(0, 0, 0, 0.7); 50 | text-align: center; 51 | margin: 0; 52 | max-width: 280px; 53 | } 54 | 55 | @media (max-width: 768px) { 56 | .previewSection { 57 | padding: 16px; 58 | } 59 | 60 | .previewCard { 61 | padding: 24px; 62 | } 63 | 64 | .previewTitle { 65 | font-size: 20px; 66 | } 67 | 68 | .previewDescription { 69 | font-size: 14px; 70 | } 71 | } -------------------------------------------------------------------------------- /src/components/eliza/PreviewCard.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import Image from 'next/image'; 3 | import { SocialLinks } from './SocialLinks'; 4 | import { SkillsList } from './SkillsList'; 5 | import styles from './PreviewCard.module.css'; 6 | 7 | interface PreviewCardProps { 8 | name: string; 9 | description: string; 10 | avatar: string; 11 | } 12 | 13 | export const PreviewCard: FC = ({ 14 | name = 'Default NPC', 15 | description = 'I will manage your community, and respond to questions.', 16 | avatar = '/default-profile.png' 17 | }) => { 18 | return ( 19 |
20 |
21 |
22 | NPC Avatar 28 |
29 |

{name}

30 |

{description}

31 | 32 | 33 | 34 |
35 |
36 | ); 37 | }; -------------------------------------------------------------------------------- /src/components/eliza/PublishButton.module.css: -------------------------------------------------------------------------------- 1 | .publishButton { 2 | background: #000; 3 | border: none; 4 | border-radius: 8px; 5 | padding: 16px; 6 | color: #fff; 7 | font-size: 16px; 8 | font-weight: 600; 9 | cursor: pointer; 10 | width: 100%; 11 | transition: all 0.2s ease; 12 | margin-top: 16px; 13 | } 14 | 15 | .publishButton:hover { 16 | background: #111; 17 | transform: translateY(-1px); 18 | } 19 | 20 | .publishButton:active { 21 | transform: translateY(0); 22 | } 23 | 24 | @media (max-width: 768px) { 25 | .publishButton { 26 | padding: 14px; 27 | font-size: 14px; 28 | } 29 | } -------------------------------------------------------------------------------- /src/components/eliza/PublishButton.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import styles from './PublishButton.module.css'; 3 | 4 | export const PublishButton: FC = () => { 5 | return ( 6 | 9 | ); 10 | }; -------------------------------------------------------------------------------- /src/components/eliza/SkillsList.module.css: -------------------------------------------------------------------------------- 1 | .skillsSection { 2 | width: 100%; 3 | display: flex; 4 | flex-direction: column; 5 | gap: 12px; 6 | } 7 | 8 | .sectionTitle { 9 | font-size: 16px; 10 | font-weight: 600; 11 | color: #000; 12 | margin: 0; 13 | } 14 | 15 | .skillButtons { 16 | display: flex; 17 | flex-wrap: wrap; 18 | gap: 8px; 19 | } 20 | 21 | .skillButton { 22 | background: transparent; 23 | border: 1px solid rgba(0, 0, 0, 0.2); 24 | border-radius: 20px; 25 | padding: 8px 16px; 26 | color: #000; 27 | font-size: 14px; 28 | transition: all 0.2s ease; 29 | } 30 | 31 | .skillButton:hover { 32 | background: rgba(0, 0, 0, 0.05); 33 | border-color: rgba(0, 0, 0, 0.3); 34 | } -------------------------------------------------------------------------------- /src/components/eliza/SkillsList.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import styles from './SkillsList.module.css'; 3 | 4 | export const SkillsList: FC = () => { 5 | return ( 6 |
7 |

Skills

8 |
9 | 10 | 11 | 12 | 13 |
14 |
15 | ); 16 | }; -------------------------------------------------------------------------------- /src/components/eliza/SkillsSelector.module.css: -------------------------------------------------------------------------------- 1 | .formGroup { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 12px; 5 | width: 100%; 6 | } 7 | 8 | .formGroup label { 9 | font-size: 14px; 10 | font-weight: 500; 11 | color: rgba(255, 255, 255, 0.8); 12 | } 13 | 14 | .buttonGroup { 15 | display: flex; 16 | gap: 8px; 17 | flex-wrap: wrap; 18 | margin-bottom: 8px; 19 | } 20 | 21 | .button { 22 | background: rgba(255, 255, 255, 0.1); 23 | border: 1px solid rgba(255, 255, 255, 0.2); 24 | border-radius: 8px; 25 | padding: 8px 16px; 26 | color: #fff; 27 | font-size: 14px; 28 | cursor: pointer; 29 | transition: all 0.2s ease; 30 | } 31 | 32 | .button:hover:not(:disabled) { 33 | background: rgba(255, 255, 255, 0.15); 34 | border-color: rgba(255, 255, 255, 0.25); 35 | } 36 | 37 | .button.selected { 38 | background: rgba(255, 255, 255, 0.2); 39 | border-color: rgba(255, 255, 255, 0.3); 40 | } 41 | 42 | .button:disabled { 43 | opacity: 0.5; 44 | cursor: not-allowed; 45 | } 46 | 47 | .accountSection { 48 | margin-top: 8px; 49 | } 50 | 51 | .disconnectButton { 52 | background: rgba(255, 0, 0, 0.1); 53 | border: 1px solid rgba(255, 0, 0, 0.2); 54 | border-radius: 8px; 55 | padding: 8px 16px; 56 | color: #ff4444; 57 | font-size: 14px; 58 | cursor: pointer; 59 | transition: all 0.2s ease; 60 | } 61 | 62 | .disconnectButton:hover { 63 | background: rgba(255, 0, 0, 0.15); 64 | border-color: rgba(255, 0, 0, 0.25); 65 | } -------------------------------------------------------------------------------- /src/components/eliza/SkillsSelector.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import styles from './SkillsSelector.module.css'; 3 | 4 | interface SkillsSelectorProps { 5 | selectedSkills: string[]; 6 | onSelectSkill: (skills: string[]) => void; 7 | } 8 | 9 | export const SkillsSelector: FC = ({ 10 | selectedSkills, 11 | onSelectSkill 12 | }) => { 13 | const handleSkillToggle = (skill: string) => { 14 | if (selectedSkills.includes(skill)) { 15 | onSelectSkill(selectedSkills.filter(s => s !== skill)); 16 | } else { 17 | onSelectSkill([...selectedSkills, skill]); 18 | } 19 | }; 20 | 21 | return ( 22 |
23 | 24 |
25 | 31 | 32 | 33 | 34 |
35 |
36 | 37 | 38 |
39 |
40 | 41 |
42 |
43 | ); 44 | }; -------------------------------------------------------------------------------- /src/components/eliza/SocialLinks.module.css: -------------------------------------------------------------------------------- 1 | .socialSection { 2 | width: 100%; 3 | display: flex; 4 | flex-direction: column; 5 | gap: 12px; 6 | } 7 | 8 | .sectionTitle { 9 | font-size: 16px; 10 | font-weight: 600; 11 | color: #000; 12 | margin: 0; 13 | } 14 | 15 | .socialButtons { 16 | display: flex; 17 | flex-wrap: wrap; 18 | gap: 8px; 19 | } 20 | 21 | .socialButton { 22 | background: #000; 23 | border: none; 24 | border-radius: 20px; 25 | padding: 8px 16px; 26 | color: #fff; 27 | font-size: 14px; 28 | display: flex; 29 | align-items: center; 30 | gap: 8px; 31 | transition: all 0.2s ease; 32 | } 33 | 34 | .socialButton:hover { 35 | opacity: 0.9; 36 | transform: translateY(-1px); 37 | } -------------------------------------------------------------------------------- /src/components/eliza/SocialLinks.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import Image from 'next/image'; 3 | import styles from './SocialLinks.module.css'; 4 | 5 | export const SocialLinks: FC = () => { 6 | return ( 7 |
8 |

Socials

9 |
10 | 14 | 18 | 22 |
23 |
24 | ); 25 | }; -------------------------------------------------------------------------------- /src/components/eliza/TelegramSection.module.css: -------------------------------------------------------------------------------- 1 | .formGroup { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 12px; 5 | width: 100%; 6 | } 7 | 8 | .formGroup label { 9 | font-size: 14px; 10 | font-weight: 500; 11 | color: rgba(255, 255, 255, 0.8); 12 | } 13 | 14 | .connectButton { 15 | background: rgba(255, 255, 255, 0.1); 16 | border: 1px solid rgba(255, 255, 255, 0.2); 17 | border-radius: 8px; 18 | padding: 12px 16px; 19 | color: #fff; 20 | font-size: 14px; 21 | cursor: pointer; 22 | transition: all 0.2s ease; 23 | width: fit-content; 24 | } 25 | 26 | .connectButton:hover { 27 | background: rgba(255, 255, 255, 0.15); 28 | border-color: rgba(255, 255, 255, 0.25); 29 | } 30 | 31 | .connectButton:active { 32 | background: rgba(255, 255, 255, 0.2); 33 | } -------------------------------------------------------------------------------- /src/components/eliza/TelegramSection.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import styles from './TelegramSection.module.css'; 3 | 4 | export const TelegramSection: FC = () => { 5 | return ( 6 |
7 | 8 | 11 |
12 | ); 13 | }; -------------------------------------------------------------------------------- /src/components/leaderboard/AvatarImage.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image" 2 | import { useState } from "react" 3 | 4 | interface AvatarProps { 5 | src?: string 6 | name: string 7 | size?: number 8 | className?: string 9 | } 10 | 11 | export const AvatarWithFallback = ({ 12 | src, 13 | name, 14 | size = 34, 15 | className, 16 | }: AvatarProps) => { 17 | const [error, setError] = useState(false) 18 | const initial = name?.[0]?.toUpperCase() || "?" 19 | const fontSize = size / 2 20 | 21 | const proxiedSrc = src 22 | ? `/api/proxy/imageProxy?url=${encodeURIComponent(src)}` 23 | : undefined 24 | 25 | return ( 26 | <> 27 | {!error && proxiedSrc && ( 28 | {`${name}'s setError(true)} 35 | /> 36 | )} 37 |
44 | {initial} 45 |
46 | 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /src/components/leaderboard/LeaderboardHoldingTotals.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from 'react'; 2 | import { useEffect, useState } from 'react'; 3 | import styles from './LeaderboardHoldingTotals.module.css'; 4 | 5 | interface DashboardData { 6 | partners: { 7 | owner: string; 8 | amount: number; 9 | createdAt: string; 10 | }[]; 11 | prices: { 12 | address: string; 13 | usdPrice: number; 14 | }[]; 15 | holdings: { 16 | value: string; 17 | }[]; 18 | } 19 | 20 | const AI16Z_ADDRESS = 'HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC'; 21 | const AI16Z_TOTAL_SUPPLY = 1099999775.54; 22 | 23 | const LeaderboardHoldingTotals: FC = () => { 24 | const [data, setData] = useState(null); 25 | const [isLoading, setIsLoading] = useState(true); 26 | const [error, setError] = useState(''); 27 | 28 | useEffect(() => { 29 | const fetchData = async () => { 30 | try { 31 | const response = await fetch('/api/dashboard'); 32 | if (!response.ok) { 33 | throw new Error('Failed to fetch data'); 34 | } 35 | const result = await response.json(); 36 | // Validate the data structure 37 | if (!result.partners || !result.prices || !result.holdings) { 38 | throw new Error('Invalid data format received'); 39 | } 40 | setData(result); 41 | } catch (err) { 42 | setError(err instanceof Error ? err.message : 'Failed to fetch data'); 43 | } finally { 44 | setIsLoading(false); 45 | } 46 | }; 47 | fetchData(); 48 | }, []); 49 | 50 | if (isLoading) return
Loading...
; 51 | if (error) return
{error}
; 52 | if (!data?.partners || !data?.prices || !data?.holdings) return null; 53 | 54 | // Calculate metrics 55 | const ai16zPrice = data.prices.find(p => p.address === AI16Z_ADDRESS)?.usdPrice || 0; 56 | const marketCap = ai16zPrice * AI16Z_TOTAL_SUPPLY; 57 | 58 | const totalValue = data.holdings.reduce((sum, holding) => { 59 | const value = parseFloat(holding.value.replace(/[$,]/g, '')); 60 | return sum + (isNaN(value) ? 0 : value); 61 | }, 0); 62 | 63 | const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); 64 | const newPartners = data.partners?.filter(partner => 65 | new Date(partner.createdAt) > sevenDaysAgo)?.length || 0; 66 | 67 | return ( 68 |
69 |
70 |
71 | ${(marketCap / 1000000).toFixed(2)}m 72 |
73 |
MARKET CAP
74 |
75 |
76 |
77 | ${(totalValue / 1000000).toFixed(2)}m 78 |
79 |
AUM
80 |
81 |
82 |
+{newPartners}
83 |
NEW PARTNERS (7D)
84 |
85 |
86 | ); 87 | }; 88 | 89 | export default LeaderboardHoldingTotals; 90 | -------------------------------------------------------------------------------- /src/components/leaderboard/LeaderboardMedals.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | justify-content: center; 4 | align-items: flex-end; 5 | padding: 1rem 1.5rem; 6 | margin: 1rem 0; 7 | gap: 10px; 8 | } 9 | 10 | .loading { 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | padding: 2rem; 15 | } 16 | 17 | .medalHolder { 18 | display: flex; 19 | flex-direction: column; 20 | min-width: 8rem; 21 | max-width: 10rem; 22 | min-height: 155px; 23 | align-items: center; 24 | gap: 0.75rem; 25 | transition: all 0.3s ease; 26 | } 27 | 28 | .firstPlace { 29 | /* transform: scale(1.15); */ 30 | margin: 0 1rem; 31 | min-height: 198px; 32 | } 33 | 34 | .imageWrapper { 35 | position: relative; 36 | width: 5rem; 37 | height: 5rem; 38 | } 39 | 40 | .firstPlaceImage { 41 | width: 7.5rem; 42 | height: 7.5rem; 43 | } 44 | 45 | .imageBorder { 46 | position: absolute; 47 | inset: 0; 48 | border-radius: 50%; 49 | padding: 3px; 50 | } 51 | 52 | .userImage { 53 | border-radius: 50%; 54 | object-fit: cover; 55 | width: 100%; 56 | height: 100%; 57 | } 58 | 59 | .medal { 60 | position: absolute; 61 | top: 1px; 62 | left: -4px; 63 | display: flex; 64 | width: 30px; 65 | height: 30px; 66 | padding: 8px 14px; 67 | justify-content: center; 68 | align-items: center; 69 | gap: 14px; 70 | z-index: 1; 71 | font-family: var(--font-family-base); 72 | border-radius: 100px; 73 | } 74 | 75 | .firstPlacemedal { 76 | width: 2.5rem !important; 77 | height: 2.5rem !important; 78 | } 79 | 80 | .gold { 81 | background-color: #ffa600; 82 | @apply text-xl font-bold text-white; 83 | } 84 | 85 | .silver { 86 | background-color: #858585; 87 | @apply text-base font-bold text-white; 88 | } 89 | 90 | .bronze { 91 | background-color: #d56b00; 92 | @apply text-base font-bold text-white; 93 | } 94 | 95 | .userInfo { 96 | display: flex; 97 | flex-direction: column; 98 | align-items: center; 99 | gap: 0.5rem; 100 | overflow: hidden; 101 | max-width: 100%; 102 | } 103 | 104 | .name { 105 | text-align: center; 106 | font-family: var(--font-family-base); 107 | max-width: 100%; 108 | overflow: hidden; 109 | text-overflow: ellipsis; 110 | white-space: nowrap; 111 | @apply text-lg font-semibold text-white; 112 | } 113 | 114 | .scoreWrapper { 115 | display: flex; 116 | justify-content: center; 117 | padding: 4px 14px; 118 | align-items: center; 119 | gap: 14px; 120 | border-radius: 100px; 121 | } 122 | 123 | .score { 124 | font-weight: 600; 125 | font-size: 1rem; 126 | color: white; 127 | } 128 | 129 | .firstPlaceScore { 130 | font-size: 1.125rem; 131 | } 132 | 133 | .placeholderImage { 134 | width: 100%; 135 | height: 100%; 136 | border-radius: 50%; 137 | } 138 | 139 | @media (max-width: 640px) { 140 | .container { 141 | gap: 1rem; 142 | padding: 1rem; 143 | } 144 | 145 | .medalHolder { 146 | max-width: 100px; 147 | min-width: 100px; 148 | } 149 | 150 | .firstPlace { 151 | /* transform: scale(1.1); */ 152 | margin: 0 0.5rem; 153 | } 154 | 155 | .imageWrapper { 156 | width: 4rem; 157 | height: 4rem; 158 | } 159 | 160 | .firstPlaceImage { 161 | width: 6.25rem; 162 | height: 6.25rem; 163 | } 164 | 165 | .medal { 166 | width: 30px; 167 | height: 30px; 168 | } 169 | 170 | .score { 171 | font-size: 0.875rem; 172 | } 173 | 174 | .firstPlaceScore { 175 | font-size: 1rem; 176 | } 177 | } 178 | 179 | @keyframes pulse { 180 | 0%, 181 | 100% { 182 | opacity: 0.7; 183 | } 184 | 185 | 50% { 186 | opacity: 0.4; 187 | } 188 | } 189 | 190 | .animate-pulse { 191 | animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; 192 | } 193 | 194 | .skeletonCircle { 195 | background-color: #242424; 196 | border: 2px solid #3c3c3c; 197 | border-radius: 50%; 198 | } 199 | 200 | .skeletonText { 201 | height: 1rem; 202 | width: 80px; 203 | background-color: #242424; 204 | border: 1px solid #3c3c3c; 205 | border-radius: 0.25rem; 206 | margin-top: 0.5rem; 207 | } 208 | 209 | .skeletonScore { 210 | height: 1.5rem; 211 | width: 4rem; 212 | background-color: #242424; 213 | border: 1px solid #3c3c3c; 214 | border-radius: 0.75rem; 215 | margin-top: 0.25rem; 216 | } 217 | -------------------------------------------------------------------------------- /src/components/leaderboard/LeaderboardPartners.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from "react" 2 | import Image from "next/image" 3 | import styles from "./LeaderboardPartners.module.css" 4 | import { useMediaQuery } from "react-responsive" 5 | import { TUser } from "@/types/user" 6 | import { AvatarWithFallback } from "./AvatarImage" 7 | 8 | const SkeletonRow = () => ( 9 |
10 |
11 |
12 |
13 |
14 | ) 15 | 16 | const LeaderboardPartners: FC<{ users: TUser[]; isLoading: boolean }> = ({ 17 | users, 18 | isLoading, 19 | }) => { 20 | const isMobile = useMediaQuery({ maxWidth: 768 }) 21 | const SKELETON_ROWS = 5 22 | 23 | const getRankDisplay = (rank: number) => { 24 | switch (rank) { 25 | case 1: 26 | return
🥇
27 | case 2: 28 | return
🥈
29 | case 3: 30 | return
🥉
31 | default: 32 | return
#{rank}
33 | } 34 | } 35 | 36 | const getTrustScoreClass = (rank: number): string => { 37 | switch (rank) { 38 | case 1: 39 | return styles.trustScoreGold 40 | case 2: 41 | return styles.trustScoreSilver 42 | case 3: 43 | return styles.trustScoreBronze 44 | default: 45 | return styles.text3 46 | } 47 | } 48 | 49 | const renderUserRow = (user: TUser) => ( 50 |
51 |
52 | 57 |
58 |
{user.name}
59 |
60 |
61 |
62 |
63 | {user.score === 0 ? ( 64 |
65 | Null trust score 72 | 73 | AI Marc is Calculating Trust 74 | 75 |
76 | ) : ( 77 |
78 | {user.score.toFixed(1)} 79 |
80 | )} 81 |
82 | {user.score ? ( 83 |
{getRankDisplay(user.rank)}
84 | ) : null} 85 |
86 |
87 | ) 88 | 89 | return ( 90 |
91 | {isLoading ? ( 92 | <> 93 | {[...Array(SKELETON_ROWS)].map((_, index) => ( 94 | 95 | ))} 96 | 97 | ) : !users?.length ? ( 98 |
No partners found
99 | ) : ( 100 | <>{users.map(renderUserRow)} 101 | )} 102 |
103 | ) 104 | } 105 | 106 | export default LeaderboardPartners 107 | -------------------------------------------------------------------------------- /src/components/leaderboard/LeaderboardTotals.module.css: -------------------------------------------------------------------------------- 1 | .frameParent { 2 | width: 579px; 3 | max-width: 100%; 4 | position: relative; 5 | border-radius: 14px; 6 | background: linear-gradient(180deg, #f98c13, #ffaf03); 7 | display: flex; 8 | flex-direction: column; 9 | align-items: flex-start; 10 | justify-content: flex-start; 11 | padding: 18px; 12 | box-sizing: border-box; 13 | text-align: left; 14 | font-size: 24px; 15 | color: #fff; 16 | font-family: var(--font-family-base); 17 | } 18 | 19 | .instanceParent { 20 | align-self: stretch; 21 | display: flex; 22 | flex-direction: row; 23 | align-items: flex-start; 24 | justify-content: flex-start; 25 | gap: 24px; 26 | } 27 | 28 | .numberAndBadgeParent { 29 | display: flex; 30 | flex-direction: column; 31 | align-items: flex-start; 32 | justify-content: flex-start; 33 | } 34 | 35 | .numberAndBadge { 36 | align-self: stretch; 37 | display: flex; 38 | flex-direction: row; 39 | align-items: flex-end; 40 | justify-content: flex-start; 41 | } 42 | 43 | .number { 44 | position: relative; 45 | line-height: 32px; 46 | font-weight: 600; 47 | font-size: 24px; 48 | } 49 | 50 | .headingWrapper { 51 | align-self: stretch; 52 | display: flex; 53 | flex-direction: row; 54 | align-items: center; 55 | justify-content: flex-start; 56 | font-size: 12px; 57 | color: rgba(255, 255, 255, 0.7); 58 | font-family: var(--font-family-base); 59 | } 60 | 61 | .heading { 62 | position: relative; 63 | line-height: 140%; 64 | text-transform: uppercase; 65 | font-weight: 600; 66 | } 67 | 68 | @media (max-width: 768px) { 69 | .frameParent { 70 | width: 100%; 71 | padding: 16px; 72 | } 73 | .instanceParent { 74 | flex-direction: row; 75 | gap: 4px; 76 | } 77 | 78 | .frameParent { 79 | padding: 16px; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/components/leaderboard/LeaderboardTotals.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from 'react'; 2 | import { useEffect, useState } from 'react'; 3 | import styles from './LeaderboardTotals.module.css'; 4 | 5 | interface DashboardData { 6 | partners: { 7 | owner: string; 8 | amount: number; 9 | createdAt: string; 10 | }[]; 11 | prices: { 12 | address: string; 13 | usdPrice: number; 14 | }[]; 15 | } 16 | 17 | const AI16Z_ADDRESS = 'HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC'; 18 | const DECIMALS = 1_000_000_000; 19 | 20 | const LeaderboardTotals: FC = () => { 21 | const [data, setData] = useState(null); 22 | const [isLoading, setIsLoading] = useState(true); 23 | const [error, setError] = useState(''); 24 | 25 | useEffect(() => { 26 | const fetchData = async () => { 27 | try { 28 | const response = await fetch('/api/dashboard'); 29 | if (!response.ok) throw new Error('Failed to fetch data'); 30 | const result = await response.json(); 31 | setData(result); 32 | } catch (err) { 33 | setError(err instanceof Error ? err.message : 'Failed to fetch data'); 34 | } finally { 35 | setIsLoading(false); 36 | } 37 | }; 38 | 39 | fetchData(); 40 | }, []); 41 | 42 | const calculateMetrics = () => { 43 | if (!data?.partners || !data?.prices) return { 44 | totalPartners: 0, 45 | totalWorth: 0, 46 | newPartners: 0 47 | }; 48 | 49 | const totalPartners = data.partners.length; 50 | const helpPrice = data.prices.find(p => p.address === AI16Z_ADDRESS)?.usdPrice || 0; 51 | const totalWorth = data.partners.reduce((sum, partner) => sum + partner.amount * helpPrice, 0); 52 | const newPartners = data.partners.filter(partner => 53 | new Date(partner.createdAt) > new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) 54 | ).length; 55 | 56 | return { totalPartners, totalWorth, newPartners }; 57 | }; 58 | 59 | const renderMetric = (value: string | number, label: string) => ( 60 |
61 |
62 |
{value}
63 |
64 |
65 |
{label}
66 |
67 |
68 | ); 69 | 70 | const metrics = calculateMetrics(); 71 | 72 | return ( 73 |
74 |
75 | {isLoading ? ( 76 | <> 77 | {renderMetric('-', 'PARTNERS')} 78 | {renderMetric('-', 'TOTAL WORTH')} 79 | {renderMetric('-', 'NEW PARTNERS (7D)')} 80 | 81 | ) : error ? ( 82 | <> 83 | {renderMetric('-', 'PARTNERS')} 84 | {renderMetric('-', 'TOTAL WORTH')} 85 | {renderMetric('-', 'NEW PARTNERS (7D)')} 86 | 87 | ) : ( 88 | <> 89 | {renderMetric(metrics.totalPartners.toLocaleString(), 'PARTNERS')} 90 | {renderMetric(`$${(metrics.totalWorth / 1000000).toFixed(2)}m`, 'TOTAL WORTH')} 91 | {renderMetric(`+${metrics.newPartners}`, 'NEW PARTNERS (7D)')} 92 | 93 | )} 94 |
95 |
96 | ); 97 | }; 98 | 99 | export default LeaderboardTotals; 100 | -------------------------------------------------------------------------------- /src/components/nav-element/Footer.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import Link from 'next/link'; 3 | import Image from 'next/image'; 4 | 5 | export const Footer: FC = () => { 6 | return ( 7 |
8 | {/* Social Links */} 9 |
10 | 11 |
12 | 13 | 14 | 15 |
16 | 17 | 18 |
19 | 20 | 21 | 22 |
23 | 24 |
25 | 26 | {/* Copyright */} 27 |
28 | © {new Date().getFullYear()} ai16z Partners. All rights reserved. 29 |
30 |
31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /src/components/nav-element/NavBar.module.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | width: 100%; 3 | height: auto; 4 | min-height: 64px; 5 | display: flex; 6 | align-items: center; 7 | justify-content: space-between; 8 | padding: 8px 16px; 9 | gap: 16px; 10 | } 11 | 12 | .logoContainer { 13 | flex-shrink: 0; 14 | } 15 | 16 | .logoLink { 17 | display: flex; 18 | align-items: center; 19 | } 20 | 21 | .searchContainer { 22 | flex: 1; 23 | max-width: 600px; 24 | position: relative; 25 | display: flex; 26 | align-items: center; 27 | } 28 | 29 | .searchInput { 30 | width: 100%; 31 | height: 40px; 32 | padding: 8px 40px 8px 16px; 33 | border-radius: 20px; 34 | border: 1px solid #9b8d7d; 35 | background-color: white; 36 | font-size: 14px; 37 | color: #242424; 38 | transition: all 0.2s ease; 39 | } 40 | 41 | .searchInput:focus { 42 | outline: none; 43 | border-color: #f98c13; 44 | box-shadow: 0 0 0 2px rgba(249, 140, 19, 0.1); 45 | } 46 | 47 | .actionsContainer { 48 | display: flex; 49 | align-items: center; 50 | gap: 16px; 51 | } 52 | 53 | .actionButton { 54 | background-color: #f98c13; 55 | color: white; 56 | font-weight: 600; 57 | padding: 8px 16px; 58 | border-radius: 14px; 59 | font-size: 14px; 60 | transition: all 0.2s ease; 61 | text-decoration: none; 62 | white-space: nowrap; 63 | } 64 | 65 | .actionButton:hover { 66 | background-color: #e07a0f; 67 | } 68 | 69 | .profileContainer { 70 | display: flex; 71 | align-items: center; 72 | padding: 4px; 73 | border-radius: 50%; 74 | transition: background-color 0.2s ease; 75 | } 76 | 77 | .profileContainer:hover { 78 | background-color: rgba(255, 255, 255, 0.1); 79 | } 80 | 81 | .profileImage { 82 | width: 32px; 83 | height: 32px; 84 | border-radius: 50%; 85 | object-fit: cover; 86 | } 87 | 88 | .profileText { 89 | color: #9a8c7c; 90 | font-weight: 500; 91 | font-size: 14px; 92 | } 93 | 94 | .iconButton { 95 | display: flex; 96 | align-items: center; 97 | justify-content: center; 98 | padding: 8px; 99 | border-radius: 50%; 100 | transition: background-color 0.2s ease; 101 | margin-right: 8px; 102 | } 103 | 104 | .iconButton:hover { 105 | background-color: rgba(255, 255, 255, 0.1); 106 | } 107 | 108 | @media (max-width: 768px) { 109 | .navbar { 110 | flex-wrap: wrap; 111 | padding: 8px; 112 | gap: 8px; 113 | } 114 | 115 | .searchContainer { 116 | order: 3; 117 | min-width: 100%; 118 | margin-top: 8px; 119 | } 120 | 121 | .actionsContainer { 122 | gap: 8px; 123 | } 124 | 125 | .actionButton { 126 | padding: 6px 10px; 127 | font-size: 12px; 128 | } 129 | 130 | .profileContainer { 131 | padding: 6px 12px; 132 | } 133 | 134 | .profileText { 135 | font-size: 12px; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/components/nav-element/NavBar.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from "react" 2 | import Link from "next/link" 3 | import Image from "next/image" 4 | import { useSession } from "next-auth/react" 5 | import styles from "./NavBar.module.css" 6 | import { useState } from "react" 7 | import { RiBarChart2Fill } from "react-icons/ri" 8 | 9 | interface NavBarProps { 10 | // Add any props if needed 11 | children?: React.ReactNode 12 | } 13 | 14 | const NavBar: FC = ({ children }) => { 15 | const { data: session } = useSession() 16 | const [searchQuery, setSearchQuery] = useState("") 17 | 18 | const handleSearch = (e: React.FormEvent) => { 19 | e.preventDefault() 20 | // Add search functionality 21 | } 22 | 23 | return ( 24 | 29 | ) 30 | } 31 | 32 | export default NavBar 33 | -------------------------------------------------------------------------------- /src/components/nav-element/PageSwitcher.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link" 2 | import { usePathname } from "next/navigation" 3 | 4 | const navItems = [ 5 | { href: "/", label: "Leaderboard" }, 6 | { href: "/trades", label: "Trades" }, 7 | ] 8 | 9 | export default function PageSwitcher({ className }: { className?: string }) { 10 | const pathname = usePathname() 11 | 12 | return ( 13 |
14 |
15 | {navItems.map((item) => ( 16 | 25 | 26 | {item.label} 27 | 28 | 29 | ))} 30 |
31 |
32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /src/components/nav-element/index.tsx: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-empty */ 2 | import Link from 'next/link'; 3 | import Text from '../Text'; 4 | import { cn } from '../../utils'; 5 | import { useRouter } from 'next/router'; 6 | import { useEffect, useRef } from 'react'; 7 | 8 | type NavElementProps = { 9 | label: string; 10 | href: string; 11 | as?: string; 12 | scroll?: boolean; 13 | chipLabel?: string; 14 | disabled?: boolean; 15 | navigationStarts?: () => void; 16 | }; 17 | 18 | const NavElement = ({ 19 | label, 20 | href, 21 | as, 22 | scroll, 23 | disabled, 24 | navigationStarts = () => {}, 25 | }: NavElementProps) => { 26 | const router = useRouter(); 27 | const isActive = href === router.asPath || (as && as === router.asPath); 28 | const divRef = useRef(null); 29 | 30 | useEffect(() => { 31 | if (divRef.current) { 32 | divRef.current.className = cn( 33 | 'h-0.5 w-1/4 transition-all duration-300 ease-out', 34 | isActive 35 | ? '!w-full bg-gradient-to-l from-fuchsia-500 to-pink-500 ' 36 | : 'group-hover:w-1/2 group-hover:bg-fuchsia-500', 37 | ); 38 | } 39 | }, [isActive]); 40 | 41 | return ( 42 | navigationStarts()} 53 | > 54 |
55 | {label} 56 |
57 |
58 | 59 | ); 60 | }; 61 | 62 | export default NavElement; 63 | 64 | -------------------------------------------------------------------------------- /src/components/profile/ApiSection.module.css: -------------------------------------------------------------------------------- 1 | .title { 2 | font-size: 24px; 3 | font-weight: 600; 4 | color: #242424; 5 | padding: 24px 0px 10px; 6 | margin: 0 24px; 7 | } 8 | 9 | .buttonParent { 10 | display: flex; 11 | flex-direction: row; 12 | align-items: center; 13 | gap: 12px; 14 | width: calc(100% - 48px); 15 | margin: 0 24px; 16 | } 17 | 18 | .button { 19 | border-radius: 14px; 20 | background-color: #f98c13; 21 | height: 38px; 22 | overflow: hidden; 23 | display: flex; 24 | flex-direction: row; 25 | align-items: center; 26 | justify-content: center; 27 | padding: 8px 0px; 28 | box-sizing: border-box; 29 | max-width: 300px; 30 | transition: all 0.2s ease; 31 | cursor: pointer; 32 | } 33 | 34 | .checkoutButton { 35 | background-color: #FF4463; 36 | margin-left: auto; 37 | } 38 | 39 | .button.active { 40 | background-color: #f98c13; 41 | } 42 | 43 | .button:hover { 44 | transform: translateY(-1px); 45 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 46 | } 47 | 48 | .text { 49 | position: relative; 50 | line-height: 22px; 51 | font-weight: 600; 52 | margin: 0 14px; 53 | color: #fff; 54 | } 55 | 56 | @media (max-width: 768px) { 57 | .buttonParent { 58 | width: calc(100% - 48px); 59 | margin: 0 24px; 60 | flex-wrap: wrap; 61 | } 62 | 63 | .checkoutButton { 64 | margin-left: 0; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/components/profile/ApiSection.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { useRouter } from 'next/router'; 3 | import styles from './ApiSection.module.css'; 4 | 5 | export const ApiSection: FC = () => { 6 | const router = useRouter(); 7 | 8 | const handleNavigateToSaas = () => { 9 | router.push('/saas'); 10 | }; 11 | 12 | const handleNavigateToEliza = () => { 13 | router.push('/eliza'); 14 | }; 15 | 16 | const handleNavigateToCheckout = () => { 17 | router.push('/eliza'); // Points to eliza/index 18 | }; 19 | 20 | return ( 21 |
22 |

23 |
24 |
28 |
29 | Get API Access 30 |
31 |
32 | 33 | 34 |
38 |
39 | Get an Eliza 40 |
41 |
42 |
43 |
44 | ); 45 | }; 46 | 47 | export default ApiSection; 48 | -------------------------------------------------------------------------------- /src/components/profile/ProfileData.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 100%; 3 | display: flex; 4 | justify-content: center; 5 | padding: 14px 24px; 6 | max-width: 800px; 7 | margin: 0 auto; 8 | } 9 | 10 | .profileContainer { 11 | display: flex; 12 | align-items: center; 13 | flex-direction: column; 14 | gap: 16px; 15 | } 16 | 17 | .userInfo { 18 | display: flex; 19 | flex-direction: column; 20 | gap: 4px; 21 | justify-content: center; 22 | } 23 | 24 | .userName { 25 | font-family: 'SF Compact Round', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 26 | font-size: 28px; 27 | font-weight: 600; 28 | color: #fff; 29 | margin: 0; 30 | } 31 | 32 | .userRole { 33 | font-family: 'SF Compact Round', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 34 | font-size: 18px; 35 | font-weight: 600; 36 | color: #7E7E7E; 37 | } 38 | 39 | .imageContainer { 40 | display: flex; 41 | align-items: center; 42 | justify-content: center; 43 | width: 120px; 44 | height: 120px; 45 | } 46 | 47 | .profileImage { 48 | border-radius: 50%; 49 | object-fit: cover; 50 | width: 100%; 51 | height: 100%; 52 | } 53 | 54 | .signOutButton { 55 | background-color: #9B8D7D; 56 | color: white; 57 | font-weight: 600; 58 | padding: 8px 16px; 59 | border-radius: 14px; 60 | font-size: 14px; 61 | border: none; 62 | cursor: pointer; 63 | transition: all 0.2s ease; 64 | margin-left: auto; 65 | height: 38px; 66 | white-space: nowrap; 67 | } 68 | 69 | .signOutButton:hover { 70 | background-color: #8a7f71; 71 | transform: translateY(-1px); 72 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 73 | } 74 | 75 | .signOutButton:active { 76 | transform: translateY(0); 77 | box-shadow: none; 78 | } 79 | 80 | @media (max-width: 768px) { 81 | .container { 82 | padding: 14px 16px 24px; 83 | } 84 | 85 | .userName { 86 | font-size: 24px; 87 | } 88 | 89 | .userRole { 90 | font-size: 16px; 91 | } 92 | } -------------------------------------------------------------------------------- /src/components/profile/ProfileData.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from "react"; 2 | import { useSession, signOut } from "next-auth/react"; 3 | import Image from "next/image"; 4 | import styles from "./ProfileData.module.css"; 5 | 6 | export const ProfileData: FC = () => { 7 | const { data: session } = useSession(); 8 | 9 | 10 | 11 | // Helper function to determine the user role 12 | const getUserRole = () => { 13 | if (!session?.user?.connections) return "Partner"; 14 | if ("telegram" in session.user.connections) return "Telegram"; 15 | if ("discord" in session.user.connections) return "Discord"; 16 | return "Partner"; 17 | }; 18 | 19 | return ( 20 |
21 |
22 | {session?.user?.image && ( 23 |
24 | Profile 33 |
34 | )} 35 |
36 |

37 | {session?.user?.name || "Anonymous"} 38 |

39 | {getUserRole()} 40 |
41 |
42 |
43 | ); 44 | }; 45 | 46 | export default ProfileData; 47 | -------------------------------------------------------------------------------- /src/components/profile/ProfileTotals.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 100%; 3 | /* max-width: 800px; */ 4 | /* margin: 0 auto; */ 5 | padding: 24px; 6 | } 7 | 8 | .metricsGrid { 9 | display: grid; 10 | grid-template-columns: repeat(2, 1fr); 11 | gap: 12px; 12 | } 13 | 14 | .metricCard { 15 | background: #2D2D2D;; 16 | backdrop-filter: blur(4px); 17 | border-radius: 24px; 18 | padding: 14px; 19 | position: relative; 20 | overflow: hidden; 21 | } 22 | 23 | .metricCard:first-child { 24 | grid-column: 1 / -1; 25 | background: #2D2D2D;; 26 | } 27 | 28 | .metricContent { 29 | display: flex; 30 | align-items: center; 31 | gap: 12px; 32 | position: relative; 33 | z-index: 1; 34 | } 35 | 36 | .metricsAi { 37 | color: #7E7E7E; 38 | font-size: 14px; 39 | font-family: 'SF Compact Round', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 40 | font-weight: 500; 41 | line-height: 18px; 42 | } 43 | 44 | .metricInfo { 45 | display: flex; 46 | flex-direction: column; 47 | gap: 4px; 48 | } 49 | 50 | .value { 51 | font-family: 'SF Compact Round', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 52 | font-size: 22px; 53 | font-weight: 600; 54 | } 55 | 56 | .trustScorevalue { 57 | font-family: 'SF Compact Round', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 58 | font-size: 38px; 59 | font-weight: 600; 60 | } 61 | 62 | .label { 63 | font-family: 'SF Compact Round', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 64 | font-size: 16px; 65 | font-weight: 600; 66 | color: #FFFFFF 67 | } 68 | 69 | .progressBar { 70 | position: relative; 71 | margin-top: 8px; 72 | /* Adds some space between the progress bar and the bottom of the card */ 73 | height: 12px; 74 | /* Slightly thicker to make it stand out */ 75 | background: #FF4463; 76 | /* Set to the specified color */ 77 | border-radius: 50px; 78 | transition: width 0.3s ease; 79 | } 80 | 81 | .progressContainer { 82 | position: relative; 83 | margin-top: 8px; 84 | /* Space between the progress bar and bottom */ 85 | height: 12px; 86 | background: #8B2C45; 87 | /* Darker version of #FF4463 */ 88 | border-radius: 50px; 89 | } 90 | 91 | @media (max-width: 768px) { 92 | .container { 93 | padding: 16px; 94 | } 95 | 96 | .metricsGrid { 97 | grid-template-columns: 1fr; 98 | } 99 | 100 | .value { 101 | font-size: 20px; 102 | } 103 | 104 | .metricsAi { 105 | font-size: 13px; 106 | } 107 | } 108 | 109 | -------------------------------------------------------------------------------- /src/components/profile/ProfileWallets.module.css: -------------------------------------------------------------------------------- 1 | .number { 2 | position: relative; 3 | line-height: 24px; 4 | font-weight: 600; 5 | } 6 | .text { 7 | position: relative; 8 | line-height: 22px; 9 | font-weight: 600; 10 | color: #fff; 11 | white-space: nowrap; 12 | } 13 | .button { 14 | border-radius: 14px; 15 | background-color: #9b8d7d !important; 16 | height: 38px; 17 | overflow: hidden; 18 | display: flex; 19 | flex-direction: row; 20 | align-items: center; 21 | justify-content: center; 22 | padding: 8px 14px; 23 | box-sizing: border-box; 24 | min-width: 140px; 25 | transition: all 0.2s ease; 26 | cursor: pointer; 27 | border: none !important; 28 | } 29 | .button.active { 30 | background-color: #f98c13 !important; 31 | } 32 | .button:hover { 33 | transform: translateY(-1px); 34 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 35 | background-color: #8a7f71 !important; 36 | } 37 | .button.active:hover { 38 | background-color: #e07b0b !important; 39 | } 40 | .button:active { 41 | transform: translateY(0); 42 | box-shadow: none; 43 | } 44 | .buttonWrapper { 45 | align-self: stretch; 46 | display: flex; 47 | flex-direction: row; 48 | align-items: flex-start; 49 | justify-content: flex-start; 50 | flex-wrap: wrap; 51 | align-content: flex-start; 52 | text-align: center; 53 | font-size: 16px; 54 | color: #fff; 55 | } 56 | .numberParent { 57 | width: 100%; 58 | position: relative; 59 | display: flex; 60 | flex-direction: column; 61 | align-items: flex-start; 62 | justify-content: flex-start; 63 | padding: 0; 64 | box-sizing: border-box; 65 | gap: 10px; 66 | text-align: left; 67 | font-size: 18px; 68 | color: #242424; 69 | font-family: var(--font-family-base); 70 | margin: 0 14px; 71 | } 72 | .title { 73 | font-size: 24px; 74 | font-weight: 600; 75 | color: #fff; 76 | padding: 24px 0px 10px; 77 | margin: 0; 78 | } 79 | .buttonParent { 80 | display: flex; 81 | flex-direction: row; 82 | align-items: center; 83 | gap: 12px; 84 | width: 100%; 85 | } 86 | 87 | @media (max-width: 768px) { 88 | .numberParent, 89 | .buttonParent { 90 | width: 100%; 91 | } 92 | 93 | .title { 94 | padding: 24px 0px 10px; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/components/profile/ProfileWallets.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { useWallet } from '@solana/wallet-adapter-react'; 3 | import { WalletMultiButton } from '@solana/wallet-adapter-react-ui'; 4 | import styles from './ProfileWallets.module.css'; 5 | 6 | interface WalletDisplayProps { 7 | truncateLength?: number; 8 | } 9 | 10 | const truncateAddress = (address: string, length: number = 4): string => { 11 | if (!address) return ''; 12 | return `${address.slice(0, length)}...${address.slice(-length)}`; 13 | }; 14 | 15 | const ProfileWallets: FC = ({ truncateLength = 4 }) => { 16 | const { publicKey, disconnect, connect, wallet } = useWallet(); 17 | 18 | const handleWalletClick = async () => { 19 | if (publicKey) { 20 | try { 21 | await disconnect(); 22 | } catch (error) { 23 | console.error('Failed to disconnect wallet:', error); 24 | } 25 | } else if (wallet) { 26 | try { 27 | await connect(); 28 | } catch (error) { 29 | console.error('Failed to connect wallet:', error); 30 | } 31 | } 32 | }; 33 | 34 | return ( 35 |
36 |

37 |
38 | {wallet ? ( 39 |
45 |
46 | {publicKey 47 | ? truncateAddress(publicKey.toString(), truncateLength) 48 | : 'Connect Wallet'} 49 |
50 |
51 | ) : ( 52 | 53 |
Select Wallet
54 |
55 | )} 56 |
57 |
58 | ); 59 | }; 60 | 61 | export default ProfileWallets; -------------------------------------------------------------------------------- /src/components/profile/Socials.module.css: -------------------------------------------------------------------------------- 1 | .number { 2 | position: relative; 3 | line-height: 24px; 4 | font-weight: 600; 5 | } 6 | .text { 7 | position: relative; 8 | line-height: 22px; 9 | font-weight: 600; 10 | margin: 0 14px; 11 | 12 | } 13 | .button { 14 | border-radius: 14px; 15 | background-color: #9B8D7D; /* Default gray state */ 16 | height: 38px; 17 | overflow: hidden; 18 | display: flex; 19 | flex-direction: row; 20 | align-items: center; 21 | justify-content: center; 22 | padding: 8px 0px; 23 | box-sizing: border-box; 24 | max-width: 300px; 25 | transition: all 0.2s ease; 26 | cursor: pointer; 27 | } 28 | 29 | .button.active { 30 | background-color: #f98c13; /* Orange when signed in */ 31 | } 32 | 33 | .button:hover { 34 | transform: translateY(-1px); 35 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 36 | } 37 | .button1 { 38 | border-radius: 14px; 39 | background-color: #9b8d7d; 40 | height: 38px; 41 | overflow: hidden; 42 | display: flex; 43 | flex-direction: row; 44 | align-items: center; 45 | justify-content: center; 46 | padding: 8px 0px; 47 | box-sizing: border-box; 48 | max-width: 600px; 49 | } 50 | .button1:hover { 51 | transform: translateY(-1px); 52 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 53 | transition: all 0.2s ease; 54 | } 55 | .buttonParent { 56 | display: flex; 57 | flex-direction: row; 58 | align-items: left; 59 | gap: 12px; 60 | width: 100%; 61 | } 62 | .numberParent { 63 | width: 100%; 64 | position: relative; 65 | display: flex; 66 | flex-direction: column; 67 | align-items: flex-start; 68 | justify-content: flex-start; 69 | padding: 0; 70 | box-sizing: border-box; 71 | gap: 10px; 72 | text-align: left; 73 | font-size: 18px; 74 | color: #242424; 75 | } 76 | .userName { 77 | margin: 0 8px; 78 | font-size: 14px; 79 | opacity: 0.9; 80 | color: #fff; 81 | } 82 | .providerIcon { 83 | margin-right: 12px; 84 | width: 20px; 85 | height: 20px; 86 | } 87 | .title { 88 | font-size: 24px; 89 | font-weight: 600; 90 | color: #fff; 91 | padding: 24px 0px 10px; 92 | margin: 0; 93 | } 94 | 95 | @media (max-width: 768px) { 96 | .numberParent, 97 | .buttonParent { 98 | width: 100%; 99 | } 100 | 101 | .title { 102 | padding: 24px 0px 10px; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/components/profile/Socials.tsx: -------------------------------------------------------------------------------- 1 | import { signIn, signOut, useSession } from "next-auth/react"; 2 | import { FC, useEffect } from "react"; 3 | import styles from "./Socials.module.css"; 4 | 5 | interface SocialButtonProps { 6 | provider: string; 7 | isActive?: boolean; 8 | } 9 | 10 | const SocialButton: FC = ({ provider }) => { 11 | const { data: session, status } = useSession(); 12 | 13 | useEffect(() => { 14 | console.log("Session updated:", { 15 | session, 16 | status, 17 | provider, 18 | account: session?.user?.connections?.[provider.toLowerCase()], 19 | }); 20 | }, [session, status, provider]); 21 | 22 | // Check if this provider is the active one 23 | const isThisProviderActive = 24 | !!session?.user?.connections?.[provider.toLowerCase()]; 25 | 26 | const handleClick = () => { 27 | if (session) { 28 | signOut(); 29 | } else { 30 | signIn(provider.toLowerCase()); 31 | } 32 | }; 33 | 34 | return ( 35 |
41 |
42 | {isThisProviderActive ? `${provider}` : `Connect with ${provider}`} 43 |
44 | {isThisProviderActive && session?.user?.name && ( 45 | ({session.user.name}) 46 | )} 47 |
48 | ); 49 | }; 50 | 51 | export const Socials: FC = () => { 52 | // Commented out GitHub and Twitter, only using Discord for now 53 | const providers = ["Discord"]; // ['GitHub', 'Discord', 'Twitter']; 54 | 55 | return ( 56 |
57 |

58 |
59 | {providers.map((provider) => ( 60 | 61 | ))} 62 |
63 |
64 | ); 65 | }; 66 | 67 | export default Socials; 68 | -------------------------------------------------------------------------------- /src/components/saas/BuyCredits.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 100%; 3 | max-width: 1200px; 4 | margin: 0 auto; 5 | padding: 32px 16px; 6 | border-radius: 14px; 7 | } 8 | 9 | .header { 10 | text-align: center; 11 | margin-bottom: 32px; 12 | padding: 0 16px; 13 | } 14 | 15 | .title { 16 | font-size: clamp(24px, 5vw, 36px); 17 | font-weight: 600; 18 | color: #242424; 19 | margin-bottom: 12px; 20 | } 21 | 22 | .subtitle { 23 | font-size: clamp(14px, 4vw, 18px); 24 | color: #666; 25 | max-width: 600px; 26 | margin: 0 auto; 27 | } 28 | 29 | .creditGrid { 30 | display: grid; 31 | grid-template-columns: repeat(3, 1fr); 32 | gap: 24px; 33 | margin-top: 24px; 34 | padding: 0 8px; 35 | } 36 | 37 | .creditCard { 38 | background-color: #E8E3D6; 39 | border-radius: 12px; 40 | padding: clamp(20px, 4vw, 32px); 41 | color: black; 42 | transition: all 0.3s ease; 43 | border: 2px solid transparent; 44 | width: 100%; 45 | max-width: 400px; 46 | margin: 0 auto; 47 | position: relative; 48 | } 49 | 50 | .highlighted { 51 | background-color: #F98C12; 52 | color: white; 53 | position: relative; 54 | } 55 | 56 | .popularBadge { 57 | position: absolute; 58 | top: -12px; 59 | left: 50%; 60 | transform: translateX(-50%); 61 | background-color: #242424; 62 | color: white; 63 | padding: 4px 12px; 64 | border-radius: 20px; 65 | font-size: 12px; 66 | font-weight: 600; 67 | } 68 | 69 | .tierName { 70 | font-size: clamp(20px, 4vw, 24px); 71 | font-weight: 600; 72 | margin-bottom: 12px; 73 | text-align: center; 74 | } 75 | 76 | .credits { 77 | font-size: clamp(32px, 6vw, 42px); 78 | font-weight: 700; 79 | text-align: center; 80 | margin-bottom: 8px; 81 | } 82 | 83 | .price { 84 | font-size: clamp(28px, 5vw, 36px); 85 | font-weight: 700; 86 | text-align: center; 87 | margin-bottom: 16px; 88 | } 89 | 90 | .currency { 91 | font-size: 0.6em; 92 | vertical-align: super; 93 | margin-right: 2px; 94 | } 95 | 96 | .description { 97 | text-align: center; 98 | margin-bottom: 24px; 99 | font-size: clamp(14px, 3vw, 16px); 100 | opacity: 0.9; 101 | } 102 | 103 | .features { 104 | list-style: none; 105 | padding: 0; 106 | margin: 0 0 24px 0; 107 | } 108 | 109 | .feature { 110 | display: flex; 111 | align-items: center; 112 | gap: 8px; 113 | margin-bottom: 12px; 114 | font-size: clamp(13px, 3vw, 14px); 115 | line-height: 1.4; 116 | } 117 | 118 | .checkmark { 119 | color: #F98C12; 120 | font-weight: bold; 121 | } 122 | 123 | .highlighted .checkmark { 124 | color: white; 125 | } 126 | 127 | @media (max-width: 1024px) { 128 | .creditGrid { 129 | grid-template-columns: repeat(2, 1fr); 130 | } 131 | } 132 | 133 | @media (max-width: 768px) { 134 | .container { 135 | padding: 24px 12px; 136 | } 137 | 138 | .creditGrid { 139 | grid-template-columns: 1fr; 140 | gap: 20px; 141 | } 142 | 143 | .creditCard { 144 | padding: 24px 20px; 145 | } 146 | } 147 | 148 | @media (max-width: 480px) { 149 | .container { 150 | padding: 16px 8px; 151 | } 152 | 153 | .header { 154 | margin-bottom: 24px; 155 | } 156 | 157 | .creditGrid { 158 | gap: 16px; 159 | } 160 | 161 | .popularBadge { 162 | font-size: 10px; 163 | padding: 4px 8px; 164 | } 165 | } -------------------------------------------------------------------------------- /src/components/saas/BuyCredits.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import styles from './BuyCredits.module.css'; 3 | import GetStarted from './GetStarted'; 4 | 5 | interface CreditPackage { 6 | name: string; 7 | credits: number; 8 | price: number; 9 | description: string; 10 | features: string[]; 11 | highlighted?: boolean; 12 | } 13 | 14 | const creditPackages: CreditPackage[] = [ 15 | { 16 | name: "Starter", 17 | credits: 1000, 18 | price: 9, 19 | description: "For testing and small projects", 20 | features: [ 21 | "1,000 API Request Credits", 22 | "30 Days Validity", 23 | "Basic Support", 24 | "Real-time Access", 25 | "Basic Analytics" 26 | ] 27 | }, 28 | { 29 | name: "Growth", 30 | credits: 5000, 31 | price: 39, 32 | description: "Ideal for growing applications", 33 | features: [ 34 | "5,000 API Credits", 35 | "60 Days Validity", 36 | "Priority Support", 37 | "Real-time Access", 38 | "Advanced Analytics" 39 | ], 40 | highlighted: true 41 | }, 42 | { 43 | name: "Scale", 44 | credits: 20000, 45 | price: 149, 46 | description: "For high-volume applications", 47 | features: [ 48 | "20,000 API Credits", 49 | "90 Days Validity", 50 | "Premium Support", 51 | "Real-time Access", 52 | "Enterprise Analytics" 53 | ] 54 | } 55 | ]; 56 | 57 | const CreditCard: FC = ({ name, credits, price, description, features, highlighted }) => ( 58 |
59 | {highlighted &&
MOST POPULAR
} 60 |

{name}

61 |
{credits.toLocaleString()}
62 |
63 | $ 64 | {price} 65 |
66 |

{description}

67 |
    68 | {features.map((feature, index) => ( 69 |
  • 70 | 71 | {feature} 72 |
  • 73 | ))} 74 |
75 | 80 |
81 | ); 82 | 83 | const BuyCredits: FC = () => { 84 | return ( 85 |
86 |
87 |

Make More Requests

88 |

Choose the request package that suits your needs

89 |
90 |
91 | {creditPackages.map((pkg, index) => ( 92 | 93 | ))} 94 |
95 |
96 | ); 97 | }; 98 | 99 | export default BuyCredits; -------------------------------------------------------------------------------- /src/components/saas/GetStarted.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | width: 100%; 3 | padding: 12px 24px; 4 | border: none; 5 | border-radius: 8px; 6 | background-color: #F98C12; 7 | color: white; 8 | font-weight: 600; 9 | cursor: pointer; 10 | transition: all 0.2s ease; 11 | font-size: 16px; 12 | } 13 | 14 | .button:disabled { 15 | opacity: 0.7; 16 | cursor: not-allowed; 17 | } 18 | 19 | .button:hover:not(:disabled) { 20 | transform: translateY(-2px); 21 | box-shadow: 0 4px 12px rgba(249, 140, 18, 0.3); 22 | } 23 | 24 | .highlighted { 25 | background-color: white; 26 | color: #F98C12; 27 | } -------------------------------------------------------------------------------- /src/components/saas/GetStarted.tsx: -------------------------------------------------------------------------------- 1 | import { FC, useState } from 'react'; 2 | import { useWallet } from '@solana/wallet-adapter-react'; 3 | import { 4 | LAMPORTS_PER_SOL, 5 | PublicKey, 6 | Transaction, 7 | SystemProgram, 8 | Connection 9 | } from '@solana/web3.js'; 10 | import { useConnection } from '@solana/wallet-adapter-react'; 11 | import styles from './GetStarted.module.css'; 12 | 13 | interface GetStartedProps { 14 | tier: string; 15 | price: number; 16 | isHighlighted?: boolean; 17 | } 18 | 19 | // Move this to a safe initialization 20 | const getTreasuryWallet = (): PublicKey | null => { 21 | try { 22 | const walletAddress = process.env.NEXT_PUBLIC_TREASURY_WALLET_SOL; 23 | if (!walletAddress) { 24 | console.error('Treasury wallet address not found in environment variables'); 25 | return null; 26 | } 27 | return new PublicKey(walletAddress); 28 | } catch (e) { 29 | console.error('Invalid treasury wallet address:', e); 30 | return null; 31 | } 32 | }; 33 | 34 | const TREASURY_WALLET = getTreasuryWallet(); 35 | const HELIUS_RPC = process.env.NEXT_PUBLIC_SOLANA_API ? 36 | `https://rpc.helius.xyz/?api-key=${process.env.NEXT_PUBLIC_SOLANA_API}` : 37 | null; 38 | 39 | const GetStarted: FC = ({ tier, price, isHighlighted }) => { 40 | const { publicKey, sendTransaction } = useWallet(); 41 | const { connection } = useConnection(); 42 | const [isLoading, setIsLoading] = useState(false); 43 | 44 | const handlePayment = async () => { 45 | if (!publicKey || !TREASURY_WALLET || !HELIUS_RPC) { 46 | console.error('Missing required configuration'); 47 | return; 48 | } 49 | 50 | setIsLoading(true); 51 | try { 52 | // ... rest of the payment logic 53 | } catch (error) { 54 | console.error('Payment failed:', error); 55 | } finally { 56 | setIsLoading(false); 57 | } 58 | }; 59 | 60 | return ( 61 | 68 | ); 69 | }; 70 | 71 | export default GetStarted; -------------------------------------------------------------------------------- /src/components/saas/Pricing.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import Link from 'next/link'; 3 | import styles from './Pricing.module.css'; 4 | import GetStarted from './GetStarted'; 5 | 6 | interface PricingTier { 7 | name: string; 8 | price: number; 9 | description: string; 10 | features: string[]; 11 | requestLimit: string; 12 | highlighted?: boolean; 13 | } 14 | 15 | const pricingTiers: PricingTier[] = [ 16 | { 17 | name: "Basic", 18 | price: 49, 19 | description: "Perfect for getting started with Trust Score API", 20 | requestLimit: "10,000 requests/month", 21 | features: [ 22 | "Trust Score API Access", 23 | "Basic Documentation", 24 | "Email Support", 25 | "Real-time Updates", 26 | "Basic Analytics" 27 | ] 28 | }, 29 | { 30 | name: "Pro", 31 | price: 149, 32 | description: "For growing projects that need more power", 33 | requestLimit: "50,000 requests/month", 34 | features: [ 35 | "Everything in Basic", 36 | "Advanced Analytics", 37 | "Priority Support", 38 | "Historical Data Access", 39 | "Custom Webhooks" 40 | ], 41 | highlighted: true 42 | }, 43 | { 44 | name: "Enterprise", 45 | price: 499, 46 | description: "For large-scale applications that need advanced features", 47 | requestLimit: "200,000 requests/month", 48 | features: [ 49 | "Everything in Pro", 50 | "Dedicated Support", 51 | "Custom Integration", 52 | "SLA Guarantee", 53 | "White-label Options" 54 | ] 55 | } 56 | ]; 57 | 58 | const PricingCard: FC = ({ name, price, description, features, requestLimit, highlighted }) => ( 59 |
60 |

{name}

61 |
62 | $ 63 | {price} 64 | /month 65 |
66 |

{description}

67 |
{requestLimit}
68 |
    69 | {features.map((feature, index) => ( 70 |
  • 71 | 72 | {feature} 73 |
  • 74 | ))} 75 |
76 | 81 |
82 | ); 83 | 84 | const Pricing: FC = () => { 85 | return ( 86 |
87 |
88 |

Simple, Transparent Pricing

89 |

Choose the plan that's right for you

90 |
91 |
92 | {pricingTiers.map((tier, index) => ( 93 | 94 | ))} 95 |
96 |
97 |

Need more?

98 | 99 | Buy Credits 100 | 101 |
102 |
103 | ); 104 | }; 105 | 106 | export default Pricing; -------------------------------------------------------------------------------- /src/components/trust/RecomendationsList.module.css: -------------------------------------------------------------------------------- 1 | .frameParent { 2 | width: 100%; 3 | max-width: 100%; 4 | margin: 0 auto; 5 | padding: 8px; 6 | color: #9B8D7D; 7 | } 8 | 9 | .headingParent { 10 | display: grid; 11 | grid-template-columns: 2fr 1fr 1fr 1fr 1fr; 12 | align-items: center; 13 | padding: 12px 16px; 14 | gap: 16px; 15 | border-radius: 14px; 16 | } 17 | 18 | .heading { 19 | font-size: 12px; 20 | line-height: 140%; 21 | text-transform: uppercase; 22 | font-weight: 600; 23 | color: #9B8D7D; 24 | text-align: left; 25 | } 26 | 27 | .row, .row1 { 28 | display: grid; 29 | grid-template-columns: 2fr 1fr 1fr 1fr 1fr; 30 | align-items: center; 31 | padding: 8px 16px; 32 | gap: 16px; 33 | color: black; 34 | border-radius: 14px; 35 | width: 100%; 36 | } 37 | 38 | .row { 39 | background: #E8E3D6; 40 | } 41 | 42 | .row1 { 43 | background: #EDE9DE; 44 | } 45 | 46 | .rowChild { 47 | display: flex; 48 | align-items: center; 49 | gap: 12px; 50 | } 51 | 52 | .tokenImage { 53 | width: 34px; 54 | height: 34px; 55 | border-radius: 50%; 56 | object-fit: cover; 57 | } 58 | 59 | .textParent { 60 | display: flex; 61 | flex-direction: column; 62 | gap: 4px; 63 | } 64 | 65 | .text1 { 66 | font-weight: bold; 67 | color: #242424; 68 | font-size: 16px; 69 | } 70 | 71 | .text2 { 72 | color: #242424; 73 | font-size: 14px; 74 | font-weight: 500; 75 | } 76 | 77 | .positive { 78 | color: #10B981; 79 | font-weight: 600; 80 | } 81 | 82 | .negative { 83 | color: #EF4444; 84 | font-weight: 600; 85 | } 86 | 87 | @media (max-width: 768px) { 88 | .headingParent, .row, .row1 { 89 | grid-template-columns: 2fr 1fr 1fr 1fr; 90 | padding: 8px 12px; 91 | gap: 12px; 92 | } 93 | 94 | .heading:last-child, .text2:last-child { 95 | display: none; /* Hide date on mobile */ 96 | } 97 | 98 | .text1 { 99 | font-size: 14px; 100 | } 101 | 102 | .text2 { 103 | font-size: 12px; 104 | } 105 | 106 | .tokenImage { 107 | width: 28px; 108 | height: 28px; 109 | } 110 | } -------------------------------------------------------------------------------- /src/components/trust/RecomendationsList.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from 'react'; 2 | import Image from "next/image"; 3 | import styles from './RecomendationsList.module.css'; 4 | 5 | interface Transaction { 6 | id: number; 7 | token: string; 8 | type: 'buy' | 'sell'; 9 | price: number; 10 | performance: number; 11 | date: string; 12 | status: 'success' | 'pending' | 'failed'; 13 | } 14 | 15 | const mockTransactions: Transaction[] = [ 16 | { 17 | id: 1, 18 | token: "BONK", 19 | type: 'buy', 20 | price: 0.00000234, 21 | performance: 12.5, 22 | date: '2024-03-15', 23 | status: 'success' 24 | }, 25 | { 26 | id: 2, 27 | token: "JTO", 28 | type: 'sell', 29 | price: 1.24, 30 | performance: -5.2, 31 | date: '2024-03-14', 32 | status: 'success' 33 | }, 34 | { 35 | id: 3, 36 | token: "PYTH", 37 | type: 'buy', 38 | price: 0.45, 39 | performance: 8.7, 40 | date: '2024-03-13', 41 | status: 'success' 42 | }, 43 | // Add more mock transactions as needed 44 | ]; 45 | 46 | const RecomendationsList: FC = () => { 47 | const formatDate = (dateString: string) => { 48 | return new Date(dateString).toLocaleDateString('en-US', { 49 | month: 'short', 50 | day: 'numeric', 51 | year: 'numeric' 52 | }); 53 | }; 54 | 55 | const formatPerformance = (performance: number) => { 56 | const isPositive = performance > 0; 57 | return ( 58 | 59 | {isPositive ? '+' : ''}{performance}% 60 | 61 | ); 62 | }; 63 | 64 | return ( 65 |
66 |
67 |
TOKEN
68 |
TYPE
69 |
PRICE
70 |
PERFORMANCE
71 |
DATE
72 |
73 | {mockTransactions.map((transaction) => ( 74 |
75 |
76 | {`${transaction.token} 83 |
84 |
{transaction.token}
85 |
86 |
87 |
88 | {transaction.type.toUpperCase()} 89 |
90 |
91 | ${transaction.price.toFixed(8)} 92 |
93 |
94 | {formatPerformance(transaction.performance)} 95 |
96 |
97 | {formatDate(transaction.date)} 98 |
99 |
100 | ))} 101 |
102 | ); 103 | }; 104 | 105 | export default RecomendationsList; -------------------------------------------------------------------------------- /src/components/trust/ScoreCard.module.css: -------------------------------------------------------------------------------- 1 | .card { 2 | background-color: #FA9737; 3 | border-radius: 0.75rem; 4 | padding: 1.5rem; 5 | transition: all 0.2s ease-in-out; 6 | } 7 | 8 | .card:hover { 9 | transform: translateY(-4px); 10 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); 11 | } 12 | 13 | .title { 14 | color: #242424; 15 | font-weight: 600; 16 | font-size: 0.875rem; 17 | margin-bottom: 0.5rem; 18 | } 19 | 20 | .value { 21 | color: white; 22 | font-size: 1.5rem; 23 | font-weight: 600; 24 | margin-bottom: 0.5rem; 25 | } 26 | 27 | .description { 28 | color: #242424; 29 | font-weight: 400; 30 | font-size: 0.875rem; 31 | } -------------------------------------------------------------------------------- /src/components/trust/ScoreCard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './ScoreCard.module.css'; 3 | 4 | interface ScoreCardProps { 5 | title: string; 6 | value: number; 7 | description: string; 8 | } 9 | 10 | const ScoreCard: React.FC = ({ title, value, description }) => { 11 | return ( 12 |
13 |

{title}

14 |
15 | {value.toFixed(2)} 16 |
17 |

{description}

18 |
19 | ); 20 | }; 21 | 22 | export default ScoreCard; -------------------------------------------------------------------------------- /src/components/trust/TrustScoreChart.module.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elizaOS/trust_scoreboard/34d89eb4387aa9f98cf5b5e0e0ab1d49ed0f5f8c/src/components/trust/TrustScoreChart.module.css -------------------------------------------------------------------------------- /src/components/trust/TrustScoreChart.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { Line } from 'react-chartjs-2'; 3 | import { 4 | Chart as ChartJS, 5 | CategoryScale, 6 | LinearScale, 7 | PointElement, 8 | LineElement, 9 | Title, 10 | Tooltip, 11 | Legend, 12 | ChartOptions, 13 | Filler 14 | } from 'chart.js'; 15 | 16 | ChartJS.register( 17 | CategoryScale, 18 | LinearScale, 19 | PointElement, 20 | LineElement, 21 | Title, 22 | Tooltip, 23 | Legend, 24 | Filler 25 | ); 26 | 27 | // Mock data - replace with real data later 28 | const mockData = { 29 | trustScore: { 30 | labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], 31 | data: [75, 78, 80, 82, 84, 85.5], 32 | }, 33 | trustRank: { 34 | labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], 35 | data: [200, 180, 165, 155, 145, 140], 36 | } 37 | }; 38 | 39 | const TrustScoreChart: FC = () => { 40 | const options: ChartOptions<'line'> = { 41 | responsive: true, 42 | maintainAspectRatio: false, 43 | plugins: { 44 | legend: { 45 | position: 'top' as const, 46 | labels: { 47 | color: '#242424', 48 | font: { 49 | weight: 600 50 | } 51 | } 52 | }, 53 | }, 54 | scales: { 55 | y: { 56 | grid: { 57 | color: '#E8E3D6' 58 | }, 59 | ticks: { 60 | color: '#242424' 61 | } 62 | }, 63 | x: { 64 | grid: { 65 | color: '#E8E3D6' 66 | }, 67 | ticks: { 68 | color: '#242424' 69 | } 70 | } 71 | } 72 | }; 73 | 74 | const trustScoreData = { 75 | labels: mockData.trustScore.labels, 76 | datasets: [ 77 | { 78 | label: 'Trust Score', 79 | data: mockData.trustScore.data, 80 | borderColor: '#F98C12', 81 | backgroundColor: 'rgba(249, 140, 18, 0.1)', 82 | fill: true, 83 | tension: 0.4, 84 | }, 85 | { 86 | label: 'Trust Rank', 87 | data: mockData.trustRank.data, 88 | borderColor: '#242424', 89 | backgroundColor: 'rgba(36, 36, 36, 0.1)', 90 | fill: true, 91 | tension: 0.4, 92 | } 93 | ], 94 | }; 95 | 96 | return ( 97 |
98 | 99 |
100 | ); 101 | }; 102 | 103 | export default TrustScoreChart; -------------------------------------------------------------------------------- /src/contexts/AutoConnectProvider.tsx: -------------------------------------------------------------------------------- 1 | import { useLocalStorage } from '@solana/wallet-adapter-react'; 2 | import { createContext, FC, ReactNode, useContext } from 'react'; 3 | 4 | export interface AutoConnectContextState { 5 | autoConnect: boolean; 6 | setAutoConnect(autoConnect: boolean): void; 7 | } 8 | 9 | export const AutoConnectContext = createContext({} as AutoConnectContextState); 10 | 11 | export function useAutoConnect(): AutoConnectContextState { 12 | return useContext(AutoConnectContext); 13 | } 14 | 15 | export const AutoConnectProvider: FC<{ children: ReactNode }> = ({ children }) => { 16 | // TODO: fix auto connect to actual reconnect on refresh/other. 17 | // TODO: make switch/slider settings 18 | // const [autoConnect, setAutoConnect] = useLocalStorage('autoConnect', false); 19 | const [autoConnect, setAutoConnect] = useLocalStorage('autoConnect', true); 20 | 21 | return ( 22 | {children} 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /src/contexts/ContextProvider.tsx: -------------------------------------------------------------------------------- 1 | import { WalletAdapterNetwork, WalletError } from '@solana/wallet-adapter-base'; 2 | import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react'; 3 | import { 4 | PhantomWalletAdapter, 5 | SolflareWalletAdapter, 6 | } from '@solana/wallet-adapter-wallets'; 7 | import { clusterApiUrl } from '@solana/web3.js'; 8 | import { FC, ReactNode, useCallback, useMemo } from 'react'; 9 | import { AutoConnectProvider, useAutoConnect } from './AutoConnectProvider'; 10 | import { notify } from "../utils/notifications"; 11 | import { NetworkConfigurationProvider } from './NetworkConfigurationProvider'; 12 | import dynamic from "next/dynamic"; 13 | 14 | const ReactUIWalletModalProviderDynamic = dynamic( 15 | async () => 16 | (await import("@solana/wallet-adapter-react-ui")).WalletModalProvider, 17 | { ssr: false } 18 | ); 19 | 20 | const WalletContextProvider: FC<{ children: ReactNode }> = ({ children }) => { 21 | const { autoConnect } = useAutoConnect(); 22 | 23 | // Force mainnet-beta 24 | const network = WalletAdapterNetwork.Mainnet; 25 | const endpoint = useMemo(() => { 26 | return clusterApiUrl(network); 27 | }, [network]); 28 | 29 | console.log('Network:', network); 30 | 31 | const wallets = useMemo( 32 | () => [ 33 | new PhantomWalletAdapter(), 34 | new SolflareWalletAdapter(), 35 | ], 36 | [] // Remove network dependency since we're using fixed mainnet 37 | ); 38 | 39 | const onError = useCallback( 40 | (error: WalletError) => { 41 | notify({ type: 'error', message: error.message ? `${error.name}: ${error.message}` : error.name }); 42 | console.error('Wallet error:', error); 43 | }, 44 | [] 45 | ); 46 | 47 | return ( 48 | 49 | 50 | 51 | {children} 52 | 53 | 54 | 55 | ); 56 | }; 57 | 58 | export const ContextProvider: FC<{ children: ReactNode }> = ({ children }) => { 59 | return ( 60 | 61 | 62 | {children} 63 | 64 | 65 | ); 66 | }; 67 | -------------------------------------------------------------------------------- /src/contexts/NetworkConfigurationProvider.tsx: -------------------------------------------------------------------------------- 1 | import { useLocalStorage } from '@solana/wallet-adapter-react'; 2 | import { createContext, FC, ReactNode, useContext } from 'react'; 3 | 4 | 5 | export interface NetworkConfigurationState { 6 | networkConfiguration: string; 7 | setNetworkConfiguration(networkConfiguration: string): void; 8 | } 9 | 10 | export const NetworkConfigurationContext = createContext({} as NetworkConfigurationState); 11 | 12 | export function useNetworkConfiguration(): NetworkConfigurationState { 13 | return useContext(NetworkConfigurationContext); 14 | } 15 | 16 | export const NetworkConfigurationProvider: FC<{ children: ReactNode }> = ({ children }) => { 17 | const [networkConfiguration, setNetworkConfiguration] = useLocalStorage("network", "devnet"); 18 | 19 | return ( 20 | {children} 21 | ); 22 | }; -------------------------------------------------------------------------------- /src/hooks/useDashboard.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import { get } from '../utils/axios'; 3 | 4 | const fetchHighestRankedUsers = async () => { 5 | console.log("sdasdsdadasd=?????????"); 6 | 7 | try { 8 | const response = await get('/user/highestRankedUsers'); 9 | return response.data; 10 | } catch (error) { 11 | console.error('Error fetching highest ranked users:', error); 12 | throw error; 13 | } 14 | }; 15 | 16 | 17 | 18 | interface Partner { 19 | trustScore: number; 20 | avatarUrl?: string; 21 | rank?:number 22 | id?: string; 23 | name?: string; 24 | } 25 | 26 | interface DashboardData { 27 | partners: Partner[]; 28 | } 29 | 30 | export const useDashboard = () => { 31 | const [data, setData] = useState(null); 32 | const [isLoading, setIsLoading] = useState(true); 33 | const [error, setError] = useState(null); 34 | 35 | useEffect(() => { 36 | const fetchDashboard = async () => { 37 | try { 38 | const partners = await fetchHighestRankedUsers(); 39 | 40 | 41 | setData({partners}); 42 | } catch (err) { 43 | setError(err instanceof Error ? err : new Error('Failed to fetch dashboard data')); 44 | } finally { 45 | setIsLoading(false); 46 | } 47 | }; 48 | 49 | fetchDashboard(); 50 | }, []); 51 | 52 | 53 | 54 | return { data, isLoading, error }; 55 | }; -------------------------------------------------------------------------------- /src/hooks/useQueryContext.tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from 'next/router' 2 | import { EndpointTypes } from '../models/types' 3 | 4 | export default function useQueryContext() { 5 | const router = useRouter() 6 | const { cluster } = router.query 7 | 8 | const endpoint = cluster ? (cluster as EndpointTypes) : 'mainnet' 9 | const hasClusterOption = endpoint !== 'mainnet' 10 | const fmtUrlWithCluster = (url) => { 11 | if (hasClusterOption) { 12 | const mark = url.includes('?') ? '&' : '?' 13 | return decodeURIComponent(`${url}${mark}cluster=${endpoint}`) 14 | } 15 | return url 16 | } 17 | 18 | return { 19 | fmtUrlWithCluster, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/models/types.ts: -------------------------------------------------------------------------------- 1 | export type EndpointTypes = 'mainnet' | 'devnet' | 'localnet' 2 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { AppProps } from "next/app" 2 | import Head from "next/head" 3 | import Script from "next/script" 4 | import { FC } from "react" 5 | import { SessionProvider } from "next-auth/react" 6 | import { ContextProvider } from "../contexts/ContextProvider" 7 | import NavBar from "../components/nav-element/NavBar" 8 | import { ContentContainer } from "../components/ContentContainer" 9 | import Notifications from "../components/Notification" 10 | import "../styles/profile.css" // Adjust the path based on your project structure. 11 | require("@solana/wallet-adapter-react-ui/styles.css") 12 | require("../styles/globals.css") 13 | 14 | import { WalletAdapterNetwork } from "@solana/wallet-adapter-base" 15 | import { 16 | ConnectionProvider, 17 | WalletProvider, 18 | } from "@solana/wallet-adapter-react" 19 | import { 20 | PhantomWalletAdapter, 21 | SolflareWalletAdapter, 22 | } from "@solana/wallet-adapter-wallets" 23 | 24 | const wallets = [new PhantomWalletAdapter(), new SolflareWalletAdapter()] 25 | 26 | const App: FC = ({ Component, pageProps }) => { 27 | return ( 28 | 29 | 30 | ai16z - Marc's Trust Leaderboard 31 | 32 | 33 |