# 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()
```