npm package: @nemoprotocol/vaults-sdk
Description: A TypeScript SDK for interacting with Nemo Vaults on the Sui blockchain
Author: Nemo Protocol
Latest version: 0.4.1

# Nemo Vaults SDK

A TypeScript SDK for interacting with Nemo Vaults on the Sui blockchain. This SDK provides comprehensive functionality for managing liquidity vaults, including deposits, withdrawals, rebalancing, and administrative operations.

## Features

- **Vault Management**: Create, configure, and manage different types of vaults (Stable, Uncorrelated, Drift)
- **Liquidity Operations**: Deposit and withdraw liquidity with automatic optimization
- **Automated Bots**: Built-in reward collection and rebalancing bots
- **Administrative Functions**: Complete admin interface for vault configuration
- **Multi-Protocol Support**: Integration with various staking protocols (Haedal, Volo, Aftermath)
- **Docker Support**: Ready-to-use Docker containers for production deployment

## Installation

```bash
npm install @nemoprotocol/vaults-sdk
# or
yarn add @nemoprotocol/vaults-sdk
# or
pnpm add @nemoprotocol/vaults-sdk
```

## Quick Start

```typescript
import { Vaults, Admin } from '@nemoprotocol/vaults-sdk';
import { SuiClient } from '@mysten/sui/client';

// Initialize the SDK
const suiClient = new SuiClient({ url: 'https://sui-mainnet.blockvision.org' });
const vaults = Vaults.createSDK({
  client: suiClient
});

// Get all vaults
const vaultList = await vaults.getVaultList();

// Get a specific vault
const vault = await vaults.getVault('vault-id');

// Get user's vault balances
const balances = await vaults.getOwnerVaultsBalance('user-address');
```

## Core Concepts

### Vault Types

1. **Stable Vaults**: Optimized for stable asset pairs with minimal price volatility
2. **Uncorrelated Vaults**: Designed for assets with low correlation, includes rebalancing triggers
3. **Drift Vaults**: Specialized for trending markets with directional price movements

### Liquidity Management

- **Both-sided deposits**: Provide both tokens in optimal ratio
- **One-sided deposits**: Provide single token with automatic swapping
- **Flexible withdrawals**: Withdraw as LP tokens or individual assets
- **Slippage protection**: Configurable slippage tolerance for all operations

## Examples

### Basic Vault Operations

```typescript
import { Vaults, Admin, InputType } from '@nemoprotocol/vaults-sdk';
import { SuiClient } from '@mysten/sui/client';
import { Transaction } from '@mysten/sui/transactions';

// Initialize the SDK
const suiClient = new SuiClient({ url: 'https://sui-mainnet.blockvision.org' });
const vaults = Vaults.createSDK({
  client: suiClient
});

async function basicVaultOperations() {
  try {
    // 1. Get all available vaults
    console.log('Fetching all vaults...');
    const allVaults = await vaults.getVaultList();
    console.log(`Found ${allVaults.length} vaults`);

    // 2. Get detailed information about a specific vault
    const vaultId = allVaults[0].id;
    const vault = await vaults.getVault(vaultId);
    if (!vault) {
      console.error('Vault not found');
      return;
    }
    console.log('Vault details:', {
      id: vault.id,
      coinTypeA: vault.coin_type_a,
      coinTypeB: vault.coin_type_b,
      totalSupply: vault.total_supply,
      depositEnabled: vault.is_deposit_enabled
    });

    // 3. Check user's vault balances
    const userAddress = 'user-wallet-address';
    const userBalances = await vaults.getOwnerVaultsBalance(userAddress);
    console.log(`User has positions in ${userBalances.length} vaults`);

    // 4. Calculate deposit amount for one-sided deposit
    console.log('Calculating deposit amount...');
    const depositCalc = await vaults.calculateDepositAmount({
      vault_id: vaultId,
      is_amount_a: true,
      input_amount: '1000000', // 1 token (assuming 6 decimals)
      slippage: 0.01, // 1% slippage
      side: InputType.OneSide
    });

    console.log('Deposit calculation result:', {
      amountA: depositCalc.amount_a,
      amountB: depositCalc.amount_b,
      lpTokens: depositCalc.ft_amount,
      swapRequired: !!depositCalc.swap_result
    });

    // 5. Execute deposit transaction
    const tx = new Transaction();
    const lpToken = await vaults.deposit({
      vault_id: vaultId,
      slippage: 0.01,
      deposit_result: depositCalc,
      return_coin: true
    }, tx);

    console.log('Deposit transaction prepared');
    // Note: You would sign and execute the transaction here

  } catch (error) {
    console.error('Error in vault operations:', error);
  }
}
```

