@nemoprotocol/vaults-sdk
Version:
A TypeScript SDK for interacting with Nemo Vaults on the Sui blockchain
758 lines (601 loc) • 19.9 kB
Markdown
# 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' }
// ]
```
#### 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
```