npm package: @fenelabs/fene-sdk
Description: TypeScript SDK for Fene API
Author: Fene Labs
Latest version: 0.8.0

# @resonance/sdk

TypeScript SDK for the Resonance API.

## Installation

```bash
pnpm add @resonance/sdk
```

## Quick Start

```typescript
import { createResonanceClient } from "@resonance/sdk";

const client = createResonanceClient({
  baseUrl: "https://api.fene.network",
});

// Get network stats
const stats = await client.getNetworkStats();
console.log(`Total validators: ${stats.total_validators}`);

// Get APR
const apr = await client.getNetworkAPR();
console.log(`Network APR: ${apr.network_apr}%`);
```

## Authentication

```typescript
import { createResonanceClient } from "@resonance/sdk";
import { signMessage } from "viem/wallet";

const client = createResonanceClient({
  baseUrl: "https://api.fene.network",
  onTokenExpired: () => {
    console.log("Token expired, please re-authenticate");
  },
});

// 1. Get nonce
const { nonce, message } = await client.getNonce("0x...");

// 2. Sign message (using viem or wagmi)
const signature = await signMessage({ message });

// 3. Verify signature (API sets secure httpOnly cookies)
const auth = await client.verify({
  address: "0x...",
  signature,
  nonce,
});
console.log(`Session expires at: ${auth.expires_at}`);

// 4. Optional: fetch current session metadata
const session = await client.getSession();
console.log(`Logged in as: ${session.role}`);

// Now authenticated requests work
await client.updateGeoLocation({ latitude: 0, longitude: 0, node_type: "rpc" });
```

## Error Handling

The SDK provides structured error handling matching the API's error system:

```typescript
import {
  createResonanceClient,
  ResonanceError,
  ErrorCode,
  isAuthError,
  isNotFoundError,
} from "@resonance/sdk";

const client = createResonanceClient({ baseUrl: "https://api.fene.network" });

try {
  const validator = await client.getValidator("0xinvalid");
} catch (error) {
  if (error instanceof ResonanceError) {
    console.log(`Error code: ${error.code}`);
    console.log(`Message: ${error.message}`);
    console.log(`Status: ${error.statusCode}`);

    // Check specific error types
    if (error.is(ErrorCode.VALIDATOR_NOT_FOUND)) {
      // Handle validator not found
    }

    // Or use helper functions
    if (isAuthError(error)) {
      // Re-authenticate
    } else if (isNotFoundError(error)) {
      // Handle not found
    }
  }
}
```

### Error Codes

The SDK recognizes these error codes from the API:

- **Validator**: `VALIDATOR_NOT_FOUND`, `VALIDATOR_INACTIVE`
- **Delegator**: `DELEGATOR_NOT_FOUND`, `NOT_WHITELISTED`
- **Referral**: `REFERRAL_KEY_INVALID`, `REFERRAL_KEY_EXPIRED`, `REFERRAL_KEY_USED`
- **Auth**: `INVALID_SIGNATURE`, `NONCE_EXPIRED`, `UNAUTHORIZED`
- **Service**: `RPC_UNAVAILABLE`, `CACHE_UNAVAILABLE`, `DATABASE_UNAVAILABLE`
- **General**: `BAD_REQUEST`, `INTERNAL_ERROR`, `RATE_LIMITED`

## Architecture

### Caching

The Resonance API implements **server-side Redis caching** with graceful degradation. The SDK does not implement client-side caching as all caching is handled by the API layer. This ensures:

- Consistent data across all API consumers
- Reduced load on blockchain RPC nodes
- Automatic cache invalidation on chain state changes
- Cache TTLs optimized per endpoint (10s to 5min)

### Authentication

The SDK uses a standard Web3 authentication flow:

1. Request a nonce from the API (`getNonce`)
2. Sign the message with your wallet (using viem, wagmi, etc.)
3. Submit signature for verification (`verify`)
4. API stores auth in secure httpOnly cookies

The SDK sends credentials automatically (`credentials: include`) and handles refresh flow for 401 responses.

## API Reference

### Auth

- `getNonce(address)` - Get authentication nonce
- `verify({ address, signature, nonce })` - Verify signature and establish cookie-based session
- `getSession()` - Get current session metadata

### Validators

- `getValidators()` - Get all validators
- `getActiveValidators()` - Get active validators
- `getCandidates()` - Get validator candidates
- `getValidator(address)` - Get validator details
- `getValidatorDelegators(address)` - Get validator's delegators

### Delegators

- `getDelegator(address)` - Get delegator info
- `getDelegatorStakes(address)` - Get delegator stakes
- `getDelegatorRewards(address)` - Get delegator rewards

### Referral

- `getReferralKey(key)` - Get referral key info
- `getValidatorKeys(address)` - Get validator's referral keys
- `checkWhitelist(data)` - Check if whitelisted (requires auth)

### Geo

- `getGeoNodes()` - Get all geo nodes
- `getGeoValidators()` - Get validator locations
- `getGeoStats()` - Get geo statistics
- `updateGeoLocation(data)` - Update geo location (requires auth)

### Stats

- `getNetworkStats()` - Get network statistics
- `getCurrentEpoch()` - Get current epoch info

### APR

- `getNetworkAPR()` - Get network-wide APR
- `getValidatorAPR(address)` - Get validator APR breakdown

### Storage

- `uploadAvatar(file)` - Upload avatar (requires auth)
- `getAvatar(address)` - Get avatar URL

### Analytics

- `getDailyBlockStats(days?)` - Get daily block stats
- `getValidatorRewardHistory(address, limit?)` - Get reward history

## Types

All types are exported for TypeScript users:

```typescript
import type {
  Validator,
  Delegator,
  NetworkStats,
  ValidatorAPR,
  // Error types
  ErrorCodeType,
  APIErrorResponse,
} from "@resonance/sdk";

// Error handling utilities
import {
  ResonanceError,
  ErrorCode,
  isResonanceError,
  hasErrorCode,
  isAuthError,
  isNetworkError,
  isNotFoundError,
} from "@resonance/sdk";
```

## Development

### Running Tests

```bash
# Run all tests
pnpm test

# Run tests in watch mode
pnpm test -- --watch

# Run tests with coverage
pnpm test -- --coverage
```

### Building

```bash
# Build the SDK
pnpm run build

# Watch mode for development
pnpm run dev

# Type checking
pnpm run lint
```

## License

MIT