diff --git a/.env.example b/.env.example index 98187c9f..fe1637fe 100644 --- a/.env.example +++ b/.env.example @@ -1,16 +1,17 @@ # Stellar Network Configuration -# RPC endpoint for Soroban smart contract interactions -NEXT_PUBLIC_SOROBAN_RPC_URL=https://soroban-testnet.stellar.org - # Horizon API endpoint for account and transaction queries NEXT_PUBLIC_HORIZON_URL=https://horizon-testnet.stellar.org -# Network passphrase (use StellarSdk.Networks.TESTNET for testnet, StellarSdk.Networks.PUBLIC for mainnet) -NEXT_PUBLIC_NETWORK_PASSPHRASE=Test SDF Network ; September 2015 +# Mercury Indexer API Authentication Token (Optional) +# If omitted, indexer, transaction history, and /api/tokens/recent features degrade gracefully. +NEXT_PUBLIC_MERCURY_AUTH_TOKEN= -# Token Contract WASM Hash -# Upload the token contract WASM to the network first: -# cd contracts && soroban contract build -# soroban contract upload --wasm target/wasm32-unknown-unknown/release/soroban_token.wasm --network testnet --source -# Then paste the returned hash here: -NEXT_PUBLIC_TOKEN_WASM_HASH= +# Token Contract WASM Hashes +# WASM binaries must be deployed separately on each network. +# Upload the token contract WASM to the network: +# cd contracts && stellar contract build +# stellar contract upload --wasm target/wasm32-unknown-unknown/release/soroban_token.wasm --network --source +# Then set the returned hash for the corresponding network below: +NEXT_PUBLIC_TOKEN_WASM_HASH_TESTNET= +NEXT_PUBLIC_TOKEN_WASM_HASH_MAINNET= +NEXT_PUBLIC_TOKEN_WASM_HASH_FUTURENET= \ No newline at end of file diff --git a/README.md b/README.md index 2f3bd05c..d1a17234 100644 --- a/README.md +++ b/README.md @@ -24,26 +24,29 @@ Built for founders, DAOs, and developers who need a clean interface to launch to | Layer | Tech | |---|---| | Smart Contracts | Rust + Soroban SDK | -| Frontend | Next.js 14 + TypeScript | +| Frontend | Next.js 16 (16.1.6) + React 19 + TypeScript | | Styling | Tailwind CSS | | Wallet | Freighter API | | RPC | Stellar Horizon + Soroban RPC | -| Testing | Soroban CLI + Jest + Playwright | +| Testing | Soroban CLI + Jest | --- ## 📁 Project Structure - -``` soroban-token-launchpad/ ├── contracts/ │ ├── token/ # SEP-41 token contract (Rust) │ └── vesting/ # Vesting schedule contract (Rust) ├── frontend/ │ ├── app/ # Next.js app router pages +│ │ ├── allowances/ # Allowance management route +│ │ ├── api/ # Internal API handlers +│ │ ├── claim/ # Claiming portal route +│ │ └── my-account/ # Account management route │ ├── components/ # UI components │ ├── hooks/ # Stellar/Soroban React hooks -│ └── lib/ # Contract clients & utilities +│ ├── lib/ # Contract clients & utilities +│ └── messages/ # Localization & message catalogs ├── scripts/ # Deploy & keygen scripts └── docs/ # Architecture, event schema, and solvency docs ``` @@ -55,43 +58,29 @@ soroban-token-launchpad/ ### Prerequisites - Node.js 18+ -- Rust + `soroban-cli` +- Rust + `stellar-cli` - Freighter browser extension ### Install ```bash -git clone https://github.com/your-org/soroban-token-launchpad -cd soroban-token-launchpad +git clone [https://github.com/soropad/launchpad.git](https://github.com/soropad/launchpad.git) +cd launchpad/frontend npm install -``` - -### Run locally - -```bash +Run locally +Bash # Build contracts -cd contracts && soroban contract build +cd contracts && stellar contract build # Start frontend cd frontend && npm run dev -``` - -### Deploy to testnet - -```bash +Deploy to testnet +Bash npm run deploy:testnet -``` - ---- - -## 🤝 Contributing - -Contributions are welcome! Many issues are tagged `good first issue` and available through the [Stellar Wave Program on Drips](https://www.drips.network/wave). - -See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup and PR guidelines. - ---- +🤝 Contributing +Contributions are welcome! Many issues are tagged good first issue and available through the Stellar Wave Program on Drips. -## 📄 License +See CONTRIBUTING.md for setup and PR guidelines. -MIT +📄 License +MIT \ No newline at end of file diff --git a/frontend/app/hooks/useDeployToken.ts b/frontend/app/hooks/useDeployToken.ts index 73d09e93..49ea94ff 100644 --- a/frontend/app/hooks/useDeployToken.ts +++ b/frontend/app/hooks/useDeployToken.ts @@ -24,8 +24,6 @@ function randomBytes(length: number): Buffer { // --------------------------------------------------------------------------- // Config // --------------------------------------------------------------------------- -const TOKEN_WASM_HASH = process.env.NEXT_PUBLIC_TOKEN_WASM_HASH; - // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- @@ -88,23 +86,24 @@ export function useDeployToken() { const { connected, publicKey, signTransaction } = useWallet(); const { networkConfig } = useNetwork(); - const deployToken = useCallback( - async (params: DeployTokenParams): Promise => { - // ── Step 0: Validation ──────────────────────────────────────────── - if (!connected || !publicKey) { - throw { - message: "Wallet not connected. Please connect your wallet and try again.", - type: "validation", - } as DeployTokenError; - } + const networkKey = (networkConfig?.network || networkConfig?.id || "").toLowerCase(); - if (!TOKEN_WASM_HASH) { - throw { - message: - "Token WASM hash not configured. Please set NEXT_PUBLIC_TOKEN_WASM_HASH in your environment.", - type: "validation", - } as DeployTokenError; - } + let TOKEN_WASM_HASH: string | undefined; + + if (networkKey.includes("testnet")) { + TOKEN_WASM_HASH = process.env.NEXT_PUBLIC_TOKEN_WASM_HASH_TESTNET; + } else if (networkKey.includes("mainnet") || networkKey.includes("public")) { + TOKEN_WASM_HASH = process.env.NEXT_PUBLIC_TOKEN_WASM_HASH_MAINNET; + } else if (networkKey.includes("futurenet")) { + TOKEN_WASM_HASH = process.env.NEXT_PUBLIC_TOKEN_WASM_HASH_FUTURENET; + } + + if (!TOKEN_WASM_HASH) { + throw { + message: `Token WASM hash not configured for network "${networkConfig?.network || networkConfig?.id || "selected"}". Please set NEXT_PUBLIC_TOKEN_WASM_HASH_${(networkConfig?.network || networkConfig?.id || "NETWORK").toUpperCase()} in your environment.`, + type: "validation", + } as DeployTokenError; + } const rpc = new StellarSdk.rpc.Server(networkConfig.rpcUrl); diff --git a/scripts/check-env.js b/scripts/check-env.js new file mode 100644 index 00000000..d1c3ab9a --- /dev/null +++ b/scripts/check-env.js @@ -0,0 +1,45 @@ +const fs = require('fs'); +const path = require('path'); + +const envExamplePath = path.resolve(__dirname, '../.env.example'); +const envExampleContent = fs.readFileSync(envExamplePath, 'utf8'); + +// Extract all NEXT_PUBLIC_ keys defined in .env.example +const exampleKeys = new Set( + [...envExampleContent.matchAll(/^(NEXT_PUBLIC_[A-Z0-9_]+)=/gm)].map((m) => m[1]) +); + +// Scan files in frontend/app, frontend/components, frontend/hooks, frontend/lib +const directoriesToScan = ['frontend/app', 'frontend/components', 'frontend/hooks', 'frontend/lib']; +const missingKeys = new Set(); + +function scanDir(dir) { + if (!fs.existsSync(dir)) return; + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + scanDir(fullPath); + } else if (/\.(ts|tsx|js|jsx)$/.test(entry.name)) { + const content = fs.readFileSync(fullPath, 'utf8'); + const matches = content.matchAll(/process\.env\.(NEXT_PUBLIC_[A-Z0-9_]+)/g); + for (const match of matches) { + const key = match[1]; + if (!exampleKeys.has(key)) { + missingKeys.add(`${key} (found in ${fullPath})`); + } + } + } + } +} + +directoriesToScan.forEach(scanDir); + +if (missingKeys.size > 0) { + console.error('❌ CI Error: The following NEXT_PUBLIC_ env variables are used in code but missing from .env.example:\n'); + missingKeys.forEach((key) => console.error(` - ${key}`)); + process.exit(1); +} else { + console.log('✅ CI Check Passed: All NEXT_PUBLIC_ environment variables in source exist in .env.example'); +} \ No newline at end of file