diff --git a/.env.local.example b/.env.local.example index efc9ea5..175bc91 100644 --- a/.env.local.example +++ b/.env.local.example @@ -32,54 +32,27 @@ ALCHEMY_API_KEY=your_alchemy_api_key # IMPORTANT: Set this in your .env.local for development! NEXT_PUBLIC_ENABLE_TESTNET_CHAINS=false -# EVM payment wallet address (same address used across all EVM chains) -# This is where users send their stablecoin payments on Ethereum, Base, Arbitrum, Optimism, Polygon +# EVM payment wallet address (used for Ethereum mainnet payments) +# This is where users send their stablecoin payments on Ethereum mainnet # NEXT_PUBLIC_ prefix is required because it's displayed in the browser UI # Format: 0x... (42 characters, EVM address) # Note: For future non-EVM chains (Solana, Monad, etc.), add separate variables like: # NEXT_PUBLIC_SOLANA_PAYMENT_WALLET_ADDRESS, NEXT_PUBLIC_MONAD_PAYMENT_WALLET_ADDRESS NEXT_PUBLIC_EVM_PAYMENT_WALLET_ADDRESS=0x0000000000000000000000000000000000000000 -# Stablecoin contract addresses (standard addresses, don't change unless deploying new token) +# Stablecoin contract addresses (standard addresses for Ethereum mainnet) -# USDC contracts +# USDC contract (Ethereum mainnet) USDC_CONTRACT_ETHEREUM=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 -USDC_CONTRACT_BASE=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 -USDC_CONTRACT_ARBITRUM=0xaf88d065e77c8cC2239327C5EDb3A432268e5831 -USDC_CONTRACT_OPTIMISM=0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85 -USDC_CONTRACT_POLYGON=0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 - NEXT_PUBLIC_USDC_CONTRACT_ETHEREUM=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 -NEXT_PUBLIC_USDC_CONTRACT_BASE=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 -NEXT_PUBLIC_USDC_CONTRACT_ARBITRUM=0xaf88d065e77c8cC2239327C5EDb3A432268e5831 -NEXT_PUBLIC_USDC_CONTRACT_OPTIMISM=0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85 -NEXT_PUBLIC_USDC_CONTRACT_POLYGON=0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 -# USDT contracts +# USDT contract (Ethereum mainnet) USDT_CONTRACT_ETHEREUM=0xdAC17F958D2ee523a2206206994597C13D831ec7 -USDT_CONTRACT_BASE=0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2 -USDT_CONTRACT_ARBITRUM=0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9 -USDT_CONTRACT_OPTIMISM=0x94b008aA00579c1307B0EF2c499aD98a8ce58e58 -USDT_CONTRACT_POLYGON=0xc2132D05D31c914a87C6611C10748AEb04B58e8F - NEXT_PUBLIC_USDT_CONTRACT_ETHEREUM=0xdAC17F958D2ee523a2206206994597C13D831ec7 -NEXT_PUBLIC_USDT_CONTRACT_BASE=0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2 -NEXT_PUBLIC_USDT_CONTRACT_ARBITRUM=0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9 -NEXT_PUBLIC_USDT_CONTRACT_OPTIMISM=0x94b008aA00579c1307B0EF2c499aD98a8ce58e58 -NEXT_PUBLIC_USDT_CONTRACT_POLYGON=0xc2132D05D31c914a87C6611C10748AEb04B58e8F -# DAI contracts +# DAI contract (Ethereum mainnet) DAI_CONTRACT_ETHEREUM=0x6B175474E89094C44Da98b954EedeAC495271d0F -DAI_CONTRACT_BASE=0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb -DAI_CONTRACT_ARBITRUM=0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1 -DAI_CONTRACT_OPTIMISM=0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1 -DAI_CONTRACT_POLYGON=0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063 - NEXT_PUBLIC_DAI_CONTRACT_ETHEREUM=0x6B175474E89094C44Da98b954EedeAC495271d0F -NEXT_PUBLIC_DAI_CONTRACT_BASE=0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb -NEXT_PUBLIC_DAI_CONTRACT_ARBITRUM=0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1 -NEXT_PUBLIC_DAI_CONTRACT_OPTIMISM=0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1 -NEXT_PUBLIC_DAI_CONTRACT_POLYGON=0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063 # Sepolia Testnet (DEV MODE ONLY - NODE_ENV=development) # Get testnet tokens from Sepolia faucets (see STABLECOIN_PAYMENTS.md) diff --git a/STABLECOIN_PAYMENTS.md b/STABLECOIN_PAYMENTS.md index fe04bc1..0077176 100644 --- a/STABLECOIN_PAYMENTS.md +++ b/STABLECOIN_PAYMENTS.md @@ -23,7 +23,7 @@ Monadic DNA Explorer Premium now uses a **database-free, stablecoin-based paymen │ ▼ ┌─────────────┐ -│ Blockchain │ Ethereum, Base, Arbitrum, Optimism, Polygon +│ Blockchain │ Ethereum mainnet │ Transaction │ Stablecoin transfer └──────┬──────┘ │ @@ -73,17 +73,13 @@ NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=your_dynamic_environment_id ### 2. Supported Chains -The system supports 5 EVM chains (production): -- **Ethereum** -- **Base** -- **Arbitrum** -- **Optimism** -- **Polygon** +The system supports **Ethereum mainnet** only (no L2 networks): +- **Ethereum** (L1) The system also supports **Sepolia testnet** when explicitly enabled: - **Sepolia** (Ethereum testnet) - Enable by setting `NEXT_PUBLIC_ENABLE_TESTNET_CHAINS=true` in `.env.local` -**USDC**, **USDT**, and **DAI** are accepted on all chains. +**USDC**, **USDT**, and **DAI** are accepted on Ethereum mainnet. ### 3. Pricing Model @@ -270,7 +266,6 @@ If you were using the old Paddle + Database system: Potential improvements: - [ ] Add email notifications when subscription expires - [ ] Add QR code for mobile wallet payments -- [ ] Support more chains (Polygon, Avalanche, etc.) - [ ] Add token price charts in UI - [ ] Add refund mechanism (for accidental overpayments) diff --git a/app/api/nilai-delegation/route.ts b/app/api/nilai-delegation/route.ts index 69a4830..d2f7c27 100644 --- a/app/api/nilai-delegation/route.ts +++ b/app/api/nilai-delegation/route.ts @@ -1,5 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; -import { DelegationTokenServer, NilAuthInstance } from '@nillion/nilai-ts'; +import { DelegationTokenServer } from '@nillion/nilai-ts'; import { validateOrigin } from '@/lib/origin-validator'; // Simple in-memory rate limiter @@ -97,7 +97,6 @@ export async function POST(request: NextRequest) { // Create delegation token server const server = new DelegationTokenServer(apiKey, { - nilauthInstance: NilAuthInstance.PRODUCTION, expirationTime: 600, // 10 minutes validity tokenMaxUses: 1 // Single use for privacy (fresh token fetched per message) }); diff --git a/app/api/nildb-delegation/route.ts b/app/api/nildb-delegation/route.ts deleted file mode 100644 index 039cc16..0000000 --- a/app/api/nildb-delegation/route.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import {Signer, NilauthClient, Builder as NucTokenBuilder, Did, Builder, Command} from '@nillion/nuc'; -import {NucCmd, SecretVaultBuilderClient } from '@nillion/secretvaults'; -import { validateOrigin } from '@/lib/origin-validator'; -import { NILDB_CONFIG } from '@/lib/nildb-config'; - -// Simple in-memory rate limiter -const rateLimitMap = new Map(); -const RATE_LIMIT_WINDOW_MS = 60 * 1000; // 1 minute -const MAX_REQUESTS_PER_WINDOW = 5; // 5 tokens per minute per IP - -function getRateLimitKey(request: NextRequest): string { - // Try to get real IP from common headers (for proxies/load balancers) - const forwarded = request.headers.get('x-forwarded-for'); - const realIp = request.headers.get('x-real-ip'); - const ip = forwarded?.split(',')[0] || realIp || 'unknown'; - return ip; -} - -function isRateLimited(key: string): boolean { - const now = Date.now(); - const record = rateLimitMap.get(key); - - if (!record || now > record.resetTime) { - // New window - rateLimitMap.set(key, { count: 1, resetTime: now + RATE_LIMIT_WINDOW_MS }); - return false; - } - - if (record.count >= MAX_REQUESTS_PER_WINDOW) { - return true; - } - - record.count++; - return false; -} - -export async function POST(request: NextRequest) { - try { - // Validate origin - const originError = validateOrigin(request); - if (originError) return originError; - - // Rate limiting check - const rateLimitKey = getRateLimitKey(request); - if (isRateLimited(rateLimitKey)) { - console.warn(`Rate limit exceeded for IP: ${rateLimitKey}`); - return NextResponse.json( - { error: 'Too many requests. Please try again later.' }, - { status: 429 } - ); - } - - const { userDid } = await request.json(); - - if (!userDid) { - return NextResponse.json( - { error: 'User DID is required' }, - { status: 400 } - ); - } - - // Check for Nillion API key - const apiKey = process.env.NILLION_API_KEY; - if (!apiKey) { - return NextResponse.json( - { error: 'Nillion API key not configured. Please set NILLION_API_KEY in your environment variables.' }, - { status: 503 } - ); - } - - console.log('Creating nilDB delegation token for user:', userDid); - - // Create signer from API key - const builderSigner = await Signer.fromPrivateKey(apiKey, "nil"); - const builderDid = await builderSigner.getDid(); - - // Create Nilauth client manually using the provided public key - // (avoiding network call to /about endpoint) - // @ts-expect-error - Constructor is private but works fine at runtime; waiting for Nillion API fix - const nilauthClient = new NilauthClient({ - payer: builderSigner, - nilauth: { - baseUrl: NILDB_CONFIG.nilauthUrl, - publicKey: NILDB_CONFIG.nilauthPublicKey, - did: Did.fromPublicKey(NILDB_CONFIG.nilauthPublicKey, 'key') - } - }); - - // Create builder client - console.log('Creating SecretVaultBuilderClient with', NILDB_CONFIG.nodes.length, 'nodes'); - const builderClient = await SecretVaultBuilderClient.from({ - signer: builderSigner, - nilauthClient, - dbs: [...NILDB_CONFIG.nodes] - }); - console.log('SecretVaultBuilderClient created successfully'); - - // Refresh authentication to get root token - console.log('Refreshing root token from nilauth...'); - try { - await builderClient.refreshRootToken(); - console.log('Root token refreshed successfully'); - } catch (err) { - console.error('Failed to refresh root token:', err); - throw new Error(`Failed to refresh root token: ${err instanceof Error ? err.message : String(err)}`); - } - - // Get the root token - const rootToken = builderClient.rootToken; - if (!rootToken) { - throw new Error('No root token available from builderClient'); - } - - // Create delegation token from builderClient to user - // Token expires in 10 minutes - enough time for user to submit data - const expiresInSeconds = 10 * 60; - const expiresAt = Math.floor(Date.now() / 1000) + expiresInSeconds; - - /*// Create delegation token from builderClient to user - // Don't specify command - let the user client add it - // Note: Using signer.privateKey() since builderClient.keypair is not exposed in current API - const delegationToken = new NucTokenBuilder() - .extending(rootToken) - .audience(userDid) - .expiresAt(expiresAt) - .build(signer.privateKey());*/ - - const delegationToken = await Builder.delegationFrom(builderClient.rootToken) - .command(NucCmd.nil.db.data.create as Command) - .audience(userDid) - .expiresIn(3600) - .signAndSerialize(builderSigner); - - console.log('nilDB delegation token created successfully'); - - return NextResponse.json({ - success: true, - delegationToken, - collectionId: NILDB_CONFIG.collectionId, - builderDid: builderDid.didString - }); - - } catch (error) { - console.error('Error creating nilDB delegation token:', error); - return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to create delegation token' }, - { status: 500 } - ); - } -} diff --git a/app/api/study-metadata/route.ts b/app/api/study-metadata/route.ts index c7e21b8..80fddb5 100644 --- a/app/api/study-metadata/route.ts +++ b/app/api/study-metadata/route.ts @@ -30,7 +30,7 @@ export async function GET(request: NextRequest) { date, journal FROM gwas_catalog - WHERE id = ? + WHERE id = $1 `; const metadata = await executeQuerySingle<{ diff --git a/app/components/MenuBar.tsx b/app/components/MenuBar.tsx index 101b427..4925add 100644 --- a/app/components/MenuBar.tsx +++ b/app/components/MenuBar.tsx @@ -288,17 +288,6 @@ export default function MenuBar() { Help - - {mounted && (