# Steer Protocol SDK

A TypeScript SDK for interacting with Steer Protocol services.

## Installation

```bash
npm install @steerprotocol/sdk viem
# or
yarn add @steerprotocol/sdk viem
# or
pnpm add @steerprotocol/sdk viem
```

## Usage

```typescript
import { createPublicClient, createWalletClient, http } from 'viem';
import { mainnet } from 'viem/chains';
import { VaultClient, StakingClient } from '@steerprotocol/sdk';

// Create viem clients
const publicClient = createPublicClient({
  chain: mainnet,
  transport: http(),
});

const walletClient = createWalletClient({
  chain: mainnet,
  transport: http(),
});

// Initialize clients
const vaultClient = new VaultClient(publicClient, walletClient, 'production');
const stakingClient = new StakingClient(publicClient, walletClient);

// Use vault operations (including single-asset deposits)
const vaults = await vaultClient.getVaults({ chainId: 1 });

// Use staking operations
const pools = await stakingClient.getStakingPools();
```

## Supported Chains, Subgraphs, and Protocols

The SDK exposes a capabilities registry so integrators can discover supported chains and the relevant configuration (subgraph URLs, protocol availability, deployments) without enumerating enums.

```ts
import { getSupportedChains, getSupportedChainIds } from '@steerprotocol/sdk';

// Most lightweight: chains that have Steer subgraph config
const supportedChainIds = getSupportedChainIds();

// Opt in to additional details (deployments, protocol subgraphs, etc)
const supported = getSupportedChains({
  include: {
    subgraph: true,
    deployments: true,
    protocols: true,
    singleAssetDeposit: true,
  },
  // Used for protocol-specific subgraph discovery (The Graph / Studio)
  theGraphApiKey: process.env.SUBGRAPH_STUDIO_KEY ?? '',
});

for (const chain of supported) {
  console.log({
    chainId: chain.chainId,
    chain: chain.chain,
    subgraphUrl: chain.subgraphUrl,
    protocolCount: chain.protocols?.length ?? 0,
    features: chain.features,
  });
}
```

### Available Features

- **Vault Operations**: Traditional vault deposits, withdrawals, and balance checking
- **Single-Asset Deposits**: Deposit one token and automatically balance for LP provision
- **Staking**: Interact with staking pools for rewards
- **Pool Data**: Fetch pool information and statistics
- **Smart Rewards**: Manage reward campaigns and claims

## Staking Client

The StakingClient provides functionality for interacting with Steer Protocol staking pools. It supports both single and dual reward staking pools.

### Features

- Fetch available staking pools
- Stake and withdraw tokens
- Claim rewards
- Check earned rewards and balances
- Calculate APR for staking pools
- Support for both single and dual reward pools

### Example Usage

```typescript
// Initialize the staking client
const stakingClient = steerClient.staking;

// Get all staking pools
const pools = await stakingClient.getStakingPools();

// Get pools for a specific chain
const polygonPools = await stakingClient.getStakingPools(137); // Polygon chain ID

// Stake tokens
await stakingClient.stake({
  stakingPool: '0x...', // staking pool address
  amount: 1000000000000000000n, // amount in wei
});

// Check earned rewards
const earned = await stakingClient.earned('0x...', '0x...'); // pool address, account address
console.log('Earned rewards:', earned.data);

// Calculate APR
const apr = stakingClient.calculateAPR(pool, rewardTokenPriceUSD, totalStakedUSD);
```

### Available Methods

- `getStakingPools(chainId?: number, protocol?: string)`: Fetch available staking pools
- `stake(params: StakeParams)`: Stake tokens in a pool
- `withdraw(params: WithdrawParams)`: Withdraw tokens from a pool
- `getReward(params: GetRewardParams)`: Claim rewards
- `earned(stakingPool: Address, account: Address)`: Get earned rewards
- `totalSupply(stakingPool: Address)`: Get total staked tokens
- `balanceOf(stakingPool: Address, account: Address)`: Get staked balance
- `calculateAPR(pool: StakingPool, rewardTokenPriceUSD: number, totalStakedUSD: number)`: Calculate current pool APR

## Single-Asset Deposit

The Single-Asset Deposit feature allows users to deposit only one token (token0 or token1) into a liquidity vault, while the contract internally performs a swap to balance the token pair before depositing into the vault. This eliminates the need for users to provide both tokens in the correct ratio.

### Features

