UNPKG

cdp-wallet-onramp-kit

Version:

Complete toolkit for Coinbase Developer Platform (CDP) Embedded Wallets and Onramp integration with reusable components, utilities, and documentation

474 lines (416 loc) 18.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.installAll = installAll; const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const chalk_1 = __importDefault(require("chalk")); const child_process_1 = require("child_process"); function detectProjectStructure(baseDir) { const hasSrcDir = fs_extra_1.default.existsSync(path_1.default.join(baseDir, 'src')); const hasAppDir = fs_extra_1.default.existsSync(path_1.default.join(baseDir, 'app')); const hasSrcApp = fs_extra_1.default.existsSync(path_1.default.join(baseDir, 'src/app')); let structure; let appPath; if (hasSrcApp) { structure = 'src/app'; appPath = 'src/app'; } else if (hasAppDir) { structure = 'app'; appPath = 'app'; } else { // Default to app structure for new projects structure = 'app'; appPath = 'app'; } return { hasSrcDir, hasAppDir, appPath, structure }; } async function installAll(options) { const { dir, force } = options; const targetDir = path_1.default.resolve(process.cwd(), dir); console.log(chalk_1.default.blue(`🚀 Installing complete CDP Wallet & Onramp integration in ${targetDir}`)); // Detect project structure const projectStructure = detectProjectStructure(targetDir); console.log(chalk_1.default.cyan(`📁 Detected project structure: ${projectStructure.structure}`)); // Ensure target directory exists await fs_extra_1.default.ensureDir(targetDir); // In published package, templates are in src/templates, not dist/templates const templateDir = path_1.default.join(__dirname, '..', '..', 'src', 'templates'); // Step 1: Set up documentation console.log(chalk_1.default.cyan('📚 Setting up documentation...')); const docsSource = path_1.default.join(templateDir, 'docs'); const docsTarget = path_1.default.join(targetDir, 'doc', 'cdp'); if (await fs_extra_1.default.pathExists(docsTarget) && !force) { console.log(chalk_1.default.yellow('⚠️ Documentation already exists. Use --force to overwrite.')); } else { await fs_extra_1.default.copy(docsSource, docsTarget); } // Copy integration prompts const walletPromptSource = path_1.default.join(templateDir, 'embedded-wallet-integration-prompt.md'); const onrampPromptSource = path_1.default.join(templateDir, 'onramp-integration-prompt.md'); if (await fs_extra_1.default.pathExists(walletPromptSource)) { await fs_extra_1.default.copy(walletPromptSource, path_1.default.join(targetDir, 'doc', 'embedded-wallet-integration-prompt.md')); } if (await fs_extra_1.default.pathExists(onrampPromptSource)) { await fs_extra_1.default.copy(onrampPromptSource, path_1.default.join(targetDir, 'doc', 'onramp-integration-prompt.md')); } // Create documentation README const combinedGuide = `# CDP Wallet & Onramp Integration Guide This directory contains comprehensive documentation for integrating both Coinbase Developer Platform (CDP) Embedded Wallets and Onramp functionality into your Next.js application. ## Quick Start 1. **For AI Coding Agents**: Use the integration prompts: - \`embedded-wallet-integration-prompt.md\` - Complete wallet integration guide - \`onramp-integration-prompt.md\` - Complete onramp integration guide 2. **For Developers**: Browse the documentation: - \`cdp/embedded-wallet/\` - Embedded wallet docs and examples - \`cdp/onramp/\` - Onramp docs and examples ## Documentation Structure - **Embedded Wallets**: \`embedded-wallet/\` directory with wallet documentation - **Onramp API**: \`onramp-api/\` directory with high-level guides - **REST API**: \`onramp-offramp-rest-api/\` directory with complete API documentation ## Available Components The package provides pre-built components: - \`CDPProvider\` - Provider wrapper for your app - \`WalletAuth\` - Pre-built wallet authentication - \`CustomWalletAuth\` - Custom authentication with full control - \`OnrampButton\` - Simple buy button - \`OnrampWidget\` - Complete purchase widget ### Utilities Included: - CDP authentication helpers - Validation schemas - API route templates ## Environment Setup Add to your \`.env.local\`: \`\`\`bash # Required for both wallet and onramp NEXT_PUBLIC_CDP_PROJECT_ID=your-project-id # Required for onramp (server-side only) CDP_API_KEY_PRIVATE_KEY=your-private-key CDP_API_KEY_NAME=your-key-name \`\`\` ## Next Steps 1. Configure CORS in CDP Portal: https://portal.cdp.coinbase.com 2. Choose your integration approach (see docs for details) 3. Start building with the provided components! Generated by cdp-wallet-onramp-kit v1.0.1 `; await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'doc', 'README.md'), combinedGuide); // Step 2: Create API routes console.log(chalk_1.default.cyan('🔌 Setting up API routes...')); const apiDir = path_1.default.join(targetDir, projectStructure.appPath, 'api', 'onramp'); // Session token API route const sessionRouteContent = `import { NextRequest, NextResponse } from 'next/server'; import { generateJWT, createSessionToken } from 'cdp-wallet-onramp-kit/lib/cdp-auth'; import { sessionTokenSchema, validateRequest, createErrorResponse, validateEnvironment } from 'cdp-wallet-onramp-kit/lib/validation'; /** * Generate a session token for CDP Onramp * POST /api/onramp/session */ export async function POST(request: NextRequest) { try { // Validate environment variables const envValidation = validateEnvironment(); if (!envValidation.isValid) { return createErrorResponse('Environment configuration error', 500, envValidation.errors); } // Parse and validate request body const body = await request.json(); const validatedData = validateRequest(sessionTokenSchema, body); // Generate CDP JWT const jwt = await generateJWT(); // Create session token with CDP API const requestBody = 'guestCheckout' in validatedData && validatedData.guestCheckout ? { // Guest checkout doesn't require specific addresses addresses: [], assets: validatedData.assets || ['ETH', 'USDC'] } : { addresses: [ { address: (validatedData as { userAddress: string }).userAddress, blockchains: ['base'] } ], assets: validatedData.assets || ['ETH', 'USDC'] }; const sessionToken = await createSessionToken(jwt, requestBody); return NextResponse.json({ sessionToken, success: true }); } catch (error: unknown) { console.error('Session token generation error:', error); const errorMessage = error instanceof Error ? error.message : 'Unknown server error'; const contextualMessage = \`Failed to generate session token: \${errorMessage}. Check your CDP credentials and ensure NEXT_PUBLIC_CDP_PROJECT_ID, CDP_API_KEY_NAME, and CDP_API_KEY_PRIVATE_KEY are set correctly.\`; return createErrorResponse( contextualMessage, 500, [ 'Verify environment variables in .env.local', 'Check CDP Portal for valid API credentials', 'Ensure project ID matches your CDP project' ] ); } }`; await fs_extra_1.default.ensureDir(path_1.default.join(apiDir, 'session')); await fs_extra_1.default.writeFile(path_1.default.join(apiDir, 'session', 'route.ts'), sessionRouteContent); // Onramp URL API route const urlRouteContent = `import { NextRequest, NextResponse } from 'next/server'; import { generateOnrampUrl } from 'cdp-wallet-onramp-kit/lib/cdp-auth'; import { onrampUrlSchema, validateRequest, createErrorResponse, validateEnvironment } from 'cdp-wallet-onramp-kit/lib/validation'; /** * Generate an onramp URL * POST /api/onramp/url */ export async function POST(request: NextRequest) { try { // Validate environment variables const envValidation = validateEnvironment(); if (!envValidation.isValid) { return createErrorResponse('Environment configuration error', 500, envValidation.errors); } // Parse and validate request body const body = await request.json(); const validatedData = validateRequest(onrampUrlSchema, body); // Generate onramp URL const url = await generateOnrampUrl({ destinationWallets: validatedData.destinationWallets?.map(wallet => ({ address: wallet.address, blockchains: wallet.blockchains || ['base'] })) || [], quoteId: validatedData.quoteId, defaultAsset: validatedData.defaultAsset || 'ETH', defaultNetwork: validatedData.defaultNetwork || 'base', defaultPaymentMethod: validatedData.defaultPaymentMethod || 'card', presetFiatAmount: validatedData.presetFiatAmount, fiatCurrency: validatedData.fiatCurrency || 'USD' }); return NextResponse.json({ url, success: true }); } catch (error: unknown) { console.error('Onramp URL generation error:', error); const errorMessage = error instanceof Error ? error.message : 'Unknown server error'; const contextualMessage = \`Failed to generate onramp URL: \${errorMessage}. This usually indicates issues with request parameters or CDP configuration.\`; return createErrorResponse( contextualMessage, 500, [ 'Verify destinationWallets addresses are valid Ethereum addresses', 'Check that defaultAsset and defaultNetwork are supported', 'Ensure CDP credentials are valid and project has onramp enabled', 'Run: npx cdp-wallet-onramp-kit validate' ] ); } }`; await fs_extra_1.default.ensureDir(path_1.default.join(apiDir, 'url')); await fs_extra_1.default.writeFile(path_1.default.join(apiDir, 'url', 'route.ts'), urlRouteContent); // Step 3: Copy ONLY source .tsx files to components directory console.log(chalk_1.default.cyan('⚛️ Copying all components...')); const srcPrefix = projectStructure.hasSrcDir ? 'src' : ''; const targetComponentsDir = path_1.default.join(targetDir, srcPrefix, 'components'); await fs_extra_1.default.ensureDir(targetComponentsDir); // Copy each component source file individually (only .tsx files) // In published package, components are in src/components, not dist/components const sourceComponentsDir = path_1.default.join(__dirname, '..', '..', 'src', 'components'); const componentFiles = [ 'CDPHooksProvider.tsx', 'CDPProvider.tsx', 'CustomWalletAuth.tsx', 'OnrampButton.tsx', 'OnrampWidget.tsx', 'WalletAuth.tsx', 'index.ts' ]; for (const file of componentFiles) { const sourcePath = path_1.default.join(sourceComponentsDir, file); const targetPath = path_1.default.join(targetComponentsDir, file); if (await fs_extra_1.default.pathExists(sourcePath)) { await fs_extra_1.default.copy(sourcePath, targetPath); } else { console.log(chalk_1.default.yellow(`⚠️ Component file not found: ${sourcePath}`)); } } // Copy lib source files (only .ts files) console.log(chalk_1.default.cyan('📚 Copying library utilities...')); const targetLibDir = path_1.default.join(targetDir, srcPrefix, 'lib'); await fs_extra_1.default.ensureDir(targetLibDir); // In published package, lib files are in src/lib, not dist/lib const sourceLibDir = path_1.default.join(__dirname, '..', '..', 'src', 'lib'); const libFiles = [ 'cdp-auth.ts', 'validation.ts', 'environment-validation.ts', 'index.ts' ]; for (const file of libFiles) { const sourcePath = path_1.default.join(sourceLibDir, file); const targetPath = path_1.default.join(targetLibDir, file); if (await fs_extra_1.default.pathExists(sourcePath)) { await fs_extra_1.default.copy(sourcePath, targetPath); } else { console.log(chalk_1.default.yellow(`⚠️ Lib file not found: ${sourcePath}`)); } } console.log(chalk_1.default.cyan('📝 Creating example component...')); const exampleDir = path_1.default.join(targetComponentsDir, 'cdp'); await fs_extra_1.default.ensureDir(exampleDir); const exampleComponentContent = `'use client'; import { CDPProvider, WalletAuth, OnrampWidget } from '../'; export default function CDPExample() { return ( <CDPProvider> <div className="max-w-2xl mx-auto p-6 space-y-6"> <h1 className="text-2xl font-bold">CDP Wallet & Onramp Integration</h1> {/* Wallet Authentication */} <div className="border rounded-lg p-4"> <h2 className="text-lg font-semibold mb-4">Wallet Authentication</h2> <WalletAuth /> </div> {/* Onramp Widget */} <div className="border rounded-lg p-4"> <h2 className="text-lg font-semibold mb-4">Buy Crypto</h2> <OnrampWidget defaultAsset="ETH" defaultNetwork="base" presetFiatAmount={100} /> </div> </div> </CDPProvider> ); }`; await fs_extra_1.default.writeFile(path_1.default.join(exampleDir, 'CDPExample.tsx'), exampleComponentContent); // Step 4: Update or create package.json with dependencies console.log(chalk_1.default.cyan('📦 Installing dependencies...')); const packageJsonPath = path_1.default.join(targetDir, 'package.json'); let packageJson; if (await fs_extra_1.default.pathExists(packageJsonPath)) { packageJson = await fs_extra_1.default.readJson(packageJsonPath); } else { packageJson = { name: 'cdp-integration', version: '1.0.0', private: true, scripts: { dev: 'next dev', build: 'next build', start: 'next start', lint: 'next lint' } }; } // Add CDP dependencies packageJson.dependencies = { ...packageJson.dependencies, 'cdp-wallet-onramp-kit': '^1.0.1', '@coinbase/cdp-core': '^0.0.18', '@coinbase/cdp-hooks': '^0.0.18', '@coinbase/cdp-react': '^0.0.18', '@coinbase/cdp-sdk': '^1.33.0', 'next': packageJson.dependencies?.next || '^14.0.0', 'react': packageJson.dependencies?.react || '^18.0.0', 'react-dom': packageJson.dependencies?.['react-dom'] || '^18.0.0', 'lucide-react': packageJson.dependencies?.['lucide-react'] || '^0.263.0', 'framer-motion': packageJson.dependencies?.['framer-motion'] || '^12.0.0' }; await fs_extra_1.default.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)); // Step 5: Create setup instructions console.log(chalk_1.default.cyan('📋 Creating setup instructions...')); const setupInstructions = `# CDP Wallet & Onramp Setup Complete! 🎉 Your Next.js application has been configured with: ## ✅ What's Been Added ### 📚 Documentation - \`doc/\` - Complete integration guides and API documentation - \`doc/embedded-wallet-integration-prompt.md\` - AI-friendly wallet integration guide - \`doc/onramp-integration-prompt.md\` - AI-friendly onramp integration guide ### 🔌 API Routes (Next.js App Router) - \`app/api/onramp/session/route.ts\` - Session token generation - \`app/api/onramp/url/route.ts\` - Onramp URL generation ### ⚛️ Components - \`components/cdp/CDPExample.tsx\` - Complete working example ### 📦 Dependencies All required CDP packages have been added to package.json ## 🚀 Next Steps ### 1. Install Dependencies \`\`\`bash npm install \`\`\` ### 2. Environment Setup Create \`.env.local\` with your CDP credentials: \`\`\`bash # Required for both wallet and onramp NEXT_PUBLIC_CDP_PROJECT_ID=your-project-id # Required for onramp (server-side only) CDP_API_KEY_PRIVATE_KEY=your-private-key CDP_API_KEY_NAME=your-key-name \`\`\` ### 3. Configure CORS Visit [CDP Portal](https://portal.cdp.coinbase.com) and add your domain to CORS settings. ### 4. Start Development \`\`\`bash npm run dev \`\`\` ### 5. Use the Example Component Add to any page: \`\`\`tsx import CDPExample from '@/components/cdp/CDPExample'; export default function Page() { return <CDPExample />; } \`\`\` ## 🔧 Available Components Import from \`cdp-wallet-onramp-kit\`: - \`CDPProvider\` - Wrap your app - \`WalletAuth\` - Pre-built wallet auth - \`CustomWalletAuth\` - Customizable auth - \`OnrampButton\` - Simple buy button - \`OnrampWidget\` - Complete purchase widget ## 📖 Documentation Check \`doc/README.md\` for comprehensive guides and examples. --- Generated by cdp-wallet-onramp-kit v1.0.1 `; await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'CDP_SETUP.md'), setupInstructions); // Step 6: Install npm dependencies if in a proper npm project const isNpmProject = await fs_extra_1.default.pathExists(packageJsonPath); if (isNpmProject && targetDir !== process.cwd()) { try { console.log(chalk_1.default.cyan('⬇️ Installing npm dependencies...')); (0, child_process_1.execSync)('npm install', { cwd: targetDir, stdio: 'inherit' }); console.log(chalk_1.default.green('✅ Dependencies installed successfully!')); } catch (error) { console.log(chalk_1.default.yellow('⚠️ Could not auto-install dependencies. Run "npm install" manually.')); } } console.log(chalk_1.default.green(`✅ Complete CDP integration setup finished!`)); console.log(chalk_1.default.blue(`📖 Read ${path_1.default.join(targetDir, 'CDP_SETUP.md')} for next steps`)); console.log(chalk_1.default.blue(`🚀 Your API routes are ready at /api/onramp/session and /api/onramp/url`)); console.log(chalk_1.default.blue(`⚛️ Example component available at ${srcPrefix ? srcPrefix + '/' : ''}components/cdp/CDPExample.tsx`)); }