cdp-wallet-onramp-kit
Version:
Complete toolkit for Coinbase Developer Platform (CDP) Embedded Wallets and Onramp integration with reusable components, utilities, and documentation
199 lines (198 loc) • 6.68 kB
JavaScript
;
// Dynamic import to avoid build-time CDP SDK type checking
Object.defineProperty(exports, "__esModule", { value: true });
exports.SUPPORTED_ASSETS = exports.SUPPORTED_NETWORKS = exports.GUEST_CHECKOUT_LIMITS = void 0;
exports.generateJWT = generateJWT;
exports.createSessionToken = createSessionToken;
exports.generateOnrampUrl = generateOnrampUrl;
exports.isValidEthereumAddress = isValidEthereumAddress;
exports.validateGuestCheckoutAmount = validateGuestCheckoutAmount;
/**
* Generate a JSON Web Token (JWT) for CDP API authentication.
* Uses the CDP SDK to handle all key formats automatically.
*
* @param config - JWT configuration options
* @returns Promise<string> - The generated JWT token
*
* @example
* ```typescript
* const jwt = await generateJWT({
* method: 'POST',
* path: '/onramp/v1/token'
* });
* ```
*/
async function generateJWT(config = {}) {
const privateKeyString = process.env.CDP_API_KEY_PRIVATE_KEY || process.env.CDP_SECRET_KEY;
const keyName = process.env.CDP_API_KEY_NAME || process.env.CDP_SECRET_KEY_NAME;
if (!privateKeyString || !keyName) {
throw new Error('CDP API credentials are required. Set CDP_API_KEY_PRIVATE_KEY and CDP_API_KEY_NAME environment variables.');
}
const { method = 'POST', host = 'api.developer.coinbase.com', path = '/onramp/v1/token', expiresIn = 120 } = config;
try {
// Use eval to avoid TypeScript trying to resolve the module during build time
const cdpSdkAuth = await eval('import("@coinbase/cdp-sdk/auth")');
// Use CDP SDK to generate JWT - handles all key formats correctly
const jwt = await cdpSdkAuth.generateJwt({
apiKeyId: keyName,
apiKeySecret: privateKeyString,
requestMethod: method,
requestHost: host,
requestPath: path,
expiresIn
});
return jwt;
}
catch (error) {
console.error('JWT generation failed:', error);
console.error('Key format detected:', privateKeyString.startsWith('-----BEGIN') ? 'PEM' : 'Base64');
throw new Error('Failed to generate JWT. Make sure you have @coinbase/cdp-sdk installed:\n' +
'npm install @coinbase/cdp-sdk\n\n' +
'Original error: ' + (error instanceof Error ? error.message : 'Unknown error'));
}
}
/**
* Create a session token for initializing the onramp widget.
* Session tokens are single-use and expire after 5 minutes.
*
* @param jwt - The JWT token for authentication
* @param params - Session token parameters
* @returns Promise<string> - The session token
*
* @example
* ```typescript
* const jwt = await generateJWT();
* const sessionToken = await createSessionToken(jwt, {
* addresses: [{
* address: '0x1234...5678',
* blockchains: ['base', 'ethereum']
* }],
* assets: ['USDC', 'ETH']
* });
* ```
*/
async function createSessionToken(jwt, params) {
const response = await fetch('https://api.developer.coinbase.com/onramp/v1/token', {
method: 'POST',
headers: {
'Authorization': `Bearer ${jwt}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
if (!response.ok) {
const errorText = await response.text();
let errorMessage = `Failed to create session token: ${response.status}`;
try {
const errorData = JSON.parse(errorText);
errorMessage = errorData.message || errorData.error || errorMessage;
}
catch {
errorMessage += ` - ${errorText}`;
}
throw new Error(errorMessage);
}
const data = await response.json();
return data.token || data.data?.token;
}
/**
* Generate a complete onramp URL with session token and parameters.
* This is a convenience function that combines JWT generation, session token creation, and URL building.
*
* @param params - Onramp URL parameters
* @returns Promise<string> - The complete onramp URL
*
* @example
* ```typescript
* const onrampUrl = await generateOnrampUrl({
* addresses: [{
* address: '0x1234...5678',
* blockchains: ['base']
* }],
* defaultAsset: 'USDC',
* presetFiatAmount: 50
* });
* ```
*/
async function generateOnrampUrl(params) {
// Generate JWT and session token
const jwt = await generateJWT();
const sessionToken = await createSessionToken(jwt, {
addresses: params.addresses,
assets: params.assets
});
// Build URL with session token and optional parameters
const url = new URL('https://pay.coinbase.com/buy/select-asset');
url.searchParams.set('sessionToken', sessionToken);
if (params.defaultAsset) {
url.searchParams.set('defaultAsset', params.defaultAsset);
}
if (params.defaultNetwork) {
url.searchParams.set('defaultNetwork', params.defaultNetwork);
}
if (params.presetFiatAmount) {
url.searchParams.set('presetFiatAmount', params.presetFiatAmount.toString());
}
if (params.presetCryptoAmount) {
url.searchParams.set('presetCryptoAmount', params.presetCryptoAmount.toString());
}
if (params.redirectUrl) {
url.searchParams.set('redirectUrl', params.redirectUrl);
}
if (params.partnerUserId) {
url.searchParams.set('partnerUserId', params.partnerUserId);
}
return url.toString();
}
/**
* Guest checkout limits as defined by Coinbase
*/
exports.GUEST_CHECKOUT_LIMITS = {
minimumAmount: 5, // $5 minimum
weeklyLimit: 500, // $500 weekly limit
country: 'US' // US residents only
};
/**
* Supported blockchain networks
*/
exports.SUPPORTED_NETWORKS = {
BASE: 'base',
ETHEREUM: 'ethereum',
ARBITRUM: 'arbitrum',
OPTIMISM: 'optimism',
POLYGON: 'polygon'
};
/**
* Common asset symbols
*/
exports.SUPPORTED_ASSETS = {
ETHEREUM: 'ETH',
USD_COIN: 'USDC',
BITCOIN: 'BTC',
TETHER: 'USDT',
DAI: 'DAI'
};
/**
* Validate wallet address format
*/
function isValidEthereumAddress(address) {
return /^0x[a-fA-F0-9]{40}$/.test(address);
}
/**
* Validate amount within guest checkout limits
*/
function validateGuestCheckoutAmount(amount) {
if (amount < exports.GUEST_CHECKOUT_LIMITS.minimumAmount) {
return {
isValid: false,
error: `Amount must be at least $${exports.GUEST_CHECKOUT_LIMITS.minimumAmount}`
};
}
if (amount > exports.GUEST_CHECKOUT_LIMITS.weeklyLimit) {
return {
isValid: false,
error: `Amount cannot exceed $${exports.GUEST_CHECKOUT_LIMITS.weeklyLimit} (weekly limit)`
};
}
return { isValid: true };
}