UNPKG

@nemoprotocol/vaults-sdk

Version:

A TypeScript SDK for interacting with Nemo Vaults on the Sui blockchain

774 lines (615 loc) 20.2 kB
# 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 ```