### Deposit and Withdrawal Operations

```typescript
async function depositWithdrawExample() {
  // Initialize SDK
  const suiClient = new SuiClient({ url: 'https://sui-mainnet.blockvision.org' });
  const vaults = Vaults.createSDK({
    client: suiClient
  });

  const vaultId = 'your-vault-id';
  const slippage = 0.01; // 1% slippage tolerance

  try {
    // === DEPOSIT OPERATIONS ===
    
    // 1. One-sided deposit (provide only token A)
    console.log('Calculating one-sided deposit...');
    const oneSidedDeposit = await vaults.calculateDepositAmount({
      vault_id: vaultId,
      is_amount_a: true,
      input_amount: '1000000', // 1 token A (assuming 6 decimals)
      slippage,
      side: InputType.OneSide
    });

    console.log('One-sided deposit calculation:', {
      inputAmount: oneSidedDeposit.original_input_amount,
      amountA: oneSidedDeposit.amount_a,
      amountB: oneSidedDeposit.amount_b,
      lpTokens: oneSidedDeposit.ft_amount,
      swapRequired: !!oneSidedDeposit.swap_result
    });

    // Execute one-sided deposit
    const depositTx = new Transaction();
    await vaults.deposit({
      vault_id: vaultId,
      slippage,
      deposit_result: oneSidedDeposit,
      return_coin: false // Transfer LP tokens to user
    }, depositTx);

    console.log('One-sided deposit transaction prepared');

    // 2. Both-sided deposit (provide both tokens)
    console.log('Calculating both-sided deposit...');
    const bothSidedDeposit = await vaults.calculateDepositAmount({
      vault_id: vaultId,
      is_amount_a: true,
      input_amount: '1000000', // 1 token A
      slippage,
      side: InputType.Both
    });

    console.log('Both-sided deposit calculation:', {
      amountA: bothSidedDeposit.amount_a,
      amountB: bothSidedDeposit.amount_b,
      lpTokens: bothSidedDeposit.ft_amount
    });

    // Execute both-sided deposit
    const bothDepositTx = new Transaction();
    await vaults.deposit({
      vault_id: vaultId,
      slippage,
      deposit_result: bothSidedDeposit,
      return_coin: false
    }, bothDepositTx);

    console.log('Both-sided deposit transaction prepared');

    // === WITHDRAWAL OPERATIONS ===

    // 1. Both-sided withdrawal (receive both tokens)
    console.log('Calculating both-sided withdrawal...');
    const bothSidedWithdraw = await vaults.calculateWithdrawAmount({
      vault_id: vaultId,
      is_amount_a: true,
      is_ft_input: true,
      input_amount: oneSidedDeposit.ft_amount, // Withdraw all LP tokens
      max_ft_amount: oneSidedDeposit.ft_amount,
      slippage,
      side: InputType.Both
    });

    console.log('Both-sided withdrawal calculation:', {
      burnAmount: bothSidedWithdraw.burn_ft_amount,
      receiveA: bothSidedWithdraw.amount_a,
      receiveB: bothSidedWithdraw.amount_b
    });

    // Execute both-sided withdrawal
    const withdrawTx = new Transaction();
    await vaults.withdraw({
      vault_id: vaultId,
      ft_amount: bothSidedWithdraw.burn_ft_amount,
      slippage,
      return_coin: false
    }, withdrawTx);

    console.log('Both-sided withdrawal transaction prepared');

    // 2. One-sided withdrawal (receive only token A)
    console.log('Calculating one-sided withdrawal...');
    const oneSidedWithdraw = await vaults.calculateWithdrawAmount({
      vault_id: vaultId,
      is_amount_a: true,
      is_ft_input: false,
      input_amount: '1000000', // input vault token amount 
      max_ft_amount: oneSidedDeposit.ft_amount,
      slippage,
      side: InputType.OneSide
    });

    console.log('One-sided withdrawal calculation:', {
      burnAmount: oneSidedWithdraw.burn_ft_amount,
      receiveA: oneSidedWithdraw.amount_a,
      receiveB: oneSidedWithdraw.amount_b,
      swapRequired: !!oneSidedWithdraw.swap_result
    });

    // Execute one-sided withdrawal
    const oneSideWithdrawTx = new Transaction();
    await vaults.withdraw({
      vault_id: vaultId,
      is_amount_a: true,
      is_ft_input: false,
      input_amount: '1000000', // input vault token amount
      max_ft_amount: oneSidedDeposit.ft_amount,
      slippage,
      return_coin: false
    }, oneSideWithdrawTx);

    console.log('One-sided withdrawal transaction prepared');

    // === ADVANCED: Using coin objects ===

    // Deposit with specific coin objects
    const advancedDepositTx = new Transaction();
    const coinA = advancedDepositTx.splitCoins(advancedDepositTx.gas, [1000000]);
    const coinB = advancedDepositTx.splitCoins(advancedDepositTx.gas, [1000000]);

    await vaults.deposit({
      vault_id: vaultId,
      coin_object_a: coinA,
      coin_object_b: coinB,
      slippage,
      deposit_result: bothSidedDeposit,
      return_coin: true // Return LP token object
    }, advancedDepositTx);

    console.log('Advanced deposit with coin objects prepared');

  } catch (error) {
    console.error('Error in deposit/withdraw operations:', error);
  }
}
```