- **Single Token Input**: Deposit only token0 or token1 instead of both
- **Automatic Balancing**: Internal swap to achieve the correct token ratio
- **Slippage Protection**: Configurable slippage limits for both swap and deposit
- **Preview Functionality**: Simulate the entire process before execution
- **Modular Architecture**: Each step is separately testable and reusable
- **Multiple AMM Support**: Currently supports UniswapV3, extensible for other AMMs
- **Comprehensive Error Handling**: Detailed error messages and validation

### How It Works

The single-asset deposit process follows these steps:

1. **Calculate Swap Amount**: Determine how much of the input token needs to be swapped
2. **Calculate Limit Price**: Apply slippage protection using current pool price
3. **Simulate Swap**: Preview the swap results without executing
4. **Estimate LP Tokens**: Calculate expected LP token output
5. **Execute Deposit**: Submit the transaction with all parameters

### Quick Start

```typescript
import { VaultClient, AMMType } from '@steerprotocol/sdk';
import { createPublicClient, createWalletClient, http, parseEther } from 'viem';
import { mainnet } from 'viem/chains';

// Setup clients
const publicClient = createPublicClient({
  chain: mainnet,
  transport: http(),
});

const walletClient = createWalletClient({
  chain: mainnet,
  transport: http(),
});

// Initialize vault client
const vaultClient = new VaultClient(publicClient, walletClient, 'production');

// Preview single-asset deposit
const preview = await vaultClient.previewSingleAssetDeposit(
  {
    assets: parseEther('100'), // 100 tokens to deposit
    receiver: userAddress, // Address to receive LP tokens
    vault: vaultAddress, // Vault contract address
    isToken0: true, // Depositing token0
    depositSlippagePercent: 5n, // 5% max slippage for deposit
    swapSlippageBP: 500, // 5% slippage for internal swap (in basis points)
    ammType: AMMType.UniswapV3, // AMM type
    singleAssetDepositContract: contractAddress, // Single-asset deposit contract
  },
  poolAddress
);

if (preview.success) {
  console.log('Expected LP tokens:', preview.data.lpEstimation.lpTokens);
  console.log('Amount to be swapped:', preview.data.swapAmount);
  console.log('Final token0 amount:', preview.data.lpEstimation.finalAmount0);
  console.log('Final token1 amount:', preview.data.lpEstimation.finalAmount1);

  // Execute the deposit
  const result = await vaultClient.singleAssetDeposit({
    assets: parseEther('100'),
    receiver: userAddress,
    vault: vaultAddress,
    isToken0: true,
    depositSlippagePercent: 5n,
    swapSlippageBP: 500,
    ammType: AMMType.UniswapV3,
    singleAssetDepositContract: contractAddress,
  });

  if (result.success) {
    console.log('Transaction hash:', result.data);
  }
}
```

### Advanced Usage

#### Using Individual Functions

Each step can be used independently for testing or custom implementations:

```typescript
import {
  calculateSwapAmount,
  calculateLimitPrice,
  simulateSwap,
  estimateLpTokens,
  AMMType,
} from '@steerprotocol/sdk';

// Step 1: Calculate swap amount
const swapResult = await calculateSwapAmount(publicClient, {
  depositAmount: parseEther('100'),
  isToken0: true,
  vault: vaultAddress,
  pool: poolAddress,
  ammType: AMMType.UniswapV3,
  singleAssetDepositContract: contractAddress,
});

// Step 2: Calculate price limit for slippage protection
const limitPrice = await calculateLimitPrice(publicClient, {
  pool: poolAddress,
  slippageBP: 500,
  zeroForOne: true,
  ammType: AMMType.UniswapV3,
});

// Step 3: Simulate the swap
const simulation = await simulateSwap(
  publicClient,
  {
    pool: poolAddress,
    recipient: userAddress,
    zeroForOne: true,
    amountSpecified: parseEther('10'),
    sqrtPriceLimitX96: limitPrice,
    ammType: AMMType.UniswapV3,
    tokenIn: '0x...', // Token being swapped from
    tokenOut: '0x...', // Token being swapped to
    fee: 3000, // Pool fee in basis points
  },
  137
); // Chain ID (e.g., 137 for Polygon)

// Step 4: Estimate LP tokens
const estimation = await estimateLpTokens(publicClient, {
  vault: vaultAddress,
  originalAssets: parseEther('100'),
  swapAmount: swapResult.data.swapAmount,
  swapResult: simulation.data,
  isToken0: true,
});
```

#### Using the SingleAssetDepositClient Directly

