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
6 changes: 6 additions & 0 deletions .changeset/fruity-lands-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@lit-protocol/contracts': minor
'@lit-protocol/networks': minor
---

Add `naga` and `naga-proto` networks. Create per-network entrypoints and subpath exports (naga, naga-production, naga-proto, naga-staging, naga-test, naga-dev, naga-local) for better tree-shaking
6 changes: 5 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ out

# allow lit-login-server build output for docker image
!dist/apps/lit-login-server/**
!dist/apps/lit-login-server
!dist/apps/lit-login-server

# allow explorer build output for docker image
!apps/explorer/dist/**
!apps/explorer/dist
8 changes: 4 additions & 4 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"root": true,
"ignorePatterns": ["**/*"],
"plugins": ["@nrwl/nx", "import"],
"plugins": ["@nx", "import"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
"@nrwl/nx/enforce-module-boundaries": [
"@nx/enforce-module-boundaries": [
"error",
{
"enforceBuildableLibDependency": true,
Expand All @@ -23,14 +23,14 @@
},
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nrwl/nx/typescript"],
"extends": ["plugin:@nx/typescript"],
"rules": {
"@typescript-eslint/no-inferrable-types": "off"
}
},
{
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nrwl/nx/javascript"],
"extends": ["plugin:@nx/javascript"],
"rules": {}
}
],
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pnpm install && pnpm build
# (Optional) Request a private rpc url from
# https://hub.conduit.xyz/chronicle-yellowstone-testnet-9qgmzfcohk
LIT_YELLOWSTONE_PRIVATE_RPC_URL=<private-rpc-url>
# (Optional) Mainnet RPC override for naga-proto / naga
LIT_MAINNET_RPC_URL=<mainnet-rpc-url>

# For live networks (naga-dev, naga-staging)
LIVE_MASTER_ACCOUNT=<master-account-private-key>
Expand All @@ -48,6 +50,8 @@ LIVE_MASTER_ACCOUNT=<master-account-private-key>
LOCAL_MASTER_ACCOUNT=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
```

When `NETWORK` is set to `naga-proto` or `naga`, the test helpers only top up generated accounts with `0.01` LIT and deposit `0.01` LIT into the Lit Ledger to avoid locking up excess mainnet funds.

## Command

```bash
Expand Down
10 changes: 10 additions & 0 deletions apps/explorer/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
}
]
}
28 changes: 28 additions & 0 deletions apps/explorer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.vercel
.cursor
.env
.plan
21 changes: 21 additions & 0 deletions apps/explorer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This file is generated by Nx.
#
# Build the docker image with `pnpm nx run explorer:docker-build`.
# Tip: Modify "docker-build" options in project.json to change docker build args.
#
# Run the container with `docker run -p 4173:80 -t explorer`.
FROM --platform=linux/amd64 docker.io/nginx:alpine

ENV NODE_ENV=production

# Remove default nginx site content
RUN rm -rf /usr/share/nginx/html/*

# Copy SPA routing config
COPY apps/explorer/nginx.conf /etc/nginx/conf.d/default.conf

# Copy the static assets produced by `nx run explorer:build`
COPY apps/explorer/dist /usr/share/nginx/html

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
72 changes: 72 additions & 0 deletions apps/explorer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Lit Explorer Naga

Lit Explorer Naga is an application that allows you to authenticate with Lit Protocol using the native Lit auth methods.

# Env vars

```bash
# Global Settings
VITE_LOGIN_SERVICE_URL=https://login.litgateway.com
VITE_LOGIN_DISCORD_CLIENT_ID=1052874239658692668

# Network-Specific Auth Service URLs
VITE_AUTH_SERVICE_URL_NAGA_DEV=https://naga-dev-auth-service.getlit.dev
VITE_AUTH_SERVICE_URL_NAGA_TEST=https://naga-test-auth-service.getlit.dev
VITE_AUTH_SERVICE_URL_NAGA=https://naga-auth-service.getlit.dev
```

## Getting started

```
pnpm install
pnpm nx run explorer:dev
```

## Docker

To produce a deployable container:

```
pnpm nx run explorer:build
pnpm nx run explorer:docker-build
docker run -p 4173:80 explorer
```

The image serves the built assets via Nginx and includes SPA routing so client-side navigation works.

## Adding Lit Action examples

- Add a new file in `src/lit-action-examples/entries/` that default-exports a `LitActionExample`. The `id` must be unique.
- Use `String.raw` to define multiline snippets, e.g. ``const code = String.raw\`...\`;`` and fill in `title`, optional `description`, `order`, and any default `jsParams`.
- The registry auto-loads every file in that directory, so your example will appear in the Lit Action editor once you save and refresh the app.
- Prefer small, focused samples that demonstrate a single concept; link to docs inside the description if extra context is needed.

## FAQs

### What "logged-in" means here

- You are considered "logged-in" when both a PKP and an auth context exist in state.

### How you become "logged-in"

After authenticating with a method (Google, Discord, WebAuthn, EOA, Stytch, Custom), either:

- You select an existing PKP in the PKP selection section
- You mint a new PKP and immediately create `authContext`, then set `user`

### What redirect happens and when

The `<LitAuthProvider />` does not redirect on successful login. It simply closes the modal once user is set and isAuthenticated becomes true.

The dashboard is always the index route for `/`, and it conditionally renders based on auth state from context. When the user logs in, React re-renders the same component with different UI.

Inside `LoggedInDashboard`, it reads user from `useLitAuth()`. If there’s no user, it shows a sign-in experience and, in popup mode, auto-opens the modal.

# Screenshots

## Login Modal

![Login Modal](./public/screenshot-1.png)

## Logged in Dashboard
![Logged in Dashboard](./public/screenshot-2.png)
169 changes: 169 additions & 0 deletions apps/explorer/check-deps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#!/usr/bin/env node

/**
* Dependency Checker Script
*
* This script scans node_modules for missing dependencies that could cause
* "Failed to resolve module specifier" errors in production builds.
*
* Usage: node check-deps.js
*/