### Administrative Operations

```typescript
async function adminOperations() {
  const admin = Admin.createSDK({
    fullNodeUrl: 'https://sui-mainnet.blockvision.org',
    senderAddress: 'admin-wallet-address'
  });

  try {
    // Create a new stable vault
    const tx = await admin.newStableVault({
      pool_id: 'clmm-pool-id',
      treasury_cap_id: 'treasury-cap-id',
      upper_price_scalling: '1.1', // 10% above current price
      lower_price_scalling: '0.9', // 10% below current price
      slippage_up: '0.02', // 2% slippage tolerance up
      slippage_down: '0.02', // 2% slippage tolerance down
      free_threshold_a: '1000000', // 1 token A threshold
      free_threshold_b: '1000000', // 1 token B threshold
      fee_val: '0.003', // 0.3% fee
      withdraw_fee_val: '0.001', // 0.1% withdrawal fee
      decimals_a: 6,
      decimals_b: 6,
      deposit_limit: '1000000000', // 1000 tokens limit
      coin_type_a: '0x2::sui::SUI',
      coin_type_b: '0x...::usdc::USDC',
      coin_type_token: '0x...::lp_token::LP_TOKEN'
    });

    console.log('Stable vault creation transaction prepared');

    // Configure vault settings
    const configTx = new Transaction();
    await admin.setDepositLimit('vault-id', 'vault-cap', 2000000000n, configTx);
    // await admin.setSlippage('risk-admin-cap', 'vault-id', '0.03', '0.03', configTx);

    console.log('Vault configuration transaction prepared');

  } catch (error) {
    console.error('Error in admin operations:', error);
  }
}
```

### Monitoring Vault Performance

```typescript
async function monitorVaultPerformance() {
  const suiClient = new SuiClient({ url: 'https://sui-mainnet.blockvision.org' });
  const vaults = Vaults.createSDK({
    client: suiClient
  });

  const userAddress = 'user-wallet-address';

  try {
    // Get user's positions across all vaults
    const userBalances = await vaults.getOwnerVaultsBalance(userAddress);
    
    console.log(`User has positions in ${userBalances.length} vaults:`);
    
    for (const balance of userBalances) {
      console.log(`
Vault: ${balance.vault_id}
LP Token Balance: ${balance.lp_token_balance}
Underlying Token A: ${balance.amount_a} (${balance.coin_type_a})
Underlying Token B: ${balance.amount_b} (${balance.coin_type_b})
Position Range: ${balance.tick_lower_index} to ${balance.tick_upper_index}
      `);
    }

    // Get detailed vault information
    for (const balance of userBalances) {
      const vault = await vaults.getVault(balance.vault_id);
      if (vault) {
        console.log(`
Vault ${vault.id} Details:
- Type: ${vault.config_type}
- Total Supply: ${vault.total_supply}
- Deposit Enabled: ${vault.is_deposit_enabled}
- Locked: ${vault.is_lock}
- Fee: ${vault.fee_val}
- Withdraw Fee: ${vault.withdraw_fee_val}
- Last Rebalance: ${new Date(parseInt(vault.last_rebalance_time) * 1000).toISOString()}
        `);
      }
    }

  } catch (error) {
    console.error('Error monitoring vault performance:', error);
  }
}
```

