├── .env.sample ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .npmrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── app ├── airdrop │ └── page.tsx ├── api │ ├── files │ │ └── route.ts │ └── tokenMints │ │ └── route.ts ├── create │ └── page.tsx ├── error.tsx ├── layout.tsx ├── mint │ └── page.tsx ├── page.tsx └── providers.tsx ├── components ├── Airdrop │ └── Airdropcard.tsx ├── Home │ ├── Benefits.tsx │ ├── GetStarted.tsx │ ├── HeroSection.tsx │ └── MainFeatures.tsx ├── Mint │ ├── MintTokenModal.tsx │ ├── TokenCard.tsx │ └── TokenList.tsx ├── Token │ ├── TokenCreationSuccessModal.tsx │ └── TokenForm.tsx ├── footer.tsx ├── icons.tsx ├── navbar.tsx ├── navbar │ └── NetworkSelector.tsx ├── primitives.ts └── theme-switch.tsx ├── config ├── fonts.ts ├── site.ts └── solana.ts ├── models └── MintAddresses.ts ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── Launchpad.png ├── favicon.ico ├── next.svg └── vercel.svg ├── styles └── globals.css ├── tailwind.config.js ├── tsconfig.json ├── types └── index.ts └── utils ├── config.ts └── dbConnect.ts /.env.sample: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SOLANA_ENDPOINT= 2 | NEXT_PUBLIC_SOLANA_NETWORK= 3 | MONGODB_URI= 4 | PINATA_JWT= 5 | NEXT_PINATA_GATEWAY_URL= -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .now/* 2 | *.css 3 | .changeset 4 | dist 5 | esm/* 6 | public/* 7 | tests/* 8 | scripts/* 9 | *.config.js 10 | .DS_Store 11 | node_modules 12 | coverage 13 | .next 14 | build 15 | !.commitlintrc.cjs 16 | !.lintstagedrc.cjs 17 | !jest.config.js 18 | !plopfile.js 19 | !react-shim.js 20 | !tsup.config.ts -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/eslintrc.json", 3 | "env": { 4 | "browser": false, 5 | "es2021": true, 6 | "node": true 7 | }, 8 | "extends": [ 9 | "plugin:react/recommended", 10 | "plugin:prettier/recommended", 11 | "plugin:react-hooks/recommended", 12 | "plugin:jsx-a11y/recommended", 13 | "plugin:@next/next/recommended" 14 | ], 15 | "plugins": ["react", "unused-imports", "import", "@typescript-eslint", "jsx-a11y", "prettier"], 16 | "parser": "@typescript-eslint/parser", 17 | "parserOptions": { 18 | "ecmaFeatures": { 19 | "jsx": true 20 | }, 21 | "ecmaVersion": 12, 22 | "sourceType": "module" 23 | }, 24 | "settings": { 25 | "react": { 26 | "version": "detect" 27 | } 28 | }, 29 | "rules": { 30 | "no-console": "warn", 31 | "react/prop-types": "off", 32 | "react/jsx-uses-react": "off", 33 | "react/react-in-jsx-scope": "off", 34 | "react-hooks/exhaustive-deps": "off", 35 | "jsx-a11y/click-events-have-key-events": "warn", 36 | "jsx-a11y/interactive-supports-focus": "warn", 37 | "prettier/prettier": "off", 38 | "no-unused-vars": "off", 39 | "unused-imports/no-unused-vars": "off", 40 | "unused-imports/no-unused-imports": "warn", 41 | "@typescript-eslint/no-unused-vars": [ 42 | "warn", 43 | { 44 | "args": "after-used", 45 | "ignoreRestSiblings": false, 46 | "argsIgnorePattern": "^_.*?$" 47 | } 48 | ], 49 | "import/order": [ 50 | "warn", 51 | { 52 | "groups": [ 53 | "type", 54 | "builtin", 55 | "object", 56 | "external", 57 | "internal", 58 | "parent", 59 | "sibling", 60 | "index" 61 | ], 62 | "pathGroups": [ 63 | { 64 | "pattern": "~/**", 65 | "group": "external", 66 | "position": "after" 67 | } 68 | ], 69 | "newlines-between": "always" 70 | } 71 | ], 72 | "react/self-closing-comp": "warn", 73 | "react/jsx-sort-props": [ 74 | "warn", 75 | { 76 | "callbacksLast": true, 77 | "shorthandFirst": true, 78 | "noSortAlphabetically": false, 79 | "reservedFirst": true 80 | } 81 | ], 82 | "padding-line-between-statements": [ 83 | "off", 84 | {"blankLine": "always", "prev": "*", "next": "return"}, 85 | {"blankLine": "always", "prev": ["const", "let", "var"], "next": "*"}, 86 | { 87 | "blankLine": "any", 88 | "prev": ["const", "let", "var"], 89 | "next": ["const", "let", "var"] 90 | } 91 | ] 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /.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*.local 29 | .env 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=true -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Next UI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Solana Token Launchpad 🚀 2 | 3 | ![Hero Section](./public/Launchpad.png) 4 | 5 | A modern, user-friendly platform for creating and managing custom tokens on the Solana blockchain. Launch your own SPL tokens in minutes with a beautiful UI and seamless wallet integration. 6 | 7 | ## ✨ Features 8 | 9 | ### 🎯 Token Creation 10 | 11 | - Create custom SPL tokens with configurable parameters 12 | - Set token name, symbol, and initial supply 13 | - Upload and store token images on IPFS via Pinata 14 | - Automatic mint authority assignment to creator's wallet 15 | 16 | ### 💰 Token Management 17 | 18 | - View all tokens created by your wallet 19 | - Mint additional tokens as needed 20 | - Track token supply and distribution 21 | - Decentralized image storage on IPFS 22 | 23 | ### 🌟 Devnet Integration 24 | 25 | - Request SOL airdrops for testing 26 | - Deploy tokens on Solana Devnet 27 | - Test token functionality before mainnet launch 28 | 29 | ### 🎨 Modern UI/UX 30 | 31 | - Responsive design for all devices 32 | - Dark/Light mode support 33 | - Seamless wallet connection 34 | - Real-time transaction feedback 35 | 36 | ## 🛠 Technology Stack 37 | 38 | ### Blockchain & Web3 39 | 40 | - Solana Web3.js 41 | - SPL Token Program 42 | - Solana Wallet Adapter 43 | 44 | ### Frontend 45 | 46 | - React 18 47 | - TypeScript 48 | - NextUI Components 49 | - TailwindCSS 50 | - Lucide Icons 51 | 52 | ### Storage & Database 53 | 54 | - IPFS via Pinata 55 | - MongoDB Atlas 56 | 57 | ## 📦 Project Structure 58 | 59 | ``` 60 | \ 61 | ├── app/ # Route Pages 62 | │ ├── airdrop/ # Airdrop page 63 | │ ├── create/ # Create token page 64 | │ ├── mint/ # Mint tokens page 65 | │ └── api/ # backend code 66 | ├── components/ # React components for pages 67 | ├── config/ # siteconfig and backend func 68 | │ ├── solana.ts # Solana integration 69 | │ └── site.ts # Site config 70 | ├── models/ # Mongodb data model 71 | ├── types/ # Typescript types 72 | └── utils/ # util files for connections 73 | ``` 74 | 75 | ## 🚀 Getting Started 76 | 77 | ### Prerequisites 78 | 79 | - Node.js 18+ 80 | - npm or yarn 81 | - A Solana wallet (e.g., Phantom) 82 | - MongoDB Atlas account 83 | - Pinata account for IPFS 84 | 85 | ### Environment Variables 86 | 87 | Create a `.env` file in the root directory: 88 | 89 | ```env 90 | MONGODB_URI=your_mongodb_uri 91 | MONGODB_URI_MAINNET=your_mongodb_uri_for_mainnet_data 92 | PINATA_JWT=your_pinata_jwt 93 | NEXT_PINATA_GATEWAY_URL=your_pinata_gateway_url 94 | NEXT_PUBLIC_SOLANA_ENDPOINT=your_rpc_node_endpoint 95 | NEXT_PUBLIC_SOLANA_NETWORK=your_network ( "devent" | "mainnet" ) 96 | ``` 97 | 98 | ### Installation 99 | 100 | 1. Clone the repository: 101 | 102 | ```bash 103 | git clone https://github.com/abhiraj2404/Token_launchpad.git 104 | cd token_launchpad 105 | ``` 106 | 107 | 2. Install dependencies: 108 | 109 | ```bash 110 | npm install 111 | ``` 112 | 113 | 3. Start the development server: 114 | 115 | ```bash 116 | npm run dev 117 | ``` 118 | 119 | 4. Open [http://localhost:3000](http://localhost:3000) in your browser 120 | 121 | ### Building for Production 122 | 123 | ```bash 124 | npm run build 125 | ``` 126 | 127 | The build output will be in the `dist` directory. 128 | 129 | ## 📱 Usage 130 | 131 | 1. **Connect Wallet** 132 | 133 | - Click "Connect Wallet" in the navbar 134 | - Select your Solana wallet (e.g., Phantom) 135 | - Approve the connection 136 | 137 | 2. **Get Test SOL** 138 | 139 | - Navigate to the Airdrop page 140 | - Click "Request Airdrop" 141 | - Receive 1 SOL on Devnet 142 | 143 | 3. **Create Token** 144 | 145 | - Go to Create Token page 146 | - Fill in token details 147 | - Upload token image 148 | - Click "Launch Token" 149 | 150 | 4. **Manage Tokens** 151 | - Visit My Tokens page 152 | - View created tokens 153 | - Mint additional supply 154 | - Track token details 155 | 156 | ## 🤝 Contributing 157 | 158 | Contributions are welcome! Please feel free to submit a Pull Request. 159 | 160 | 1. Fork the project 161 | 2. Create your feature branch (`git checkout -b feature/AmazingFeature`) 162 | 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) 163 | 4. Push to the branch (`git push origin feature/AmazingFeature`) 164 | 5. Open a Pull Request 165 | 166 | ## 📄 License 167 | 168 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 169 | 170 | ## 📧 Creator 171 | 172 | Abhiraj Chauhan - [@abhiraj_2404](https://x.com/abhiraj_2404) 173 | 174 | Project Link - [https://solanatokenlaunchpad.vercel.app](https://solanatokenlaunchpad.vercel.app) 175 | -------------------------------------------------------------------------------- /app/airdrop/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { AirdropCard } from "@/components/Airdrop/Airdropcard"; 3 | import { Card } from "@nextui-org/card"; 4 | import { useWallet } from "@solana/wallet-adapter-react"; 5 | import React from "react"; 6 | 7 | function AirdropPage() { 8 | const { connected } = useWallet(); 9 | return ( 10 |
11 |
12 |

13 | Get Test SOL 14 |

15 |

16 | Request an airdrop of 1 SOL to test token creation on Solana Devnet. 17 |

18 |
19 | 20 |
21 | {!connected ? ( 22 | 26 |

27 | Please connect your wallet to request an airdrop. 28 |

29 |
30 | ) : ( 31 | 32 | )} 33 |
34 |
35 | ); 36 | } 37 | 38 | export default AirdropPage; 39 | -------------------------------------------------------------------------------- /app/api/files/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse, type NextRequest } from "next/server"; 2 | import { pinata } from "@/utils/config"; 3 | 4 | export async function POST(request: NextRequest) { 5 | try { 6 | const data = await request.formData(); 7 | const file: File | null = data.get("file") as unknown as File; 8 | 9 | const uploadData = await pinata.upload 10 | .file(file).group("51bde156-0d66-483f-976f-8826dfb8624c"); 11 | 12 | const url = `https://${process.env.NEXT_PINATA_GATEWAY_URL}/ipfs/${uploadData.IpfsHash}`; 13 | 14 | return NextResponse.json(url, { status: 200 }); 15 | } catch (e) { 16 | console.log(e); 17 | return NextResponse.json( 18 | { error: "Internal Server Error" }, 19 | { status: 500 } 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/api/tokenMints/route.ts: -------------------------------------------------------------------------------- 1 | import MintAddresses from "@/models/MintAddresses"; 2 | import dbConnect from "@/utils/dbConnect"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | // POST Request Handler 6 | export async function POST(request: NextRequest) { 7 | try { 8 | // Connect to the database 9 | await dbConnect(); 10 | 11 | // Parse the request body 12 | const { address } = await request.json(); 13 | 14 | // Validate the address 15 | if (!address) { 16 | return NextResponse.json( 17 | { error: "Mint address is required" }, 18 | { status: 400 } 19 | ); 20 | } 21 | 22 | // Check if address already exists 23 | const existingAddress = await MintAddresses.exists({ address }); 24 | if (existingAddress) { 25 | return NextResponse.json( 26 | { error: "Mint address already exists" }, 27 | { status: 409 } 28 | ); 29 | } 30 | 31 | // Create new mint address 32 | const newMintAddress = await MintAddresses.create({ address }); 33 | 34 | // Return success response 35 | return NextResponse.json( 36 | { 37 | message: "Mint address added successfully", 38 | address: newMintAddress, 39 | }, 40 | { status: 201 } 41 | ); 42 | } catch (error) { 43 | console.error("Error adding mint address:", error); 44 | return NextResponse.json( 45 | { error: "Failed to add mint address" }, 46 | { status: 500 } 47 | ); 48 | } 49 | } 50 | 51 | // GET Request Handler 52 | export async function GET() { 53 | try { 54 | // Connect to the database 55 | await dbConnect(); 56 | 57 | // Retrieve all mint addresses 58 | const response = await MintAddresses.find(); 59 | const mintAddresses = response.map((doc) => doc.address); 60 | // Return mint addresses 61 | return NextResponse.json( 62 | { 63 | message: "Mint addresses retrieved successfully", 64 | addresses: mintAddresses, 65 | }, 66 | { status: 200 } 67 | ); 68 | } catch (error) { 69 | console.error("Error retrieving mint addresses:", error); 70 | return NextResponse.json( 71 | { error: "Failed to retrieve mint addresses" }, 72 | { status: 500 } 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/create/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import TokenForm from "@/components/Token/TokenForm"; 4 | import { Card } from "@nextui-org/card"; 5 | import { useWallet } from "@solana/wallet-adapter-react"; 6 | import { Coins } from "lucide-react"; 7 | import React from "react"; 8 | 9 | function CreatetokenPage() { 10 | const { connected } = useWallet(); 11 | 12 | return ( 13 |
14 |
15 |
16 | 17 |
18 |

19 | Create Your Token 20 |

21 |

22 | Fill in the details below to launch your custom token on Solana. 23 |

24 |
25 | 26 |
27 | {!connected ? ( 28 | 32 |

33 | Please connect your wallet to create a token 34 |

35 |
36 | ) : ( 37 | 38 | )} 39 |
40 |
41 | ); 42 | } 43 | 44 | export default CreatetokenPage; 45 | -------------------------------------------------------------------------------- /app/error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect } from "react"; 4 | 5 | export default function Error({ 6 | error, 7 | reset, 8 | }: { 9 | error: Error; 10 | reset: () => void; 11 | }) { 12 | useEffect(() => { 13 | // Log the error to an error reporting service 14 | /* eslint-disable no-console */ 15 | console.error(error); 16 | }, [error]); 17 | 18 | return ( 19 |
20 |

Something went wrong!

21 | 29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "@/styles/globals.css"; 2 | import { Metadata, Viewport } from "next"; 3 | import { Link } from "@nextui-org/link"; 4 | import clsx from "clsx"; 5 | import { Analytics } from "@vercel/analytics/react"; 6 | 7 | import { Providers } from "./providers"; 8 | 9 | import { siteConfig } from "@/config/site"; 10 | import { fontSans } from "@/config/fonts"; 11 | import { Navbar } from "@/components/navbar"; 12 | import { Footer } from "@/components/footer"; 13 | 14 | export const metadata: Metadata = { 15 | title: { 16 | default: siteConfig.name, 17 | template: `%s - ${siteConfig.name}`, 18 | }, 19 | description: siteConfig.description, 20 | icons: { 21 | icon: "/favicon.ico", 22 | }, 23 | }; 24 | 25 | export const viewport: Viewport = { 26 | themeColor: [ 27 | { media: "(prefers-color-scheme: light)", color: "white" }, 28 | { media: "(prefers-color-scheme: dark)", color: "black" }, 29 | ], 30 | }; 31 | 32 | export default function RootLayout({ 33 | children, 34 | }: { 35 | children: React.ReactNode; 36 | }) { 37 | return ( 38 | 39 | 40 | 46 | 47 |
48 | 49 | 50 |
{children}
51 |
53 |
54 | 55 | 56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /app/mint/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { TokenList } from "@/components/Mint/TokenList"; 4 | import { Card } from "@nextui-org/card"; 5 | import { useWallet } from "@solana/wallet-adapter-react"; 6 | import React from "react"; 7 | 8 | function MintPage() { 9 | const { connected } = useWallet(); 10 | 11 | return ( 12 |
13 |
14 |

My Tokens

15 |

16 | Manage your launched tokens and mint additional supply. 17 |

18 |
19 | 20 |
21 | {!connected ? ( 22 | 26 |

27 | Please connect your wallet to mint tokens. 28 |

29 |
30 | ) : ( 31 | 32 | )} 33 |
34 |
35 | ); 36 | } 37 | 38 | export default MintPage; 39 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import HeroSection from "@/components/Home/HeroSection"; 3 | import { MainFeatures } from "@/components/Home/MainFeatures"; 4 | import { GetStarted } from "@/components/Home/GetStarted"; 5 | import { Benefits } from "@/components/Home/Benefits"; 6 | 7 | export default function Home() { 8 | return ( 9 | <> 10 | 11 | 12 | 13 | 14 | {/*
15 |
16 |
17 | 18 |
19 |

Launch Your Solana Token

20 |

21 | Create and deploy your own token on the Solana blockchain in 22 | minutes. Simple, fast, and secure. 23 |

24 | 33 |
34 | 35 |
36 | } 38 | title="Fast Deployment" 39 | description="Launch your token in seconds with our streamlined process." 40 | /> 41 | } 43 | title="Secure" 44 | description="Built on Solana's secure and scalable blockchain infrastructure." 45 | /> 46 | } 48 | title="Low Cost" 49 | description="Minimal fees for token creation and transactions." 50 | /> 51 | } 53 | title="Low Cost" 54 | description="Minimal fees for token creation and transactions." 55 | /> 56 |
57 | 58 | 59 |

60 | Ready to Create Your Token? 61 |

62 |

63 | Join thousands of projects already launched on Solana 64 |

65 | 68 |
69 |
*/} 70 | 71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /app/providers.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import type { ThemeProviderProps } from "next-themes"; 4 | import { 5 | ConnectionProvider, 6 | WalletProvider, 7 | } from "@solana/wallet-adapter-react"; 8 | import { WalletAdapterNetwork } from "@solana/wallet-adapter-base"; 9 | import { UnsafeBurnerWalletAdapter } from "@solana/wallet-adapter-wallets"; 10 | import { 11 | WalletModalProvider, 12 | WalletDisconnectButton, 13 | WalletMultiButton, 14 | } from "@solana/wallet-adapter-react-ui"; 15 | import { clusterApiUrl } from "@solana/web3.js"; 16 | 17 | // Default styles that can be overridden by your app 18 | import "@solana/wallet-adapter-react-ui/styles.css"; 19 | 20 | import * as React from "react"; 21 | import { NextUIProvider } from "@nextui-org/system"; 22 | import { useRouter } from "next/navigation"; 23 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 24 | 25 | export interface ProvidersProps { 26 | children: React.ReactNode; 27 | themeProps?: ThemeProviderProps; 28 | } 29 | 30 | declare module "@react-types/shared" { 31 | interface RouterConfig { 32 | routerOptions: NonNullable< 33 | Parameters["push"]>[1] 34 | >; 35 | } 36 | } 37 | 38 | export function Providers({ children, themeProps }: ProvidersProps) { 39 | const router = useRouter(); 40 | console.log(process.env.NEXT_PUBLIC_SOLANA_ENDPOINT); 41 | return ( 42 | 43 | 44 | 45 | 46 | {children} 47 | 48 | 49 | 50 | 51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /components/Airdrop/Airdropcard.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import { useWallet } from "@solana/wallet-adapter-react"; 5 | import { Loader2 } from "lucide-react"; 6 | import { Button } from "@nextui-org/button"; 7 | import { getConnection } from "@/config/solana"; 8 | import { LAMPORTS_PER_SOL } from "@solana/web3.js"; 9 | 10 | export function AirdropCard() { 11 | const { publicKey } = useWallet(); 12 | const [isLoading, setIsLoading] = useState(false); 13 | 14 | const handleAirdrop = async () => { 15 | setIsLoading(true); 16 | 17 | if (!publicKey) return; 18 | 19 | try { 20 | const connection = getConnection(); 21 | const signature = await connection.requestAirdrop( 22 | publicKey, 23 | LAMPORTS_PER_SOL 24 | ); 25 | await connection.confirmTransaction(signature); 26 | alert("Airdrop successful!, check your balance"); 27 | } catch (error) { 28 | alert("Failed to request airdrop, some error occured"); 29 | } 30 | 31 | setIsLoading(false); 32 | }; 33 | 34 | return ( 35 |
36 |
37 |
38 |

39 | Your Wallet 40 |

41 |

42 | {publicKey?.toString()} 43 |

44 |
45 | 46 |
47 |
48 |

49 | Amount to receive: 50 |

51 |

1 SOL

52 |
53 | 54 | 62 |
63 |
64 |
65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /components/Home/Benefits.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from "@nextui-org/card"; 2 | import { Shield, Zap, Lock, Settings } from "lucide-react"; 3 | 4 | export function Benefits() { 5 | return ( 6 |
7 |
8 |

9 | Why Choose Our Platform? 10 |

11 |

12 | Built with security and simplicity in mind 13 |

14 |
15 | 16 |
17 | {benefits.map((benefit) => ( 18 | 23 |
24 |
25 |
26 | {benefit.icon} 27 |
28 |
29 |
30 |

31 | {benefit.title} 32 |

33 |

{benefit.description}

34 |
35 |
36 |
37 | ))} 38 |
39 |
40 | ); 41 | } 42 | 43 | const benefits = [ 44 | { 45 | icon: , 46 | title: "Fast Deployment", 47 | description: 48 | "Launch your token in seconds with our optimized deployment process. No complex setup required.", 49 | }, 50 | { 51 | icon: , 52 | title: "Secure Platform", 53 | description: 54 | "Built on Solana's secure blockchain with industry-standard security practices.", 55 | }, 56 | { 57 | icon: , 58 | title: "Full Control", 59 | description: 60 | "Maintain complete control as the mint authority of your token with no restrictions.", 61 | }, 62 | { 63 | icon: , 64 | title: "Easy Management", 65 | description: 66 | "Intuitive interface to manage your tokens and mint additional supply when needed.", 67 | }, 68 | ]; 69 | -------------------------------------------------------------------------------- /components/Home/GetStarted.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@nextui-org/button"; 2 | import { Card } from "@nextui-org/card"; 3 | import { Link } from "@nextui-org/link"; 4 | import { Rocket, Coins } from "lucide-react"; 5 | 6 | const selectedNetwork = process.env.NEXT_PUBLIC_SOLANA_NETWORK; 7 | 8 | export function GetStarted() { 9 | return ( 10 |
11 |
12 | 13 |
14 |
15 |

16 | Ready to Launch Your Token? 17 |

18 |

19 | Join the growing community of projects on Solana. Create your 20 | token in minutes. 21 |

22 |
23 | 32 | 42 |
43 |
44 | 45 |
46 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /components/Home/HeroSection.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@nextui-org/button"; 2 | import { Coins, Rocket } from "lucide-react"; 3 | import Link from "next/link"; 4 | import React from "react"; 5 | 6 | const selectedNetwork = process.env.NEXT_PUBLIC_SOLANA_NETWORK; 7 | function HeroSection() { 8 | return ( 9 |
10 |
11 |
12 |
13 |
14 | 15 | 16 | Token Launchpad 17 | 18 |
19 |

20 | Launch Your Own Solana Token{" "} 21 | in Minutes 22 |

23 |

24 | Create, deploy, and manage your custom token on Solana with our 25 | simple and secure platform. 26 |

27 |
28 | 37 | 46 |
47 |
48 |
49 | Blockchain Illustration 54 |
55 |
56 |
57 |
58 | ); 59 | } 60 | 61 | export default HeroSection; 62 | -------------------------------------------------------------------------------- /components/Home/MainFeatures.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from "@nextui-org/card"; 2 | import { Chip } from "@nextui-org/chip"; 3 | import { Link } from "@nextui-org/link"; 4 | import { Wallet, Plus, Coins, Waves } from "lucide-react"; 5 | 6 | const selectedNetwork = process.env.NEXT_PUBLIC_SOLANA_NETWORK; 7 | 8 | export function MainFeatures() { 9 | return ( 10 |
11 |
12 |
13 |

14 | Create Your Token in Simple Steps 15 |

16 |

17 | Follow our streamlined process to launch your token 18 |

19 |
20 | 21 |
22 | {/* Connection Lines */} 23 |
24 | 25 |
26 | {features.map((feature, index) => ( 27 |
31 | {feature.link ? ( 32 | 33 | 34 | 35 | ) : ( 36 | 37 | )} 38 |
39 | ))} 40 |
41 |
42 |
43 |
44 | ); 45 | } 46 | 47 | interface Feature { 48 | icon: React.ReactNode; 49 | title: string; 50 | description: string; 51 | link?: string; 52 | comingSoon?: boolean; 53 | } 54 | 55 | function FeatureCard({ feature, index }: { feature: Feature; index: number }) { 56 | return ( 57 | 61 |
62 |
63 |
{feature.icon}
64 |
65 | {feature.comingSoon && ( 66 | 67 | Coming Soon 68 | 69 | )} 70 | {index} 71 |
72 |
73 |

74 | {feature.title} 75 |

76 |

{feature.description}

77 |
78 |
79 | ); 80 | } 81 | 82 | const features: Feature[] = [ 83 | { 84 | icon: , 85 | title: selectedNetwork == "devnet" ? "Get Test SOL" : "Connect Wallet", 86 | description: 87 | "Connect your solana wallet to do transactions on the platform.", 88 | link: selectedNetwork == "devnet" ? "/airdrop" : "/", 89 | }, 90 | { 91 | icon: , 92 | title: "Create Tokens", 93 | description: 94 | "Configure your token with custom name, symbol, and initial supply", 95 | link: "/create", 96 | }, 97 | { 98 | icon: , 99 | title: "Manage Tokens", 100 | description: "View your tokens and mint additional supply whenever needed", 101 | link: "/mint", 102 | }, 103 | { 104 | icon: , 105 | title: "Create Liquidity", 106 | description: "Add liquidity pool for your token to enable trading", 107 | comingSoon: true, 108 | }, 109 | ]; 110 | -------------------------------------------------------------------------------- /components/Mint/MintTokenModal.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { 3 | Modal, 4 | ModalContent, 5 | ModalHeader, 6 | ModalBody, 7 | ModalFooter, 8 | } from "@nextui-org/modal"; 9 | import { TokenData } from "@/types"; 10 | import { Input } from "@nextui-org/input"; 11 | import { Button } from "@nextui-org/button"; 12 | import { getConnection, mintTo } from "@/config/solana"; 13 | import { useWallet } from "@solana/wallet-adapter-react"; 14 | import { PublicKey } from "@solana/web3.js"; 15 | import { getAssociatedTokenAddress, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token"; 16 | 17 | interface MintTokenModalProps { 18 | isOpen: boolean; 19 | token: TokenData | null; 20 | onClose: () => void; 21 | } 22 | 23 | export function MintTokenModal({ 24 | isOpen, 25 | token, 26 | onClose, 27 | }: MintTokenModalProps) { 28 | const [formData, setFormData] = useState({ amount: 0 }); 29 | const [isLoading, setIsLoading] = useState(false); 30 | const connection = getConnection(); 31 | const wallet = useWallet(); 32 | const mint = token ? new PublicKey(token.mint) : null; 33 | 34 | const handleSubmit = async (e: React.FormEvent) => { 35 | e.preventDefault(); 36 | if (!token) return; 37 | 38 | if (token.mintAuthority !== wallet.publicKey?.toBase58()) { 39 | alert("You are not the mint authority for this token"); 40 | onClose(); 41 | return; 42 | } 43 | const mintAmount = formData.amount * Math.pow(10, token.decimals); 44 | 45 | setIsLoading(true); 46 | 47 | try { 48 | if (mint && wallet.publicKey) { 49 | const tokenAccount = await getAssociatedTokenAddress( 50 | mint, 51 | wallet.publicKey, 52 | true, 53 | TOKEN_2022_PROGRAM_ID 54 | ); 55 | 56 | await mintTo( 57 | connection, 58 | wallet, // Payer wallet 59 | mint, // Mint address 60 | tokenAccount, // Destination address 61 | wallet.publicKey, // Mint authority 62 | mintAmount // Amount to mint 63 | ); 64 | } 65 | alert("Tokens minted successfully"); 66 | } catch (error) { 67 | console.error("Error minting tokens:", error); 68 | alert("Error minting tokens"); 69 | } finally { 70 | setIsLoading(false); 71 | onClose(); 72 | } 73 | }; 74 | 75 | if (!token) return null; 76 | 77 | return ( 78 | 79 | 80 |
81 | Mint {token.symbol} Tokens 82 | 83 |
84 |

85 | Enter the amount of tokens you want to mint to your wallet. 86 |

87 | 93 | setFormData({ amount: parseInt(e.target.value) }) 94 | } 95 | required 96 | /> 97 |
98 |
99 | 100 | 108 | 111 | 112 |
113 |
114 |
115 | ); 116 | } 117 | -------------------------------------------------------------------------------- /components/Mint/TokenCard.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { TokenData } from '@/types'; 3 | import { Button } from '@nextui-org/button'; 4 | import { Card } from '@nextui-org/card'; 5 | import { Tooltip } from '@nextui-org/tooltip'; 6 | import { Copy, CheckCircle } from 'lucide-react'; 7 | import { useState } from 'react'; 8 | 9 | interface TokenCardProps { 10 | token: TokenData; 11 | onMint: () => void; 12 | } 13 | 14 | export default function TokenCard({ token, onMint }: TokenCardProps) { 15 | const [copiedField, setCopiedField] = useState(null); 16 | 17 | const handleCopy = async (value: string, field: string) => { 18 | await navigator.clipboard.writeText(value); 19 | setCopiedField(field); 20 | setTimeout(() => setCopiedField(null), 2000); 21 | }; 22 | 23 | return ( 24 | 25 |
26 |
27 |
28 | {token.name} 33 |
34 | 35 |
36 |
37 |
38 |

39 | {token.name} ({token.symbol}) 40 |

41 |

42 | Total Supply: {token.totalSupply.toLocaleString()} {token.symbol} 43 |

44 |
45 | 51 |
52 | 53 |
54 | handleCopy(token.mint, 'mint')} 58 | copied={copiedField === 'mint'} 59 | /> 60 | handleCopy(token.mintAuthority, 'mintAuth')} 64 | copied={copiedField === 'mintAuth'} 65 | /> 66 | {token.freezeAuthority && ( 67 | handleCopy(token.freezeAuthority!, 'freezeAuth')} 71 | copied={copiedField === 'freezeAuth'} 72 | /> 73 | )} 74 |

