# 🎯 Uniswap Position Quoter

<div align="center">

**High-precision rebalance calculations for multi-position liquidity strategies**

[![npm version](https://img.shields.io/npm/v/@steerprotocol/uniswap-position-quoter.svg)](https://www.npmjs.com/package/@steerprotocol/uniswap-position-quoter)
[![License](https://img.shields.io/npm/l/@steerprotocol/uniswap-position-quoter.svg)](https://github.com/steerprotocol/uniswap-position-quoter/blob/main/LICENSE)
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)

</div>

---

## ✨ Features

- 🎯 **99% Accuracy** - Precise rebalance calculations for concentrated liquidity
- 🏭 **Factory Pattern** - Simple, automatic protocol detection
- 🔌 **Multi-Protocol** - Supports 9+ DEX protocols out of the box
- 📦 **TypeScript Native** - Full type safety and IntelliSense support
- 🎨 **Modular Architecture** - Clean, maintainable codebase
- ⚡ **Zero Config** - Works with sensible defaults
- 🔧 **Extensible** - Easy to add new protocols

## 🆕 What's New in v2.0.0

### SwapCalculator - High-Level Orchestrator

The new `SwapCalculator` class provides a simplified, all-in-one API for swap calculations:

```typescript
import { SwapCalculator } from '@steerprotocol/uniswap-position-quoter';

const calculator = new SwapCalculator(signer, 'arbitrum');
const result = await calculator.calculateSwapForVault({
  vaultAddress: '0x...',
  beaconName: 'UniswapV3',
  token0: '0x...',
  token1: '0x...',
  newPositions: positions,
  bal0: 1000000000000000000n,
  bal1: 2000000000000000000n,
  poolAddress: '0x...',
  poolFee: 3000,
  sqrtPriceX96: 79228162514264337593543950336n,
  slippage: 0.01,
});
```

### Dynamic Quoter Address Resolution

No more manual quoter address management! The SDK now includes network configurations for automatic address resolution:

```typescript
// Automatically resolves quoter addresses from network config
const calculator = new SwapCalculator(signer, 'arbitrum');
```

### Helper Modules

Seven new helper modules for advanced use cases:

- **`AbiManager`** - Protocol-specific ABI resolution
- **`ContractFactory`** - Ethers.js contract instantiation
- **`ProtocolDetector`** - Automatic protocol detection
- **`PositionExtractor`** - Position data extraction
- **`BalanceQuerier`** - Vault balance queries
- **`PoolStateQuerier`** - Pool state queries
- **`NetworkConfig`** - Network-specific configurations

### Breaking Changes

See the [Migration Guide](#migration-guide-from-v1x-to-v200) below for details on upgrading from v1.x.

## 📖 Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Supported Protocols](#supported-protocols)
- [API Reference](#api-reference)
- [Advanced Usage](#advanced-usage)
- [Examples](#examples)
- [Documentation](#documentation)
- [Contributing](#contributing)

---

## 📦 Installation

```bash
# npm
npm install @steerprotocol/uniswap-position-quoter

# yarn
yarn add @steerprotocol/uniswap-position-quoter

# pnpm
pnpm add @steerprotocol/uniswap-position-quoter
```

## 🚀 Quick Start

### Basic Usage

The simplest way to use the SDK - just 3 steps:

```typescript
import {
  CustomRouterFactory,
  NoopLogger,
} from '@steerprotocol/uniswap-position-quoter';
import { ethers } from 'ethers';

// 1. Create the factory
const factory = new CustomRouterFactory(NoopLogger);

// 2. Create a router (automatic protocol detection)
const router = factory.createCustomRouter({
  beaconName: 'UniswapV3', // Your vault's beacon name
  quoterV2: quoterContract, // Your QuoterV2 contract
  poolObj: poolContract, // Your pool contract
});

// 3. Get swap amount for rebalancing
const result = await router.getSwapAmount(
  poolContract,
  positions, // Your desired positions
  token0Balance, // Current token0 balance
  token1Balance, // Current token1 balance
  token0Address,
  token1Address,
  poolFee,
  10, // Max iterations
);

console.log('Swap amount:', result.amount.toString());
console.log(
  'Direction:',
  result.zeroForOne ? 'Token0 → Token1' : 'Token1 → Token0',
);
```

### Complete Example

```typescript
import {
  CustomRouterFactory,
  NoopLogger,
  type Position,
  type SwapResult,
} from '@steerprotocol/uniswap-position-quoter';
import { ethers } from 'ethers';

// Setup
const provider = new ethers.JsonRpcProvider(RPC_URL);
const quoterContract = new ethers.Contract(QUOTER_ADDRESS, QuoterABI, provider);
const poolContract = new ethers.Contract(POOL_ADDRESS, PoolABI, provider);

// Create factory
const factory = new CustomRouterFactory(NoopLogger);

// Create router
const router = factory.createCustomRouter({
  beaconName: 'UniswapV3',
  quoterV2: quoterContract,
  poolObj: poolContract,
});

// Define your liquidity positions
const positions: Position[] = [
  {
    lowerTick: -887220,
    upperTick: 887220,
    weight: 1,
  },
];

// Get optimal swap amount
const result: SwapResult = await router.getSwapAmount(
  poolContract,
  positions,
  ethers.parseEther('100'), // 100 token0
  ethers.parseEther('100'), // 100 token1
  TOKEN0_ADDRESS,
  TOKEN1_ADDRESS,
  3000, // 0.3% fee
  10, // max iterations
);

// Use the result
if (result.zeroForOne) {
  console.log(`Swap ${result.amount} of token0 for token1`);
} else {
  console.log(`Swap ${result.amount} of token1 for token0`);
}
```

---

## 🎨 Supported Protocols

The SDK automatically detects and supports the following protocols:

| Protocol                | Description               | Special Config            |
| ----------------------- | ------------------------- | ------------------------- |
| **Uniswap V3**          | Standard Uniswap V3 pools | None                      |
| **Uniswap V4**          | Latest Uniswap version    | None                      |
| **Algebra**             | Algebra DEX               | None                      |
| **Algebra Integral**    | Algebra Integral variants | None                      |
| **Algebra Integral V2** | With optional deployer    | Optional deployer address |
| **ThickV2**             | ThickV2 protocol          | None                      |
| **Aerodrome**           | Aerodrome DEX             | None                      |
| **Shadow**              | Shadow protocol           | None                      |
| **PoolShark**           | PoolShark DEX             | None                      |

---

## 📚 API Reference

### CustomRouterFactory

The main entry point for the SDK.

```typescript
import {
  CustomRouterFactory,
  NoopLogger,
} from '@steerprotocol/uniswap-position-quoter';

const factory = new CustomRouterFactory(logger);
```

#### `createCustomRouter(params)`

Creates a router for the specified protocol.

**Parameters:**

- `beaconName: string` - Your vault's beacon name
- `quoterV2: Contract` - QuoterV2 contract instance
- `poolObj: Contract` - Pool contract instance
- `protocolParams?: object` - Optional protocol-specific parameters

**Returns:** `CustomRouter` - A router instance for your protocol

**Example:**

```typescript
const router = factory.createCustomRouter({
  beaconName: 'UniswapV3',
  quoterV2: quoterContract,
  poolObj: poolContract,
});
```

### Router Methods

#### `getSwapAmount()`

Calculates the optimal swap amount for rebalancing positions.

**Parameters:**

- `pool: Contract` - Pool contract
- `positions: Position[]` - Desired liquidity positions
- `token0Balance: BigNumber` - Current token0 balance
- `token1Balance: BigNumber` - Current token1 balance
- `token0Address: string` - Token0 address
- `token1Address: string` - Token1 address
- `fee: number` - Pool fee (e.g., 3000 for 0.3%)
- `maxIterations: number` - Maximum calculation iterations

**Returns:** `Promise<SwapResult>`

```typescript
{
    amount: BigNumber,      // Amount to swap
    zeroForOne: boolean    // true: swap token0→token1, false: token1→token0
}
```

---

## 🔧 Advanced Usage

### Protocol-Specific Parameters

Some protocols require additional configuration:

```typescript
import {
  CustomRouterFactory,
  NoopLogger,
} from '@steerprotocol/uniswap-position-quoter';

const factory = new CustomRouterFactory(NoopLogger);

// Algebra Integral V2 with custom deployer (e.g., Blackhole)
const router = factory.createCustomRouter({
  beaconName: 'AlgebraIntegral_v2_0_Blackhole',
  quoterV2: quoterContract,
  poolObj: poolContract,
  protocolParams: {
    algebraIntegralV2Deployer: '0x1234567890123456789012345678901234567890',
  },
});
```

### Custom Logger

Implement your own logger for production monitoring:

```typescript
import {
  CustomRouterFactory,
  type Logger,
} from '@steerprotocol/uniswap-position-quoter';

const myLogger: Logger = {
  log: (payload, msg, ...args) => console.log(msg, payload),
  info: (payload, msg, ...args) => console.info(msg, payload),
  error: (payload, msg, ...args) => console.error(msg, payload),
  warn: (payload, msg, ...args) => console.warn(msg, payload),
  debug: (payload, msg, ...args) => console.debug(msg, payload),
};

const factory = new CustomRouterFactory(myLogger);
```

### TypeScript Types

Full TypeScript support with exported types:

```typescript
import type {
  Position,
  SwapResult,
  GetSwapAmountOptions,
  CreateCustomRouterParams,
  ProtocolSpecificParams,
} from '@steerprotocol/uniswap-position-quoter';

const positions: Position[] = [
  { lowerTick: -1000, upperTick: 1000, weight: 1 },
];

const result: SwapResult = await router.getSwapAmount(/* ... */);
```

---

## 💡 Examples

### Example 1: Simple Uniswap V3 Rebalance

```typescript
import {
  CustomRouterFactory,
  NoopLogger,
} from '@steerprotocol/uniswap-position-quoter';

const factory = new CustomRouterFactory(NoopLogger);
const router = factory.createCustomRouter({
  beaconName: 'UniswapV3',
  quoterV2: quoterContract,
  poolObj: poolContract,
});

const result = await router.getSwapAmount(
  poolContract,
  [{ lowerTick: -887220, upperTick: 887220, weight: 1 }],
  currentToken0Balance,
  currentToken1Balance,
  token0Address,
  token1Address,
  3000,
  10,
);
```

### Example 2: Multi-Position Strategy

```typescript
const positions = [
  { lowerTick: -887220, upperTick: -443610, weight: 2 }, // Wide position
  { lowerTick: -443610, upperTick: 443610, weight: 5 }, // Main position
  { lowerTick: 443610, upperTick: 887220, weight: 2 }, // Wide position
];

const result = await router.getSwapAmount(
  poolContract,
  positions,
  token0Balance,
  token1Balance,
  token0Address,
  token1Address,
  3000,
  15, // More iterations for complex strategies
);
```

### Example 3: Protocol with Special Configuration

```typescript
// For protocols like Blackhole that need deployer address
const router = factory.createCustomRouter({
  beaconName: 'AlgebraIntegral_v2_0_Blackhole',
  quoterV2: quoterContract,
  poolObj: poolContract,
  protocolParams: {
    algebraIntegralV2Deployer: process.env.BLACKHOLE_DEPLOYER,
  },
});
```

---

## 🔄 Migration Guide (from v1.x to v2.0.0)

### Overview

v2.0.0 introduces significant enhancements while maintaining backward compatibility for existing code. The main addition is the `SwapCalculator` high-level API.

### What's Changed

#### ✅ Backward Compatible (No Changes Required)

The existing factory pattern continues to work exactly as before:

```typescript
// v1.x code - Still works in v2.0.0
import {
  CustomRouterFactory,
  NoopLogger,
} from '@steerprotocol/uniswap-position-quoter';

const factory = new CustomRouterFactory(NoopLogger);
const router = factory.createCustomRouter({
  beaconName: 'UniswapV3',
  quoterV2: quoterContract,
  poolObj: poolContract,
});

const result = await router.getSwapAmount(/* ... */);
```

#### 🆕 New in v2.0.0

**1. SwapCalculator (Recommended for New Code)**

The new high-level API simplifies complex workflows:

```typescript
// v2.0.0 - New simplified API
import { SwapCalculator } from '@steerprotocol/uniswap-position-quoter';

const calculator = new SwapCalculator(signer, 'arbitrum');
const result = await calculator.calculateSwapForVault({
  vaultAddress: '0x...',
  beaconName: 'UniswapV3',
  token0: '0x...',
  token1: '0x...',
  newPositions: positions,
  bal0: balance0,
  bal1: balance1,
  poolAddress: '0x...',
  poolFee: 3000,
  sqrtPriceX96: currentSqrtPrice,
  slippage: 0.01,
});
```

**2. Helper Modules**

Seven new helper modules are now available for advanced use cases:

```typescript
import {
  AbiManager,
  ContractFactory,
  QuoterFactory,
  PositionExtractor,
  BalanceQuerier,
  PoolStateQuerier,
  getNetworkConfig,
} from '@steerprotocol/uniswap-position-quoter';
```

**3. Dynamic Quoter Resolution**

No more manual quoter address management:

```typescript
// Before (v1.x) - Manual address required
const router = factory.createCustomRouter({
  beaconName: 'UniswapV3',
  quoterV2: new Contract(QUOTER_ADDRESS, abi, signer), // Had to know the address
  poolObj: poolContract,
});

// After (v2.0) - Automatic resolution with SwapCalculator
const calculator = new SwapCalculator(signer, 'arbitrum');
// Quoter address resolved automatically from network config
```

### Migration Steps

1. **No action required** for existing code - it will continue to work
2. **For new features**, consider using `SwapCalculator` for simpler implementations
3. **For advanced use cases**, leverage the new helper modules

### Breaking Changes

None. v2.0.0 is fully backward compatible with v1.x code.

---

## 📖 Documentation

Comprehensive documentation is available:

- **[API Guide](./API_GUIDE.md)** - Complete API reference
- **[Router Factory Usage](./ROUTER_FACTORY_USAGE.md)** - Detailed factory usage guide
- **[Logger Implementation](./LOGGER_USAGE.md)** - Custom logger guide
- **[Testing Guide](./TESTING_GUIDE.md)** - Local and remote test release workflows
- **[Examples](./examples/)** - Code examples and patterns
- **[Architecture](./BEFORE_AFTER_STRUCTURE.md)** - Project structure

---

## 🏗️ Architecture

The SDK uses a modular architecture:

```
src/
├── factory/          # CustomRouterFactory & protocol detection
├── routers/          # Protocol-specific router implementations
│   ├── uniswap/     # Uniswap V3 & V4
│   ├── algebra/     # Algebra variants
│   └── [others]/    # Other protocol routers
├── engines/          # Quote calculation engines
├── types/            # TypeScript type definitions
└── utils/            # Utilities (logger, math, constants)
```

**Benefits:**

- ✅ Clean separation of concerns
- ✅ Easy to add new protocols
- ✅ Maintainable and testable
- ✅ Tree-shakeable for smaller bundles

---

## 🔒 Security

- All calculations are performed on-chain or using verified contracts
- No private keys or sensitive data are required
- Open source and auditable

---

## 🤝 Contributing

We welcome contributions! Here's how you can help:

1. **Report Bugs** - Open an issue with details
2. **Suggest Features** - Share your ideas
3. **Submit PRs** - Fix bugs or add features
4. **Improve Docs** - Help others understand the SDK

### Adding a New Protocol

1. Create router in `src/routers/[protocol]/`
2. Add detection logic in `src/factory/protocol-detector.ts`
3. Update factory in `src/factory/custom-router-factory.ts`
4. Add tests and documentation

See [CONTRIBUTING.md](./CONTRIBUTING.md) for detailed guidelines.

---

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.

---

## 🙏 Acknowledgments

Built with ❤️ by [Steer Protocol](https://steer.finance)

- [@uniswap/v3-sdk](https://github.com/Uniswap/v3-sdk) - Uniswap V3 SDK
- [@uniswap/v3-periphery](https://github.com/Uniswap/v3-periphery) - Uniswap periphery contracts

---

## 📞 Support

- **Documentation**: [Full docs](./API_GUIDE.md)
- **Issues**: [GitHub Issues](https://github.com/steerprotocol/uniswap-position-quoter/issues)
- **Discord**: [Join our community](https://discord.gg/steerprotocol)

---

<div align="center">

**[⬆ back to top](#-uniswap-position-quoter)**

Made with ❤️ by the Steer Protocol team

</div>