## API Reference

### Vaults Class

The main class for interacting with vaults.

#### Creating an Instance

```typescript
const vaults = Vaults.createSDK({
  client: suiClient           // SuiClient instance
});

// Or with custom RPC URL
const vaults = Vaults.createSDK({
  fullNodeUrl: 'https://custom-rpc-url'
});
```

#### Core Methods

##### `getVaultList()`
Retrieves all available vaults.

```typescript
const vaults = await sdk.getVaultList();
```

##### `getVault(id: string)`
Get detailed information about a specific vault.

```typescript
const vault = await sdk.getVault('vault-id');
```

##### `getOwnerVaultsBalance(address: string)`
Get user's balances across all vaults.

```typescript
const balances = await sdk.getOwnerVaultsBalance('user-address');
```

##### `getOwnerVaultBalance(address: string, vaultId: string)`
Get a user's balance for a specific vault including LP token decimals.

```typescript
const balance = await sdk.getOwnerVaultBalance('user-address', 'vault-id');
```

##### `getPositionAssets(vaultId: string)`
Get position token amounts with current prices and USD values for a specific vault.

```typescript
const positionAssets = await sdk.getPositionAssets('vault-id');
console.log('Position Assets:', {
  amountA: positionAssets.amount_a,
  amountB: positionAssets.amount_b,
  priceA: positionAssets.price_a,
  priceB: positionAssets.price_b,
  totalValueUSD: positionAssets.total_value_usd,
  lpUsdPrice: positionAssets.lp_usd_price
});
```

##### `getRemainingCap(vaultId: string)`
Get remaining deposit capacity for a specific vault.

```typescript
const remainingCap = await sdk.getRemainingCap('vault-id');
console.log('Remaining Capacity:', {
  depositLimitUSD: remainingCap.deposit_limit_usd,
  currentPositionValueUSD: remainingCap.current_position_value_usd,
  remainingCapUSD: remainingCap.remaining_cap_usd
});
```

##### `getVaultMarketHolders(vaultId?: string)`
Get all vault market holders with their aggregated USD values across all vault markets.

```typescript
const holders = await sdk.getVaultMarketHolders('vault-id');
console.log('Vault Market Holders:', holders);
// Output: [
//   { address: '0x123...', usdValue: '100.5000000000000000' },
//   { address: '0x456...', usdValue: '210.7500000000000000' }
// ]
```

##### `getVaultStatus()`
Get the current status of all vaults including rebalancing status and configuration details.

```typescript
const vaultStatus = await sdk.getVaultStatus();
console.log('Vault Status:', vaultStatus);
// Output: [
//   {
//     vaultId: '0x123...',
//     isRebalancing: false,
//     vaultName: 'SUI-USDC Vault',
//     disable: false,
//   }
// ]
```

#### Deposit Operations

##### `calculateDepositAmount(params)`
Calculate optimal deposit amounts and required swaps.

```typescript
const depositCalc = await sdk.calculateDepositAmount({
  vault_id: 'vault-id',
  is_amount_a: true,
  input_amount: '1000000',
  slippage: 0.01,
  side: InputType.OneSide
});
```

##### `deposit(params, tx)`
Execute a deposit transaction.

```typescript
import { Transaction } from '@mysten/sui/transactions';

const tx = new Transaction();
const result = await sdk.deposit({
  vault_id: 'vault-id',
  slippage: 0.01,
  deposit_result: depositCalc,
  return_coin: true
}, tx);
```

#### Withdrawal Operations

##### `calculateWithdrawAmount(params)`
Calculate withdrawal amounts and required swaps.

```typescript
const withdrawCalc = await sdk.calculateWithdrawAmount({
  vault_id: 'vault-id',
  is_amount_a: true,
  is_ft_input: true,
  input_amount: '1000000',
  max_ft_amount: '2000000',
  slippage: 0.01,
  side: InputType.OneSide
});
```

##### `withdraw(params, tx)`
Execute a withdrawal transaction.

```typescript
const tx = new Transaction();
const result = await sdk.withdraw({
  vault_id: 'vault-id',
  ft_amount: '1000000',
  slippage: 0.01,
  return_coin: true
}, tx);
```

### Admin Class

Administrative functions for vault management.

#### Creating an Instance

