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
JavaScript
"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`));
}