import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

console.log('🔍 Scanning for missing dependencies...\n');

// Read current package.json
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
const currentDeps = {
...packageJson.dependencies || {},
...packageJson.devDependencies || {}
};

// Function to extract require/import statements
function extractDependencies(filePath) {
try {
const content = fs.readFileSync(filePath, 'utf8');
const requireMatches = content.match(/require\(["']([^"']+)["']\)/g) || [];
const importMatches = content.match(/import\s+.*?\s+from\s+["']([^"']+)["']/g) || [];

const deps = new Set();

// Extract require dependencies
requireMatches.forEach(match => {
const dep = match.match(/require\(["']([^"']+)["']\)/)[1];
if (!dep.startsWith('.') && !dep.startsWith('/')) {
// Get package name (handle scoped packages)
const packageName = dep.startsWith('@')
? dep.split('/').slice(0, 2).join('/')
: dep.split('/')[0];
deps.add(packageName);
}
});

// Extract import dependencies
importMatches.forEach(match => {
const dep = match.match(/from\s+["']([^"']+)["']/)[1];
if (!dep.startsWith('.') && !dep.startsWith('/')) {
const packageName = dep.startsWith('@')
? dep.split('/').slice(0, 2).join('/')
: dep.split('/')[0];
deps.add(packageName);
}
});

return Array.from(deps);
} catch (error) {
return [];
}
}

// Function to scan directory recursively
function scanDirectory(dir, extensions = ['.js', '.ts', '.tsx', '.jsx']) {
const allDeps = new Set();

function scanRecursive(currentDir) {
try {
const items = fs.readdirSync(currentDir);

for (const item of items) {
const fullPath = path.join(currentDir, item);
const stat = fs.statSync(fullPath);

if (stat.isDirectory() && !item.startsWith('.') && item !== 'node_modules') {
scanRecursive(fullPath);
} else if (stat.isFile() && extensions.some(ext => item.endsWith(ext))) {
const deps = extractDependencies(fullPath);
deps.forEach(dep => allDeps.add(dep));
}
}
} catch (error) {
// Skip directories we can't read
}
}

scanRecursive(dir);
return Array.from(allDeps);
}

// Scan specific directories for dependencies
const directories = [
'node_modules/@lit-protocol',
'src'
];

const allFoundDeps = new Set();

directories.forEach(dir => {
if (fs.existsSync(dir)) {
console.log(`📁 Scanning ${dir}...`);
const deps = scanDirectory(dir);
deps.forEach(dep => allFoundDeps.add(dep));
}
});

// Filter out built-in Node.js modules and current dependencies
const builtInModules = new Set([
'fs', 'path', 'crypto', 'stream', 'buffer', 'util', 'events', 'os', 'url',
'http', 'https', 'querystring', 'zlib', 'worker_threads', 'fs/promises'
]);

const missingDeps = Array.from(allFoundDeps).filter(dep =>
!currentDeps[dep] &&
!builtInModules.has(dep) &&
dep !== 'react' && // Common false positives
dep !== 'node_modules'
);

console.log('\n📊 Results:');
console.log(`Total dependencies found: ${allFoundDeps.size}`);
console.log(`Missing dependencies: ${missingDeps.length}`);

if (missingDeps.length > 0) {
console.log('\n❌ Missing Dependencies:');
console.log('Add these to your package.json:\n');

const suggestions = {};

// Common version suggestions
const versionMap = {
'siwe': '^2.3.2',
'siwe-recap': '^0.0.2-alpha.0',
'jose': '^4.14.4',
'ethers': '5.7.2',
'viem': '^2.29.4',
'@noble/curves': '^1.2.0',
'@noble/hashes': '^1.3.0',
'base64url': '^3.0.1',
'cbor-web': '^9.0.2',
'elysia': '^1.2.25',
'tslib': '^2.3.0',
'zod-validation-error': '^3.4.0',
'@openagenda/verror': '^3.1.4',
'@simplewebauthn/browser': '^7.2.0'
};

missingDeps.forEach(dep => {
const version = versionMap[dep] || '^latest';
suggestions[dep] = version;
console.log(` "${dep}": "${version}",`);
});

console.log('\n💡 Or run this command to install them all:');
const installCmd = `pnpm add ${missingDeps.map(dep => `${dep}@${versionMap[dep] || 'latest'}`).join(' ')}`;
console.log(`\n${installCmd}\n`);

} else {
console.log('\n✅ All dependencies appear to be present!');
}

console.log('\n🔧 Current package.json dependencies:');
Object.keys(currentDeps).sort().forEach(dep => {
console.log(` ${dep}: ${currentDeps[dep]}`);
});
Loading
Loading