```typescript
import { SingleAssetDepositClient } from '@steerprotocol/sdk';

const client = new SingleAssetDepositClient(publicClient, walletClient);

// Preview deposit
const preview = await client.previewDeposit(params, poolAddress);

// Prepare transaction (without executing)
const preparedTx = await client.prepareDepositTx(params);

// Validate parameters
client.validateDepositParams(params);
```

### Supported AMM Types

```typescript
enum AMMType {
  UniswapV3 = 0, // ✅ Fully implemented
  Algebra = 1, // 🚧 Planned
  AlgebraDirectional = 2, // 🚧 Planned
  AlgebraVE33 = 3, // 🚧 Planned
  AlgebraIntegral = 4, // 🚧 Planned
}
```

Currently, only **UniswapV3** is fully implemented. Other AMM types will be added in future releases.

### Error Handling

All functions return a consistent `SteerResponse<T>` format:

```typescript
interface SteerResponse<T> {
  data: T | null;
  status: number;
  success: boolean;
  error?: string;
}

// Example error handling
const result = await vaultClient.previewSingleAssetDeposit(params, poolAddress);
if (!result.success) {
  console.error('Preview failed:', result.error);
  return;
}

// Use result.data safely
console.log('LP tokens:', result.data.lpEstimation.lpTokens);
```

### Common Error Cases

- **Invalid AMM Type**: Only UniswapV3 is currently supported
- **Insufficient Liquidity**: Pool doesn't have enough liquidity for the swap
- **Slippage Too High**: Price moved beyond acceptable limits
- **Invalid Addresses**: Zero addresses or invalid contract addresses
- **Amount Too Small**: Deposit amount too small to process

### Testing

Each function is designed to be independently testable:

```typescript
import { calculateSwapAmount, AMMType } from '@steerprotocol/sdk';

describe('Single-Asset Deposit', () => {
  it('should calculate correct swap amount', async () => {
    const result = await calculateSwapAmount(publicClient, {
      depositAmount: parseEther('100'),
      isToken0: true,
      vault: vaultAddress,
      pool: poolAddress,
      ammType: AMMType.UniswapV3,
      singleAssetDepositContract: contractAddress,
    });

    expect(result.success).toBe(true);
    expect(result.data.swapAmount).toBeGreaterThan(0n);
  });
});
```

### Configuration Parameters

| Parameter                    | Type      | Description                               |
| ---------------------------- | --------- | ----------------------------------------- |
| `assets`                     | `bigint`  | Amount of input token to deposit          |
| `receiver`                   | `Address` | Address to receive LP tokens              |
| `vault`                      | `Address` | Vault contract address                    |
| `isToken0`                   | `boolean` | `true` for token0, `false` for token1     |
| `depositSlippagePercent`     | `bigint`  | Max slippage for deposit (in %)           |
| `swapSlippageBP`             | `number`  | Slippage for internal swap (basis points) |
| `ammType`                    | `AMMType` | Type of AMM (currently UniswapV3)         |
| `singleAssetDepositContract` | `Address` | Single-asset deposit contract address     |

### Architecture

The module is built with a modular architecture where each step is a separate, testable function:

```
src/base/vault/single-asset/
├── types.ts                    # TypeScript interfaces and enums
├── calculateSwapAmount.ts      # Step 1: Calculate swap amount
├── calculateLimitPrice.ts      # Step 2: Calculate slippage protection
├── simulateSwap.ts            # Step 3: Simulate swap execution
├── estimateLpTokens.ts        # Step 4: Estimate LP token output
├── singleAssetDeposit.ts      # Main orchestrator client
└── index.ts                   # Exports everything
```

This design allows for:

- **Individual Testing**: Each function can be unit tested separately
- **Reusability**: Functions can be used in different contexts
- **Maintainability**: Easy to update or extend individual components
- **Debugging**: Easy to isolate issues to specific steps

## Requirements

- Node.js >= 18.0.0
- viem >= 2.22.0

## Development

1. Install dependencies:

```bash
npm install
```

2. Build the package:

```bash
npm run build
```

3. Run tests:

```bash
npm test
```

4. Format code:

```bash
npm run format
```

5. Lint code:

```bash
npm run lint
```

## Troubleshooting

### CommonJS/ESM Import Issues

If you encounter the following error:

```typescript
import { SteerClient } from "@steerprotocol/sdk";
         ^^^^^^^^^^^
SyntaxError: Named export 'SteerClient' not found. The requested module '@steerprotocol/sdk' is a CommonJS module...
```

Add the following to your `tsconfig.json`:

```json
{
  "compilerOptions": {
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
  }
}
```

## License

MIT
