metal-presale
Version:
SDK for Metal Presales - Client and Server utilities
246 lines (189 loc) • 6.08 kB
Markdown
# Metal Presales SDK
A secure, type-safe SDK for interacting with Metal L2 presales and tokens.
## Installation
```bash
npm install metal-presale
```
## Quick Start
### Client-Side Usage
For browser/client-side applications:
```typescript
import { MetalPresalesClient } from 'metal-presale/client'
const metal = new MetalPresalesClient({
publicApiKey: process.env.NEXT_PUBLIC_METAL_PUBLIC_KEY!,
apiBasePath: '/api/metal' // Your API route base path
})
// Public operations (no server required)
const holder = await metal.getHolder('user123')
const portfolio = await metal.getHolderWithPortfolio('user123')
const presale = await metal.getPresale('presale-id')
// Server-proxied operations (requires API routes)
await metal.createUser('user123')
await metal.buyPresale('user123', 'presale-id', 100)
await metal.buyTokens('user123', '0x...', 50)
```
### Server-Side Usage
For server-side applications and API routes:
```typescript
import { MetalPresalesServer } from 'metal-presale/server'
const metal = new MetalPresalesServer({
publicApiKey: process.env.NEXT_PUBLIC_METAL_PUBLIC_KEY!,
secretApiKey: process.env.METAL_SECRET_KEY!
})
// All operations available
const holder = await metal.getOrCreateHolder('user123')
const presales = await metal.getActivePresales()
await metal.buyPresale('user123', { presaleId: 'abc', usdcAmount: 100 })
```
### Next.js API Routes (Recommended)
The easiest way to set up all API routes in Next.js:
1. Create `app/api/metal/[...action]/route.ts`:
```typescript
export { GET, POST } from 'metal-presale/next'
```
That's it! This automatically creates all the required endpoints:
- `POST /api/metal/users/create`
- `GET /api/metal/presales/active`
- `POST /api/metal/presales/buy`
- `POST /api/metal/tokens/buy`
- `POST /api/metal/tokens/sell`
- `POST /api/metal/tokens/quote-buy`
- `POST /api/metal/tokens/quote-sell`
### Environment Variables
```env
# Required for both client and server
NEXT_PUBLIC_METAL_PUBLIC_KEY=your-public-key
# Required for server-side only
METAL_SECRET_KEY=your-secret-key
```
## Core Features
### 🎯 Portfolio Management
Get holder data with calculated portfolio values:
```typescript
const { holder, portfolio } = await metal.getHolderWithPortfolio(userId)
console.log(portfolio.buyingPower) // USDC balance
console.log(portfolio.holdings) // Value of non-USDC tokens
console.log(portfolio.vaulted) // Value of locked tokens
console.log(portfolio.totalValue) // Total portfolio value
```
### 🚀 Presale Operations
```typescript
// Get only active presales
const activePresales = await metal.getActivePresales()
// Get presale with utility functions
import { getPresaleProgress, getPresaleTimeRemaining, formatTimeRemaining } from 'metal-presale'
const presale = await metal.getPresale(presaleId)
const progress = getPresaleProgress(presale) // Returns 0-100
const timeLeft = getPresaleTimeRemaining(presale) // Returns milliseconds
const timeString = formatTimeRemaining(timeLeft) // "2 days remaining"
```
### 💰 Token Operations
```typescript
// Get quotes before transactions
const buyQuote = await metal.quoteBuyTokens(userId, {
tokenAddress: '0x...',
usdcAmount: 100
})
const sellQuote = await metal.quoteSellTokens(userId, {
tokenAddress: '0x...',
tokenAmount: 50
})
// Execute transactions
const buyResult = await metal.buyTokens(userId, {
tokenAddress: '0x...',
usdcAmount: 100
})
const sellResult = await metal.sellTokens(userId, {
tokenAddress: '0x...',
tokenAmount: 50
})
```
## Utility Functions
```typescript
import {
calculatePortfolioSummary,
isPresaleActive,
getPresaleProgress,
getPresaleTimeRemaining,
formatTimeRemaining,
filterTokensByType,
calculateTotalValue,
findToken,
sortTokensByValue,
getUnlockedTokens
} from 'metal-presale'
// Use these utilities for common operations
const presaleTokens = filterTokensByType(holder.tokens, 'presale')
const totalPresaleValue = calculateTotalValue(presaleTokens)
const usdcToken = findToken(holder.tokens, 'USDC')
const sortedTokens = sortTokensByValue(holder.tokens)
```
## TypeScript Support
The SDK is fully typed. All types are exported:
```typescript
import type {
HolderResponse,
PortfolioSummary,
PresaleResponse,
TokenLikeItem,
BuyPresaleParams,
BuyTokensParams,
SellTokensParams,
QuoteBuyTokensParams,
QuoteSellTokensParams
} from 'metal-presale/client'
```
## Security Best Practices
1. **Never expose your secret key**: The secret key should only be used server-side
2. **Use the client SDK in browsers**: It only includes public operations and API calls
3. **Set up API routes**: Use the built-in Next.js handlers for secure server operations
4. **Validate inputs**: Always validate user inputs in your API routes
## Advanced Usage
### Custom API Configuration
```typescript
const metal = new MetalPresalesClient({
publicApiKey: 'your-key',
apiBasePath: '/custom/api/path' // Default: '/api/metal'
})
```
### Error Handling
```typescript
try {
await metal.buyPresale('user123', 'presale-id', 100)
} catch (error) {
if (error.message.includes('Insufficient balance')) {
// Handle insufficient funds
}
// Handle other errors
}
```
## Migration Guide
### From Direct API Calls
**Before:**
```typescript
// Manual portfolio calculations
const buyingPower = holder.tokens?.find(t => t.symbol === 'USDC')?.balance || 0
const holdings = holder.tokens
?.filter(t => t.symbol !== 'USDC')
?.reduce((sum, token) => sum + token.value, 0) || 0
```
**After:**
```typescript
// Automatic portfolio calculations
const { portfolio } = await metal.getHolderWithPortfolio(userId)
// portfolio.buyingPower, portfolio.holdings already calculated
```
### From Manual Presale Filtering
**Before:**
```typescript
// Manual presale filtering
const presales = await fetch('/api/presales/list')
const activePresales = presales.filter(p => {
// Complex logic to determine if active
})
```
**After:**
```typescript
// Built-in active presale filtering
const activePresales = await metal.getActivePresales()
```