```typescript
const admin = Admin.createSDK({
  fullNodeUrl: 'https://sui-mainnet.blockvision.org',
  senderAddress: 'admin-address'
});
```

#### Vault Creation

##### `newStableVault(params)`
Create a new stable vault.

```typescript
const tx = await admin.newStableVault({
  pool_id: 'clmm-pool-id',
  treasury_cap_id: 'treasury-cap-id',
  upper_price_scalling: '1.1',
  lower_price_scalling: '0.9',
  slippage_up: '0.01',
  slippage_down: '0.01',
  free_threshold_a: '1000000',
  free_threshold_b: '1000000',
  fee_val: '0.003',
  withdraw_fee_val: '0.001',
  decimals_a: 6,
  decimals_b: 6,
  deposit_limit: '1000000000',
  coin_type_a: 'coin-type-a',
  coin_type_b: 'coin-type-b',
  coin_type_token: 'lp-token-type'
});
```

##### `newUncorrelatedVault(params)`
Create a new uncorrelated vault with rebalancing triggers.

```typescript
const tx = await admin.newUncorrelatedVault({
  // ... similar params as stable vault
  lock_threshold_a: '5000000',
  lock_threshold_b: '5000000',
  target_adapter: 'price-adapter',
  is_target_reverse: false
});
```

#### Vault Configuration

##### `setDepositLimit(vaultId, vaultCap, limit)`
Update vault deposit limits.

```typescript
await admin.setDepositLimit('vault-id', 'vault-cap', 1000000000n, tx);
```

##### `setFee(adminCap, vaultId, feeVal)`
Set vault fee percentages.

```typescript
await admin.setFee('admin-cap', 'vault-id', '0.003', tx);
```

## Types and Interfaces

### Vault Interface

```typescript
interface Vault {
  id: string;
  clmm_pool_id: string;
  coin_type_a: string;
  coin_type_b: string;
  lp_token_type: string;
  config_type: string;
  total_supply: string;
  deposit_limit: string;
  is_deposit_enabled: boolean;
  is_lock: boolean;
  // ... additional properties
}
```

### Calculation Parameters

```typescript
interface CalculateAmountParams {
  vault_id: string;
  is_amount_a: boolean;
  input_amount: string;
  slippage: number;
  side: InputType;
}

enum InputType {
  Both = 'both',
  OneSide = 'oneSide'
}
```

## Automated Bots

The SDK includes two automated bots for production use:

### Reward Bot

Automatically collects and compounds vault rewards.

```bash
# Run directly
pnpm run run:reward

# Or with Docker
docker-compose up reward-bot
```

### Rebalance Bot

Monitors and rebalances uncorrelated vaults when price thresholds are reached.

```bash
# Run directly  
pnpm run run:rebalance

# Or with Docker
docker-compose up rebalance-bot
```

## Environment Configuration

Create a `.env` file with the following variables:

```env
# Sui Network
SUI_PRIVATE_KEY=your-base64-encoded-private-key
FULL_NODE_URL=https://sui-mainnet.blockvision.org

# Slack Notifications (for bots)
SLACK_TOKEN=xoxb-your-slack-bot-token
SLACK_CONVERSATION_ID=your-channel-id
SLACK_VAULT_REBALANCE_CONVERSATION_ID=rebalance-channel-id

# Optional
NODE_ENV=production
```

## Docker Deployment

The SDK provides ready-to-use Docker containers:

```bash
# Build and run both bots
docker-compose up -d

# Run specific bot
docker-compose up -d reward-bot
docker-compose up -d rebalance-bot

# View logs
docker-compose logs -f reward-bot
```

See [DOCKER.md](./DOCKER.md) for detailed Docker setup instructions.

## Error Handling

The SDK uses a comprehensive error handling system:

```typescript
import { VaultsErrorCode } from '@nemoprotocol/vaults-sdk';

try {
  const vault = await vaults.getVault('invalid-id');
} catch (error) {
  if (error.code === VaultsErrorCode.ObjectNotFound) {
    console.log('Vault not found');
  }
}
```

## Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests
5. Submit a pull request

## License

MIT License

## Support

For issues and questions:
- Create an issue on GitHub
- Check the [API documentation](./docs/api.md)

## Development Setup

```bash
# Install dependencies
pnpm install

# Build the SDK
pnpm run build

# Run tests
pnpm test

# Build bots
pnpm run build:all
```