75 | Decimals: {token.decimals} 76 |

77 |
78 |
79 |
80 |
81 |
82 | ); 83 | } 84 | 85 | 86 | interface AddressRowProps { 87 | label: string; 88 | value: string; 89 | onCopy: () => void; 90 | copied: boolean; 91 | } 92 | 93 | function AddressRow({ label, value, onCopy, copied }: AddressRowProps) { 94 | return ( 95 |
96 |
97 |

98 | {label}: 99 | 100 | {value.slice(0, 16)}...{value.slice(-8)} 101 | 102 |

103 |
104 | 105 | 118 | 119 |
120 | ); 121 | } -------------------------------------------------------------------------------- /components/Mint/TokenList.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { getConnection } from "@/config/solana"; 3 | import { TokenData } from "@/types"; 4 | import { Spinner } from "@nextui-org/spinner"; 5 | import { 6 | getMint, 7 | getTokenMetadata, 8 | TOKEN_2022_PROGRAM_ID, 9 | } from "@solana/spl-token"; 10 | import { useWallet } from "@solana/wallet-adapter-react"; 11 | import { PublicKey } from "@solana/web3.js"; 12 | import { useEffect, useState } from "react"; 13 | import { MintTokenModal } from "./MintTokenModal"; 14 | import TokenCard from "./TokenCard"; 15 | 16 | async function fetchUserTokens(userPublicKey: any): Promise { 17 | const connection = getConnection(); 18 | const tokens: TokenData[] = []; 19 | // const data = JSON.parse(JSON.stringify(tokenmint)); 20 | 21 | const response = await fetch("/api/tokenMints"); 22 | const data = await response.json(); 23 | const mintAddresses = data.addresses; 24 | for (const mintAddress of mintAddresses) { 25 | try { 26 | const mintPubkey = new PublicKey(mintAddress); 27 | const metadata = await getTokenMetadata( 28 | connection, 29 | mintPubkey, 30 | "confirmed", 31 | TOKEN_2022_PROGRAM_ID 32 | ); 33 | const mintInfo = await getMint( 34 | connection, 35 | mintPubkey, 36 | "confirmed", 37 | TOKEN_2022_PROGRAM_ID 38 | ); 39 | const uriResponse = await fetch(metadata?.uri as string); 40 | const uriData = await uriResponse.json(); 41 | 42 | if (mintInfo.mintAuthority?.toBase58() == userPublicKey) 43 | tokens.push({ 44 | mint: mintAddress, 45 | name: metadata?.name || "Unknown", 46 | symbol: metadata?.symbol || "Unknown", 47 | decimals: mintInfo.decimals, 48 | totalSupply: 49 | Number(mintInfo.supply) / Math.pow(10, mintInfo.decimals), 50 | imageUrl: uriData?.image || "", 51 | mintAuthority: mintInfo.mintAuthority?.toBase58() || "None", 52 | freezeAuthority: metadata?.updateAuthority?.toBase58() || null, 53 | }); 54 | } catch (error) { 55 | console.error(`Error fetching token ${mintAddress}:`, error); 56 | } 57 | } 58 | 59 | return tokens; 60 | } 61 | 62 | export function TokenList() { 63 | const wallet = useWallet(); 64 | const [tokens, setTokens] = useState([]); 65 | const [selectedToken, setSelectedToken] = useState(null); 66 | const [showMintModal, setShowMintModal] = useState(false); 67 | const [isLoading, setIsLoading] = useState(true); 68 | 69 | useEffect(() => { 70 | async function loadTokens() { 71 | if (wallet.publicKey) { 72 | try { 73 | const userTokens = await fetchUserTokens(wallet.publicKey); 74 | setTokens(userTokens); 75 | } catch (error) { 76 | console.error("Error loading tokens:", error); 77 | } finally { 78 | setIsLoading(false); 79 | } 80 | } 81 | } 82 | 83 | loadTokens(); 84 | }, [wallet.publicKey]); 85 | 86 | const handleMint = (token: TokenData) => { 87 | setSelectedToken(token); 88 | setShowMintModal(true); 89 | }; 90 | 91 | if (isLoading) { 92 | return ( 93 |
94 | 95 |
96 | ); 97 | } 98 | 99 | return ( 100 |
101 | {tokens 102 | .slice() 103 | .reverse() 104 | .map((token) => ( 105 | handleMint(token)} 109 | /> 110 | ))} 111 | 112 | {tokens.length === 0 && ( 113 |
114 |

115 | You haven't created any tokens yet. 116 |

117 |
118 | )} 119 | 120 | { 124 | setShowMintModal(false); 125 | setSelectedToken(null); 126 | }} 127 | /> 128 |
129 | ); 130 | } 131 | -------------------------------------------------------------------------------- /components/Token/TokenCreationSuccessModal.tsx: -------------------------------------------------------------------------------- 1 | import { TokenMetadata } from "@/types"; 2 | import { Button } from "@nextui-org/button"; 3 | import { Link } from "@nextui-org/link"; 4 | import { 5 | Modal, 6 | ModalContent, 7 | ModalHeader, 8 | ModalBody, 9 | ModalFooter, 10 | } from "@nextui-org/modal"; 11 | import { ExternalLink, Copy, CheckCircle } from "lucide-react"; 12 | import { useState } from "react"; 13 | 14 | interface TokenCreationSuccessProps { 15 | isOpen: boolean; 16 | onClose: () => void; 17 | tokenData: TokenMetadata; 18 | } 19 | 20 | const selectedNetwork = process.env.NEXT_PUBLIC_SOLANA_NETWORK; 21 | 22 | export default function TokenCreationSuccessModal({ 23 | isOpen, 24 | onClose, 25 | tokenData, 26 | }: TokenCreationSuccessProps) { 27 | const explorerUrl = `https://explorer.solana.com/address/${tokenData.mint}?cluster=${selectedNetwork == "mainnet" ? "mainnet-beta" : "devnet"}`; 28 | const [copiedField, setCopiedField] = useState(null); 29 | 30 | const handleCopy = async (value: string, field: string) => { 31 | await navigator.clipboard.writeText(value); 32 | setCopiedField(field); 33 | setTimeout(() => setCopiedField(null), 2000); 34 | }; 35 | 36 | return ( 37 | 48 | 49 | 50 |

51 | Token Created Successfully! 52 |

53 |
54 | 55 |
56 |
57 |

58 | Token Details 59 |

60 |
61 | 62 | 63 | handleCopy(tokenData.mint, "mint")} 67 | copied={copiedField === "mint"} 68 | /> 69 | 73 | handleCopy(tokenData.mintAuthority, "authority") 74 | } 75 | copied={copiedField === "authority"} 76 | /> 77 | 81 | 85 |
86 |
87 |
88 |
89 | 90 | 100 | 103 | 104 |
105 |
106 | ); 107 | } 108 | 109 | interface DetailRowProps { 110 | label: string; 111 | value: string; 112 | onCopy?: () => void; 113 | copied?: boolean; 114 | } 115 | 116 | function DetailRow({ label, value, onCopy, copied }: DetailRowProps) { 117 | return ( 118 |
119 | 120 | {label}: 121 | 122 |
123 | 124 | {value} 125 | 126 | {onCopy && ( 127 | 140 | )} 141 |
142 |
143 | ); 144 | } 145 | -------------------------------------------------------------------------------- /components/Token/TokenForm.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { TokenFormData, TokenMetadata } from "@/types"; 4 | import { Button } from "@nextui-org/button"; 5 | import { Card } from "@nextui-org/card"; 6 | import { Input } from "@nextui-org/input"; 7 | import React, { useRef, useState } from "react"; 8 | import TokenCreationSuccessModal from "./TokenCreationSuccessModal"; 9 | import { createToken, getConnection } from "@/config/solana"; 10 | import { useWallet } from "@solana/wallet-adapter-react"; 11 | import { Upload } from "lucide-react"; 12 | 13 | const initialFormData: TokenFormData = { 14 | name: "", 15 | symbol: "", 16 | decimals: 9, 17 | initialMintAmount: 100, 18 | }; 19 | 20 | const dummyTokenMetadata: TokenMetadata = { 21 | mint: "3kH9fZnYqDfPz3x5Qf8mK2fUyUxtjhgHb3Wq7N7s8X4A", 22 | mintAuthority: "B2cN8jTy5XQoQ9Bd7pWV7pJkqD8WRzF5j9p6bHqLn3k4", 23 | name: "DummyToken", 24 | symbol: "DMT", 25 | decimals: 6, 26 | initialMintAmount: 1000000, 27 | }; 28 | 29 | function TokenForm() { 30 | const wallet = useWallet(); 31 | const [formData, setFormData] = useState(initialFormData); 32 | const [isLoading, setIsLoading] = useState(false); 33 | const [loadingText, setLoadingText] = useState("Launch Token"); 34 | const [file, setFile] = useState(); 35 | const [imageURL, setImageURL] = useState(null); 36 | const [URI, setURI] = useState(null); 37 | const [tokenData, setTokenData] = useState(null); 38 | const [showSuccess, setShowSuccess] = useState(false); 39 | const [imagePreview, setImagePreview] = useState(null); 40 | const fileInputRef = useRef(null); 41 | 42 | const handleInputChange = ( 43 | field: keyof TokenFormData, 44 | value: string | number 45 | ) => { 46 | setFormData((prev) => ({ ...prev, [field]: value })); 47 | }; 48 | 49 | const handleImageUpload = (e: React.ChangeEvent) => { 50 | const file = e.target.files?.[0]; 51 | if (file) { 52 | setFile(e.target?.files?.[0]); 53 | const reader = new FileReader(); 54 | reader.onloadend = () => { 55 | setImagePreview(reader.result as string); 56 | }; 57 | reader.readAsDataURL(file); 58 | } 59 | }; 60 | 61 | const uploadImageFile = async () => { 62 | try { 63 | if (!file) { 64 | alert("No file selected"); 65 | return; 66 | } 67 | 68 | const data = new FormData(); 69 | data.set("file", file, formData.name); 70 | const uploadRequest = await fetch("/api/files", { 71 | method: "POST", 72 | body: data, 73 | }); 74 | const imageURL = await uploadRequest.json(); 75 | setImageURL(imageURL); 76 | return imageURL; 77 | } catch (e) { 78 | console.log(e); 79 | alert("Trouble uploading file"); 80 | } 81 | }; 82 | 83 | const uploadMetadata = async (metadata: object) => { 84 | try { 85 | const metadataBlob = new Blob([JSON.stringify(metadata)], { 86 | type: "application/json", 87 | }); 88 | 89 | const metadataFormData = new FormData(); 90 | metadataFormData.set( 91 | "file", 92 | metadataBlob, 93 | `metadata-${formData.name}.json` 94 | ); 95 | 96 | const response = await fetch("/api/files", { 97 | method: "POST", 98 | body: metadataFormData, 99 | }); 100 | 101 | if (!response.ok) throw new Error("Failed to upload metadata"); 102 | 103 | alert("Metadata uploaded successfully!"); 104 | 105 | const metadataURI = await response.json(); 106 | console.log("Metadata URI in upload metadata function:", metadataURI); 107 | return metadataURI; 108 | } catch (e) { 109 | console.error("Error uploading metadata:", e); 110 | alert("Trouble uploading metadata"); 111 | } 112 | }; 113 | 114 | const handleSubmit = async (e: React.FormEvent) => { 115 | e.preventDefault(); 116 | setIsLoading(true); 117 | 118 | try { 119 | const connection = getConnection(); 120 | if (wallet.publicKey) { 121 | const balance = await connection.getBalance(wallet.publicKey); 122 | const balanceInSOL = balance / 1e9; 123 | if (balanceInSOL < 0.01) { 124 | alert( 125 | "Insufficient balance, transactions might fail, please request an airdrop" 126 | ); 127 | setIsLoading(false); 128 | return; 129 | } 130 | } 131 | setLoadingText("Uploading metadata files..."); 132 | const imageURL = await uploadImageFile(); 133 | if (imageURL) { 134 | //metadata upload 135 | const metadata = { 136 | name: formData.name, 137 | symbol: formData.symbol, 138 | image: imageURL, 139 | }; 140 | const metadataURI = await uploadMetadata(metadata); 141 | console.log("Metadata URI just before createtoken:", metadataURI); 142 | 143 | //create token 144 | setLoadingText("Creating token..."); 145 | const result = await createToken(formData, wallet, metadataURI); 146 | 147 | setLoadingText("Storing to db..."); 148 | const response = await fetch("/api/tokenMints", { 149 | method: "POST", 150 | headers: { "Content-Type": "application/json" }, 151 | body: JSON.stringify({ address: result.mint }), 152 | }); 153 | 154 | const data = await response.json(); 155 | if (!response.ok) { 156 | throw new Error(data.error); 157 | } 158 | 159 | setTokenData(result); 160 | } 161 | 162 | setShowSuccess(true); 163 | } catch (error) { 164 | console.error("Error creating token:", error); 165 | } finally { 166 | setIsLoading(false); 167 | } 168 | }; 169 | 170 | return ( 171 | <> 172 | 173 |
177 | {/* Image Upload Section */} 178 | 211 | 212 |
213 | handleInputChange("name", e.target.value)} 219 | required 220 | variant="bordered" 221 | /> 222 | 223 | 230 | handleInputChange("symbol", e.target.value.toUpperCase()) 231 | } 232 | required 233 | variant="bordered" 234 | /> 235 | 236 | 245 | handleInputChange("decimals", parseInt(e.target.value)) 246 | } 247 | required 248 | variant="bordered" 249 | /> 250 | 251 | 259 | handleInputChange("initialMintAmount", parseInt(e.target.value)) 260 | } 261 | required 262 | variant="bordered" 263 | /> 264 | 265 | 273 |
274 |
275 |
276 | 277 | {tokenData && ( 278 | setShowSuccess(false)} 281 | tokenData={tokenData} 282 | /> 283 | )} 284 | 285 | ); 286 | } 287 | 288 | export default TokenForm; 289 | -------------------------------------------------------------------------------- /components/footer.tsx: -------------------------------------------------------------------------------- 1 | import { siteConfig } from "@/config/site"; 2 | import { Coins, Github, Twitter } from "lucide-react"; 3 | import Link from "next/link"; 4 | 5 | const selectedNetwork = process.env.NEXT_PUBLIC_SOLANA_NETWORK; 6 | 7 | export function Footer() { 8 | return ( 9 |
10 |
11 |
12 | {/* Brand Section */} 13 |
14 |
15 | 16 | 17 | Token Launchpad 18 | 19 |
20 |

21 | Launch your custom tokens on Solana with ease. Simple, fast, and 22 | secure token creation platform. 23 |

24 |
25 | 26 | {/* Quick Links */} 27 |
28 |

29 | Quick Links 30 |

31 |
    32 |
  • 33 | 37 | Create Token 38 | 39 |
  • 40 |
  • 41 | 45 | My Tokens 46 | 47 |
  • 48 | 49 | {selectedNetwork == "devnet" ? ( 50 | <> 51 |
  • 52 | 56 | Airdrop 57 | 58 |
  • 59 |
  • 60 | 64 | Mainnet 65 | 66 |
  • 67 | 68 | ) : ( 69 |
  • 70 | 74 | Devnet 75 | 76 |
  • 77 | )} 78 |
79 |
80 | 81 | {/* Social Links */} 82 |
83 |

84 | Connect 85 |

86 | 104 |
105 |
106 | 107 |

108 | © {new Date().getFullYear()} Token Launchpad. All rights reserved. 109 |

110 |
111 |
112 | ); 113 | } 114 | -------------------------------------------------------------------------------- /components/icons.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { IconSvgProps } from "@/types"; 4 | 5 | export const Logo: React.FC = ({ 6 | size = 36, 7 | width, 8 | height, 9 | ...props 10 | }) => ( 11 | 18 | 24 | 25 | ); 26 | 27 | export const DiscordIcon: React.FC = ({ 28 | size = 24, 29 | width, 30 | height, 31 | ...props 32 | }) => { 33 | return ( 34 | 40 | 44 | 45 | ); 46 | }; 47 | 48 | export const TwitterIcon: React.FC = ({ 49 | size = 24, 50 | width, 51 | height, 52 | ...props 53 | }) => { 54 | return ( 55 | 61 | 65 | 66 | ); 67 | }; 68 | 69 | export const GithubIcon: React.FC = ({ 70 | size = 24, 71 | width, 72 | height, 73 | ...props 74 | }) => { 75 | return ( 76 | 82 | 88 | 89 | ); 90 | }; 91 | 92 | export const MoonFilledIcon = ({ 93 | size = 24, 94 | width, 95 | height, 96 | ...props 97 | }: IconSvgProps) => ( 98 | 112 | ); 113 | 114 | export const SunFilledIcon = ({ 115 | size = 24, 116 | width, 117 | height, 118 | ...props 119 | }: IconSvgProps) => ( 120 | 134 | ); 135 | 136 | export const HeartFilledIcon = ({ 137 | size = 24, 138 | width, 139 | height, 140 | ...props 141 | }: IconSvgProps) => ( 142 | 159 | ); 160 | 161 | export const SearchIcon = (props: IconSvgProps) => ( 162 | 187 | ); 188 | 189 | export const NextUILogo: React.FC = (props) => { 190 | const { width, height = 40 } = props; 191 | 192 | return ( 193 | 201 | 205 | 209 | 213 | 214 | ); 215 | }; 216 | -------------------------------------------------------------------------------- /components/navbar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { 3 | Navbar as NextUINavbar, 4 | NavbarContent, 5 | NavbarMenu, 6 | NavbarMenuToggle, 7 | NavbarBrand, 8 | NavbarItem, 9 | NavbarMenuItem, 10 | } from "@nextui-org/navbar"; 11 | import dynamic from "next/dynamic"; 12 | import { Link } from "@nextui-org/link"; 13 | import { link as linkStyles } from "@nextui-org/theme"; 14 | import NextLink from "next/link"; 15 | import clsx from "clsx"; 16 | 17 | import { siteConfig } from "@/config/site"; 18 | import { ThemeSwitch } from "@/components/theme-switch"; 19 | import { GithubIcon } from "@/components/icons"; 20 | import { Coins } from "lucide-react"; 21 | const WalletMultiButton = dynamic( 22 | () => 23 | import("@solana/wallet-adapter-react-ui").then( 24 | (mod) => mod.WalletMultiButton 25 | ), 26 | { 27 | ssr: false, 28 | } 29 | ); 30 | 31 | import "@solana/wallet-adapter-react-ui/styles.css"; 32 | import { NetworkSelector } from "./navbar/NetworkSelector"; 33 | 34 | export const Navbar = () => { 35 | return ( 36 | 37 | 38 | 39 | 40 | 41 | 42 | Token Launchpad 43 | 44 | 45 | 46 | 47 | 48 | {/* rightmost content */} 49 | 53 |
    54 | {siteConfig.navItems.map((item) => ( 55 | 56 | 64 | {item.label} 65 | 66 | 67 | ))} 68 | 69 |
70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
92 | {siteConfig.navMenuItems.map((item, index) => ( 93 | 94 | 95 | {item.label} 96 | 97 | 98 | ))} 99 | 100 | 101 |
102 |
103 |
104 | ); 105 | }; 106 | -------------------------------------------------------------------------------- /components/navbar/NetworkSelector.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@nextui-org/button"; 2 | import { 3 | Dropdown, 4 | DropdownItem, 5 | DropdownMenu, 6 | DropdownTrigger, 7 | } from "@nextui-org/dropdown"; 8 | import { Network, Globe } from "lucide-react"; 9 | import { useState } from "react"; 10 | 11 | type NetworkType = "devnet" | "mainnet"; 12 | 13 | export function NetworkSelector() { 14 | const [selectedNetwork, setSelectedNetwork] = useState( 15 | process.env.NEXT_PUBLIC_SOLANA_NETWORK as NetworkType 16 | ); 17 | 18 | return ( 19 | 20 | 21 | 35 | 36 | 37 | {selectedNetwork == "devnet" ? ( 38 | 39 | } 42 | description="Use for testing" 43 | className="text-warning" 44 | > 45 | Devnet 46 | 47 | } 50 | description="Production network" 51 | className="text-success" 52 | href="https://solanatokenlaunchpadmainnet.vercel.app/" 53 | > 54 | Mainnet 55 | 56 | 57 | ) : ( 58 | 59 | } 62 | description="Production network" 63 | className="text-success" 64 | > 65 | Mainnet 66 | 67 | 68 | } 71 | description="Use for testing" 72 | className="text-warning" 73 | href="https://solanatokenlaunchpad.vercel.app/" 74 | > 75 | Devnet 76 | 77 | 78 | )} 79 | 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /components/primitives.ts: -------------------------------------------------------------------------------- 1 | import { tv } from "tailwind-variants"; 2 | 3 | export const title = tv({ 4 | base: "tracking-tight inline font-semibold", 5 | variants: { 6 | color: { 7 | violet: "from-[#FF1CF7] to-[#b249f8]", 8 | yellow: "from-[#FF705B] to-[#FFB457]", 9 | blue: "from-[#5EA2EF] to-[#0072F5]", 10 | cyan: "from-[#00b7fa] to-[#01cfea]", 11 | green: "from-[#6FEE8D] to-[#17c964]", 12 | pink: "from-[#FF72E1] to-[#F54C7A]", 13 | foreground: "dark:from-[#FFFFFF] dark:to-[#4B4B4B]", 14 | }, 15 | size: { 16 | sm: "text-3xl lg:text-4xl", 17 | md: "text-[2.3rem] lg:text-5xl leading-9", 18 | lg: "text-4xl lg:text-6xl", 19 | }, 20 | fullWidth: { 21 | true: "w-full block", 22 | }, 23 | }, 24 | defaultVariants: { 25 | size: "md", 26 | }, 27 | compoundVariants: [ 28 | { 29 | color: [ 30 | "violet", 31 | "yellow", 32 | "blue", 33 | "cyan", 34 | "green", 35 | "pink", 36 | "foreground", 37 | ], 38 | class: "bg-clip-text text-transparent bg-gradient-to-b", 39 | }, 40 | ], 41 | }); 42 | 43 | export const subtitle = tv({ 44 | base: "w-full md:w-1/2 my-2 text-lg lg:text-xl text-default-600 block max-w-full", 45 | variants: { 46 | fullWidth: { 47 | true: "!w-full", 48 | }, 49 | }, 50 | defaultVariants: { 51 | fullWidth: true, 52 | }, 53 | }); 54 | -------------------------------------------------------------------------------- /components/theme-switch.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { FC } from "react"; 4 | import { VisuallyHidden } from "@react-aria/visually-hidden"; 5 | import { SwitchProps, useSwitch } from "@nextui-org/switch"; 6 | import { useTheme } from "next-themes"; 7 | import { useIsSSR } from "@react-aria/ssr"; 8 | import clsx from "clsx"; 9 | 10 | import { SunFilledIcon, MoonFilledIcon } from "@/components/icons"; 11 | 12 | export interface ThemeSwitchProps { 13 | className?: string; 14 | classNames?: SwitchProps["classNames"]; 15 | } 16 | 17 | export const ThemeSwitch: FC = ({ 18 | className, 19 | classNames, 20 | }) => { 21 | const { theme, setTheme } = useTheme(); 22 | const isSSR = useIsSSR(); 23 | 24 | const onChange = () => { 25 | theme === "light" ? setTheme("dark") : setTheme("light"); 26 | }; 27 | 28 | const { 29 | Component, 30 | slots, 31 | isSelected, 32 | getBaseProps, 33 | getInputProps, 34 | getWrapperProps, 35 | } = useSwitch({ 36 | isSelected: theme === "light" || isSSR, 37 | "aria-label": `Switch to ${theme === "light" || isSSR ? "dark" : "light"} mode`, 38 | onChange, 39 | }); 40 | 41 | return ( 42 | 51 | 52 | 53 | 54 |
73 | {!isSelected || isSSR ? ( 74 | 75 | ) : ( 76 | 77 | )} 78 |
79 |
80 | ); 81 | }; 82 | -------------------------------------------------------------------------------- /config/fonts.ts: -------------------------------------------------------------------------------- 1 | import { Fira_Code as FontMono, Inter as FontSans } from "next/font/google"; 2 | 3 | export const fontSans = FontSans({ 4 | subsets: ["latin"], 5 | variable: "--font-sans", 6 | }); 7 | 8 | export const fontMono = FontMono({ 9 | subsets: ["latin"], 10 | variable: "--font-mono", 11 | }); 12 | -------------------------------------------------------------------------------- /config/site.ts: -------------------------------------------------------------------------------- 1 | export type SiteConfig = typeof siteConfig; 2 | 3 | const selectedNetwork = process.env.NEXT_PUBLIC_SOLANA_NETWORK as 4 | | "devnet" 5 | | "mainnet"; 6 | 7 | export const siteConfig = { 8 | name: "Solana Token Launchpad", 9 | description: "Launch your token on the Solana blockchain in minutes.", 10 | navItems: [ 11 | { 12 | label: "Home", 13 | href: "/", 14 | }, 15 | { 16 | label: "Create Token", 17 | href: "/create", 18 | }, 19 | { 20 | label: "My Tokens", 21 | href: "/mint", 22 | }, 23 | ...(selectedNetwork == "devnet" 24 | ? [{ label: "Airdrop", href: "/airdrop" }] 25 | : []), // Add 'Airdrop' conditionally 26 | ], 27 | navMenuItems: [ 28 | { 29 | label: "Home", 30 | href: "/", 31 | }, 32 | { 33 | label: "Create Token", 34 | href: "/create", 35 | }, 36 | { 37 | label: "Mint Tokens", 38 | href: "/mint", 39 | }, 40 | ...(selectedNetwork == "devnet" 41 | ? [{ label: "Airdrop", href: "/airdrop" }] 42 | : []), 43 | ], 44 | links: { 45 | github: "https://github.com/abhiraj2404/Token_launchpad", 46 | twitter: "https://x.com/abhiraj_2404", 47 | }, 48 | }; 49 | -------------------------------------------------------------------------------- /config/solana.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Connection, 3 | Keypair, 4 | PublicKey, 5 | Signer, 6 | SystemProgram, 7 | Transaction, 8 | TransactionSignature, 9 | } from "@solana/web3.js"; 10 | import { 11 | ASSOCIATED_TOKEN_PROGRAM_ID, 12 | createAssociatedTokenAccountInstruction, 13 | createInitializeMint2Instruction, 14 | createMintToInstruction, 15 | getAssociatedTokenAddressSync, 16 | getMinimumBalanceForRentExemptMint, 17 | MINT_SIZE, 18 | TOKEN_PROGRAM_ID, 19 | } from "@solana/spl-token"; 20 | import { 21 | createInitializeMetadataPointerInstruction, 22 | createInitializeMintInstruction, 23 | ExtensionType, 24 | getMintLen, 25 | LENGTH_SIZE, 26 | TOKEN_2022_PROGRAM_ID, 27 | TYPE_SIZE, 28 | } from "@solana/spl-token"; 29 | import { 30 | createInitializeInstruction, 31 | pack, 32 | TokenMetadata, 33 | } from "@solana/spl-token-metadata"; 34 | import { TokenFormData, TokenMetadata as TokenMetadataType } from "@/types"; 35 | 36 | export const getConnection = () => 37 | new Connection(process.env.NEXT_PUBLIC_SOLANA_ENDPOINT as string, "confirmed"); 38 | 39 | export async function creatingATA( 40 | connection: Connection, 41 | mint: PublicKey, 42 | owner: PublicKey, 43 | wallet: any, 44 | programId = TOKEN_2022_PROGRAM_ID, 45 | associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID, 46 | allowOwnerOffCurve = false 47 | ): Promise { 48 | const associatedToken = getAssociatedTokenAddressSync( 49 | mint, 50 | owner, 51 | allowOwnerOffCurve, 52 | programId, 53 | associatedTokenProgramId 54 | ); 55 | 56 | const transaction = new Transaction().add( 57 | createAssociatedTokenAccountInstruction( 58 | owner, 59 | associatedToken, 60 | owner, 61 | mint, 62 | programId, 63 | associatedTokenProgramId 64 | ) 65 | ); 66 | 67 | transaction.feePayer = owner; 68 | transaction.recentBlockhash = ( 69 | await connection.getLatestBlockhash() 70 | ).blockhash; 71 | 72 | await wallet.sendTransaction(transaction, connection); 73 | 74 | return associatedToken; 75 | } 76 | 77 | export async function mintTo( 78 | connection: Connection, 79 | wallet: any, // Wallet object with `sendTransaction` method 80 | mint: PublicKey, 81 | destination: PublicKey, 82 | authority: Signer | PublicKey, 83 | amount: number | bigint, 84 | multiSigners: Signer[] = [], 85 | programId = TOKEN_2022_PROGRAM_ID 86 | ): Promise { 87 | function getSigners( 88 | signerOrMultisig: Signer | PublicKey, 89 | multiSigners: Signer[] 90 | ): [PublicKey, Signer[]] { 91 | return signerOrMultisig instanceof PublicKey 92 | ? [signerOrMultisig, multiSigners] 93 | : [signerOrMultisig.publicKey, [signerOrMultisig]]; 94 | } 95 | 96 | const [authorityPublicKey, signers] = getSigners(authority, multiSigners); 97 | 98 | const mintToInstruction = createMintToInstruction( 99 | mint, 100 | destination, 101 | authorityPublicKey, 102 | amount, 103 | multiSigners, 104 | programId 105 | ); 106 | 107 | const transaction = new Transaction().add(mintToInstruction); 108 | 109 | // Set the recent blockhash and fee payer 110 | transaction.feePayer = wallet.publicKey; 111 | const { blockhash } = await connection.getLatestBlockhash(); 112 | transaction.recentBlockhash = blockhash; 113 | 114 | // Partially sign the transaction if required 115 | if (signers.length > 0) { 116 | transaction.partialSign(...signers); 117 | } 118 | 119 | // Send the transaction using the wallet 120 | return await wallet.sendTransaction(transaction, connection); 121 | } 122 | 123 | export const createToken = async ( 124 | { name, symbol, decimals, initialMintAmount }: TokenFormData, 125 | wallet: any, 126 | uri: string | null 127 | ): Promise => { 128 | const connection = getConnection(); 129 | 130 | //creating mint account 131 | const mintKeypair = Keypair.generate(); 132 | console.log("metadataURI", uri); 133 | //metadata 134 | const metadata: TokenMetadata = { 135 | mint: mintKeypair.publicKey, 136 | name: name, 137 | symbol: symbol, 138 | uri: uri as string, 139 | additionalMetadata: [], 140 | }; 141 | const mintLen = getMintLen([ExtensionType.MetadataPointer]); 142 | const metadataLen = TYPE_SIZE + LENGTH_SIZE + pack(metadata).length; 143 | const mintLamports = await connection.getMinimumBalanceForRentExemption( 144 | mintLen + metadataLen 145 | ); 146 | const mintTransaction = new Transaction().add( 147 | SystemProgram.createAccount({ 148 | fromPubkey: wallet.publicKey, 149 | newAccountPubkey: mintKeypair.publicKey, 150 | space: mintLen, 151 | lamports: mintLamports, 152 | programId: TOKEN_2022_PROGRAM_ID, 153 | }), 154 | createInitializeMetadataPointerInstruction( 155 | mintKeypair.publicKey, 156 | wallet.publicKey, 157 | mintKeypair.publicKey, 158 | TOKEN_2022_PROGRAM_ID 159 | ), 160 | createInitializeMintInstruction( 161 | mintKeypair.publicKey, 162 | decimals, 163 | wallet.publicKey, 164 | null, 165 | TOKEN_2022_PROGRAM_ID 166 | ), 167 | createInitializeInstruction({ 168 | programId: TOKEN_2022_PROGRAM_ID, 169 | mint: mintKeypair.publicKey, 170 | metadata: mintKeypair.publicKey, 171 | name: metadata.name, 172 | symbol: metadata.symbol, 173 | uri: metadata.uri, 174 | mintAuthority: wallet.publicKey, 175 | updateAuthority: wallet.publicKey, 176 | }) 177 | ); 178 | mintTransaction.feePayer = wallet.publicKey; 179 | mintTransaction.recentBlockhash = ( 180 | await connection.getLatestBlockhash() 181 | ).blockhash; 182 | mintTransaction.partialSign(mintKeypair); 183 | 184 | console.log("mintTransaction", mintTransaction); 185 | await wallet.sendTransaction(mintTransaction, connection); 186 | console.log(`Token mint created at ${mintKeypair.publicKey.toBase58()}`); 187 | 188 | // const lamports = await getMinimumBalanceForRentExemptMint(connection); 189 | 190 | // const transaction = new Transaction().add( 191 | // SystemProgram.createAccount({ 192 | // fromPubkey: wallet.publicKey, 193 | // newAccountPubkey: mintKeypair.publicKey, 194 | // space: MINT_SIZE, 195 | // lamports, 196 | // programId: TOKEN_PROGRAM_ID, 197 | // }), 198 | // createInitializeMint2Instruction( 199 | // mintKeypair.publicKey, 200 | // decimals, 201 | // wallet.publicKey, 202 | // wallet.publicKey, 203 | // TOKEN_PROGRAM_ID 204 | // ) 205 | // ); 206 | 207 | // transaction.feePayer = wallet.publicKey; 208 | // transaction.recentBlockhash = ( 209 | // await connection.getLatestBlockhash() 210 | // ).blockhash; 211 | // transaction.partialSign(mintKeypair); 212 | 213 | // await wallet.sendTransaction(transaction, connection); 214 | // console.log(`Token mint created at ${mintKeypair.publicKey.toBase58()}`); 215 | 216 | //creatin associated token account 217 | const tokenAccount = await creatingATA( 218 | connection, 219 | mintKeypair.publicKey, 220 | wallet.publicKey, 221 | wallet 222 | ); 223 | console.log( 224 | `Created an account for the owner of the token, here is the account ${tokenAccount.toBase58()}` 225 | ); 226 | 227 | //minting tokens 228 | 229 | const mintAmount = initialMintAmount * Math.pow(10, decimals); 230 | await mintTo( 231 | connection, 232 | wallet, // Payer wallet 233 | mintKeypair.publicKey, // Mint address 234 | tokenAccount, // Destination address 235 | wallet.publicKey, // Mint authority 236 | mintAmount // Amount to mint 237 | ); 238 | 239 | console.log(`Token mint created at ${mintKeypair.publicKey.toBase58()}`); 240 | console.log(`Minted ${mintAmount} tokens to ${wallet.publicKey.toBase58()}`); 241 | 242 | return { 243 | mint: mintKeypair.publicKey.toBase58(), 244 | mintAuthority: wallet.publicKey.toBase58(), 245 | name, 246 | symbol, 247 | decimals, 248 | initialMintAmount, 249 | }; 250 | }; 251 | -------------------------------------------------------------------------------- /models/MintAddresses.ts: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import { unique } from "next/dist/build/utils"; 3 | 4 | const MintAddressesSchema = new mongoose.Schema({ 5 | address: { 6 | type: String, 7 | required: true, 8 | unique: true, 9 | trim: true, 10 | }, 11 | }); 12 | 13 | const MintAddresses = 14 | mongoose.models.MintAddresses || 15 | mongoose.model("MintAddresses", MintAddressesSchema); 16 | 17 | export default MintAddresses; 18 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-app-template", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev --turbopack", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "eslint . --ext .ts,.tsx -c .eslintrc.json --fix" 10 | }, 11 | "dependencies": { 12 | "@nextui-org/button": "2.2.3", 13 | "@nextui-org/card": "^2.2.7", 14 | "@nextui-org/chip": "^2.2.4", 15 | "@nextui-org/code": "2.2.3", 16 | "@nextui-org/dropdown": "^2.3.7", 17 | "@nextui-org/input": "2.4.3", 18 | "@nextui-org/kbd": "2.2.3", 19 | "@nextui-org/link": "2.2.3", 20 | "@nextui-org/listbox": "2.3.3", 21 | "@nextui-org/modal": "^2.2.5", 22 | "@nextui-org/navbar": "2.2.3", 23 | "@nextui-org/snippet": "2.2.4", 24 | "@nextui-org/spinner": "^2.2.4", 25 | "@nextui-org/switch": "2.2.3", 26 | "@nextui-org/system": "2.4.3", 27 | "@nextui-org/theme": "2.4.1", 28 | "@nextui-org/tooltip": "^2.2.5", 29 | "@react-aria/ssr": "3.9.7", 30 | "@react-aria/visually-hidden": "3.8.18", 31 | "@solana/spl-token": "^0.4.9", 32 | "@solana/spl-token-metadata": "^0.1.6", 33 | "@solana/wallet-adapter-base": "^0.9.23", 34 | "@solana/wallet-adapter-react": "^0.15.35", 35 | "@solana/wallet-adapter-react-ui": "^0.9.35", 36 | "@solana/wallet-adapter-wallets": "^0.19.32", 37 | "@solana/web3.js": "^1.98.0", 38 | "@vercel/analytics": "^1.4.1", 39 | "clsx": "2.1.1", 40 | "framer-motion": "11.13.1", 41 | "intl-messageformat": "^10.5.0", 42 | "lucide-react": "^0.468.0", 43 | "mongoose": "^8.9.1", 44 | "next": "15.0.4", 45 | "next-themes": "^0.4.4", 46 | "nextui-cli": "^0.5.0", 47 | "pinata-web3": "^0.5.3", 48 | "react": "18.3.1", 49 | "react-dom": "18.3.1" 50 | }, 51 | "devDependencies": { 52 | "@next/eslint-plugin-next": "15.0.4", 53 | "@react-types/shared": "3.25.0", 54 | "@types/node": "20.5.7", 55 | "@types/react": "18.3.3", 56 | "@types/react-dom": "18.3.0", 57 | "@typescript-eslint/eslint-plugin": "8.11.0", 58 | "@typescript-eslint/parser": "8.11.0", 59 | "autoprefixer": "10.4.19", 60 | "eslint": "^8.57.0", 61 | "eslint-config-next": "15.0.4", 62 | "eslint-config-prettier": "9.1.0", 63 | "eslint-plugin-import": "^2.26.0", 64 | "eslint-plugin-jsx-a11y": "^6.4.1", 65 | "eslint-plugin-node": "^11.1.0", 66 | "eslint-plugin-prettier": "5.2.1", 67 | "eslint-plugin-react": "^7.23.2", 68 | "eslint-plugin-react-hooks": "^4.6.0", 69 | "eslint-plugin-unused-imports": "4.1.4", 70 | "postcss": "8.4.49", 71 | "prettier": "3.3.3", 72 | "tailwind-variants": "0.1.20", 73 | "tailwindcss": "3.4.16", 74 | "typescript": "5.6.3" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/Launchpad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhiraj2404/Token_launchpad/965d04cfc50de32b0956041115752a4730245fb5/public/Launchpad.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhiraj2404/Token_launchpad/965d04cfc50de32b0956041115752a4730245fb5/public/favicon.ico -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const { nextui } = require("@nextui-org/theme"); 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | module.exports = { 5 | content: [ 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 8 | "./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}", 9 | ], 10 | theme: { 11 | extend: { 12 | fontFamily: { 13 | sans: [ "var(--font-sans)" ], 14 | mono: [ "var(--font-mono)" ], 15 | }, 16 | }, 17 | }, 18 | darkMode: "class", 19 | plugins: [ nextui() ], 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /types/index.ts: -------------------------------------------------------------------------------- 1 | import { SVGProps } from "react"; 2 | 3 | export type IconSvgProps = SVGProps & { 4 | size?: number; 5 | }; 6 | 7 | export interface TokenFormData { 8 | name: string; 9 | symbol: string; 10 | decimals: number; 11 | initialMintAmount: number; 12 | } 13 | 14 | export interface TokenMetadata { 15 | mint: string; 16 | mintAuthority: string; 17 | name: string; 18 | symbol: string; 19 | decimals: number; 20 | initialMintAmount: number; 21 | } 22 | 23 | export interface TokenData { 24 | mint: string; 25 | name: string; 26 | symbol: string; 27 | decimals: number; 28 | totalSupply: number; 29 | imageUrl: string; 30 | mintAuthority: string; 31 | freezeAuthority: string | null; 32 | } 33 | -------------------------------------------------------------------------------- /utils/config.ts: -------------------------------------------------------------------------------- 1 | "server only"; 2 | 3 | import { PinataSDK } from "pinata-web3"; 4 | 5 | export const pinata = new PinataSDK({ 6 | pinataJwt: `${process.env.PINATA_JWT}`, 7 | pinataGateway: `${process.env.PINATA_GATEWAY_URL}`, 8 | }); 9 | -------------------------------------------------------------------------------- /utils/dbConnect.ts: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | type ConnectionObject = { 4 | isConnected?: number; 5 | }; 6 | 7 | const connection: ConnectionObject = {}; 8 | 9 | async function dbConnect(): Promise { 10 | if (connection.isConnected) { 11 | console.log("Already connected to database"); 12 | return; 13 | } 14 | 15 | try { 16 | const uri = 17 | process.env.NEXT_PUBLIC_SOLANA_NETWORK == "devnet" 18 | ? process.env.MONGODB_URI 19 | : process.env.MONGODB_URI_MAINNET; 20 | 21 | console.log(uri); 22 | const db = await mongoose.connect(uri as string, {}); 23 | connection.isConnected = db.connections[0].readyState; 24 | console.log("Connected to MongoDB"); 25 | } catch (error) { 26 | console.log("Error connecting to database: ", error); 27 | process.exit(1); 28 | } 29 | } 30 | 31 | export default dbConnect; 32 | --------------------------------------------------------------------------------