Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions .env.example
Original file line number Diff line number Diff line change
@@ -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 <your-identity>
# Then paste the returned hash here:
NEXT_PUBLIC_TOKEN_WASM_HASH=<your-wasm-hash-here>
# 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 <network> --source <your-identity>
# 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=
53 changes: 21 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand All @@ -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
35 changes: 17 additions & 18 deletions frontend/app/hooks/useDeployToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ function randomBytes(length: number): Buffer {
// ---------------------------------------------------------------------------
// Config
// ---------------------------------------------------------------------------
const TOKEN_WASM_HASH = process.env.NEXT_PUBLIC_TOKEN_WASM_HASH;

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -88,23 +86,24 @@ export function useDeployToken() {
const { connected, publicKey, signTransaction } = useWallet();
const { networkConfig } = useNetwork();

const deployToken = useCallback(
async (params: DeployTokenParams): Promise<DeployTokenResult> => {
// ── 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);

Expand Down
45 changes: 45 additions & 0 deletions scripts/check-env.js
Original file line number Diff line number Diff line change
@@ -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');
}
Loading