# DexPaprika SDK

[![npm version](https://img.shields.io/npm/v/dexpaprika-sdk.svg)](https://www.npmjs.com/package/dexpaprika-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

The official JavaScript client library for the [DexPaprika API](https://api.dexpaprika.com), providing easy access to decentralized exchange data across multiple blockchain networks.

Developed and maintained by [Coinpaprika](https://coinpaprika.com).

## Installation

```bash
npm install dexpaprika-sdk
```

## Important: migration notices

**Breaking changes in v1.6.0**: The API removed the per-network list/filter REST endpoints (now HTTP 410). `pools.listByNetwork()`, `pools.filter()`, `tokens.getTop()`, and `tokens.filter()` now call the unified search endpoints and return the cursor-paginated shape `{ results, has_next_page, next_cursor }`. Method signatures are unchanged; legacy sort/filter values are mapped internally. See the [CHANGELOG](./CHANGELOG.md) for field-level details.

```javascript
// Cursor pagination (page is ignored on search endpoints):
const first = await client.pools.listByNetwork('ethereum', { limit: 10 });
console.log(first.results, first.has_next_page);
if (first.next_cursor) {
  const next = await client.pools.listByNetwork('ethereum', {
    limit: 10,
    cursor: first.next_cursor
  });
}
```

**Breaking changes in v1.4.0**: The global pools endpoint has been deprecated. All pool queries now require a network specification.

```javascript
// ❌ OLD (deprecated) - will throw DeprecatedEndpointError:
const pools = await client.pools.list();

// ✅ NEW (required) - specify network:
const ethereumPools = await client.pools.listByNetwork('ethereum');
const solanaPools = await client.pools.listByNetwork('solana');
```

## Usage

```javascript
import { DexPaprikaClient } from 'dexpaprika-sdk';

// Create client
const client = new DexPaprikaClient();

// Get supported networks
const networks = await client.networks.list();
console.log(networks);

// Get top pools on Ethereum (cursor-paginated search endpoint)
const pools = await client.pools.listByNetwork('ethereum', {
  limit: 10
});
console.log(pools.results);            // pool rows
console.log(pools.has_next_page, pools.next_cursor); // pagination

// Search for tokens
const results = await client.search.search('bitcoin');
console.log(`Found ${results.tokens.length} tokens`);
```

## Options Pattern API

The DexPaprika SDK uses an options pattern for API method parameters, which provides better flexibility, readability, and extensibility:

```javascript
import { DexPaprikaClient } from 'dexpaprika-sdk';

const client = new DexPaprikaClient();

// Get top pools with sorting and cursor pagination
const pools = await client.pools.listByNetwork('ethereum', {
  limit: 10,
  sort: 'desc',
  orderBy: 'volume_usd_24h' // legacy 'volume_usd' is also accepted and mapped
});

// Get pool details with options
const poolDetails = await client.pools.getDetails(
  'ethereum', 
  '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640',
  { inversed: false }
);

// Get pools that contain a token (network-scoped /pools/search filter,
// cursor-paginated: rows under `results`, page via `cursor`)
const tokenPools = await client.tokens.getPools(
  'ethereum',
  '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
  {
    limit: 5,
    sort: 'desc',
    orderBy: 'volume_usd_24h' // legacy 'volume_usd' is also accepted and mapped
  }
);

// Get OHLCV data with options
const ohlcv = await client.pools.getOHLCV(
  'ethereum',
  '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640',
  {
    start: '2023-01-01',
    end: '2023-01-07',
    limit: 7,
    interval: '24h',
    inversed: false
  }
);
```

## Advanced Configuration

The SDK supports automatic retry with exponential backoff and response caching, both enabled by default:

```javascript
import { DexPaprikaClient } from 'dexpaprika-sdk';

// Custom configuration
const client = new DexPaprikaClient(
  'https://api.dexpaprika.com', // Default API URL
  {}, // Axios options (optional)
  {
    // Retry configuration (optional)
    retry: {
      maxRetries: 4,              // Maximum retry attempts
      delaySequenceMs: [100, 500, 1000, 5000], // Specific delay for each retry (in ms)
      retryableStatuses: [408, 429, 500, 502, 503, 504] // HTTP statuses to retry
    },
    // Cache configuration (optional)
    cache: {
      ttl: 5 * 60 * 1000,         // Time-to-live: 5 minutes
      maxSize: 1000,              // Maximum cache entries
      enabled: true               // Enable/disable caching
    }
  }
);

// Working with cache
const firstCall = await client.networks.list(); // Hits API
const secondCall = await client.networks.list(); // Uses cache

// Manual cache operations
client.clearCache();             // Clear all cached data
console.log(client.cacheSize);   // Get current cache size
client.setCacheEnabled(false);   // Temporarily disable caching
```

## API Reference

For detailed API documentation, visit [docs.dexpaprika.com](https://docs.dexpaprika.com)

### Networks & DEXes

```js
// Networks
const networks = await client.networks.list();

// DEXes on a network
const dexes = await client.dexes.listByNetwork('ethereum', {
  limit: 10
});
```

### Pools & Transactions

```js
// Top pools on Ethereum (cursor-paginated; read results / has_next_page / next_cursor)
const topPools = await client.pools.listByNetwork('ethereum', {
  limit: 10,
  sort: 'desc',
  orderBy: 'volume_usd_24h'
});
console.log(`Got ${topPools.results.length} pools`);

// Fetch the next page with the returned cursor
if (topPools.has_next_page && topPools.next_cursor) {
  const nextPage = await client.pools.listByNetwork('ethereum', {
    limit: 10,
    cursor: topPools.next_cursor
  });
  console.log(`Next page: ${nextPage.results.length} pools`);
}

// Top pools on different networks
const solanaPools = await client.pools.listByNetwork('solana', { limit: 5 });
const fantomPools = await client.pools.listByNetwork('fantom', { limit: 5 });

// Pool details
const poolDetails = await client.pools.getDetails(
  'ethereum',
  '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640'
);

// Transactions
const txs = await client.pools.getTransactions(
  'ethereum',
  '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640',
  { limit: 20 }
);
```

### OHLCV Data

For price charts:

```js
// Price history (daily candles for a week)
const startDate = new Date();
startDate.setDate(startDate.getDate() - 7);

const ohlcv = await client.pools.getOHLCV(
  'ethereum', 
  '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640',
  {
    start: startDate.toISOString(),
    interval: '24h',
    limit: 7
  }
);
```

### Tokens

```js
// Token details (WETH)
const token = await client.tokens.getDetails(
  'ethereum', 
  '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
);

// Find WETH pools (network-scoped /pools/search with token_address).
// Rows come back under `results`; page with `cursor` from `next_cursor`.
const pools = await client.tokens.getPools(
  'ethereum', 
  '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
  {
    limit: 10,
    sort: 'desc',
    orderBy: 'volume_usd_24h'
  }
);
for (const p of pools.results) {
  console.log(p.id, p.volume_usd_24h);
}

// The removed /tokens/{address}/pools endpoint supported pair queries
// (`pairWith`); /pools/search has no equivalent, so `pairWith` is deprecated
// and ignored. To match a pair, filter client-side:
const usdc = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
const wethUsdcPools = pools.results.filter(
  p => (p.tokens ?? []).some(t => t.id === usdc)
);
```

### Pool Filtering

Backed by the unified `/networks/{network}/pools/search` endpoint. Results come
back as `{ results, has_next_page, next_cursor }`; legacy `sortBy` values such as
`volume_24h` are accepted and mapped to canonical fields.

```js
// Find high-volume pools on Ethereum
const filtered = await client.pools.filter('ethereum', {
  volume24hMin: 100000,
  txns24hMin: 50,
  sortBy: 'volume_usd_24h', // legacy 'volume_24h' also accepted
  sortDir: 'desc',
  limit: 10
});
console.log(`Found ${filtered.results.length} pools matching criteria`);
// Next page: pass filtered.next_cursor as `cursor`
```

### Top Tokens & Token Filtering

Both are backed by `/networks/{network}/tokens/search` and return
`{ results, has_next_page, next_cursor }`. Token rows are flat: `address`,
`volume_usd_24h`, `fdv_usd`, `txns_24h`, `price_change_percentage_24h`, etc.

```js
// Get top tokens by 24h volume
const topTokens = await client.tokens.getTop('ethereum', {
  orderBy: 'volume_usd_24h', // legacy 'volume_24h' also accepted
  limit: 10
});
console.log(topTokens.results.map(t => t.address));

// Filter tokens by criteria
const filtered = await client.tokens.filter('ethereum', {
  volume24hMin: 100000,
  fdvMin: 1000000,
  limit: 10
});
```

### Batch Token Prices

```js
// Get prices for multiple tokens in one request (max 10)
const prices = await client.tokens.getMultiPrices('ethereum', [
  '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
  '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC
]);
prices.forEach(p => console.log(`${p.id}: $${p.price_usd}`));
```

### Search & Stats

```js
// Search
const results = await client.search.search('bitcoin');

// Stats
const stats = await client.utils.getStats();
```

## TypeScript Types

The SDK includes TypeScript types for easier development. See the documentation for response type details.

## Error Handling

The SDK includes specific error classes for better error handling:

```javascript
import { 
  DexPaprikaClient, 
  DeprecatedEndpointError, 
  NetworkNotFoundError,
  PoolNotFoundError,
  ApiError 
} from 'dexpaprika-sdk';

const client = new DexPaprikaClient();

try {
  // This will throw DeprecatedEndpointError
  const pools = await client.pools.list();
} catch (error) {
  if (error instanceof DeprecatedEndpointError) {
    console.log('Migration required:', error.message);
    // Use network-specific method instead
    const pools = await client.pools.listByNetwork('ethereum');
  } else if (error instanceof NetworkNotFoundError) {
    console.log('Invalid network:', error.message);
  } else if (error instanceof PoolNotFoundError) {
    console.log('Pool not found:', error.message);
  } else {
    console.error('Other error:', error.message);
  }
}
```

Using the error helper:

```js
import { parseError } from 'dexpaprika-sdk/dist/utils/helpers';

try {
  const pools = await client.pools.listByNetwork('ethereum');
  // Process results
} catch (err) {
  console.error(parseError(err));
}
```

## Utilities

Helper functions:

```js
import { formatVolume, formatPair } from 'dexpaprika-sdk/dist/utils/helpers';

// Format volume like $1.23M
console.log(formatVolume(1234567));  // $1.23M

// Format token pair
console.log(formatPair('ETH', 'USDC')); // ETH/USDC
```

## Retry & Caching

You can also use the retry and caching utilities directly:

```js
import { withRetry, Cache } from 'dexpaprika-sdk';

// Retry a function with custom settings
const result = await withRetry(
  async () => {
    // Your async operation here
    return await someAsyncFunction();
  },
  { maxRetries: 4, delaySequenceMs: [100, 200, 300, 400] }
);

// Create a standalone cache
const cache = new Cache({ ttl: 60 * 1000 }); // 1 minute TTL
cache.set('key', value);
const cachedValue = cache.get('key');
```

## Resources

- [Official Documentation](https://docs.dexpaprika.com) - Comprehensive API reference
- [DexPaprika Website](https://dexpaprika.com) - Main product website
- [CoinPaprika](https://coinpaprika.com) - Related cryptocurrency data platform
- [Discord Community](https://discord.gg/DhJge5TUGM) - Get support and connect with other developers

## License

MIT

## Contributing

Contributions are welcome! Please read our [contributing guidelines](https://github.com/coinpaprika/dexpaprika-sdk-ts/blob/main/CONTRIBUTING.md) before submitting a Pull Request.

## Development and Testing

The SDK includes a comprehensive test suite to verify functionality:

```bash
# Run basic functionality tests
npm test

# Run real-world API tests
npx ts-node tests/test-real-world.ts

# Run all tests sequentially
npm run test:all
```

All test files are located in the `tests/` directory:
- `test-basic.ts` - Basic API functionality tests
- `test-real-world.ts` - Tests with actual API calls and simulated failures

## Support

For issues, questions, or feedback, please:
- Open an issue on our [GitHub repository](https://github.com/coinpaprika/dexpaprika-sdk-ts/issues)
- Reach out to the DexPaprika team at [support@coinpaprika.com](mailto:support@coinpaprika.com)

## Disclaimer

This SDK is created and maintained by Coinpaprika. While we strive to provide accurate and up-to-date data, we make no guarantees regarding the accuracy, reliability, or completeness of the data provided through the DexPaprika API.
