JavaScript SDK

The official JavaScript/TypeScript SDK for Neo Service Layer. Build powerful dApps with a simple, intuitive API that abstracts blockchain complexity.

npm install @neo-service-layer/sdk TypeScript Support Node.js & Browser

Installation

Install the Neo Service Layer SDK using your preferred package manager:

Terminal
npm install @neo-service-layer/sdk
Terminal
yarn add @neo-service-layer/sdk
Terminal
pnpm add @neo-service-layer/sdk

Requirements: Node.js 16+ or modern browser with ES2020 support

Quick Start

Get up and running with Neo Service Layer in just a few lines of code:

main.js
import { NeoServiceLayer } from '@neo-service-layer/sdk';

// Initialize the SDK
const nsl = new NeoServiceLayer({
  network: 'testnet', // or 'mainnet'
  privateKey: process.env.PRIVATE_KEY, // Optional: for transactions
  rpcEndpoint: 'https://testnet1.neo.coz.io:443' // Optional: custom RPC
});

// Example: Store data with encryption
async function storeUserData() {
  try {
    const result = await nsl.storage.store({
      key: 'user-profile-123',
      value: JSON.stringify({
        name: 'Alice',
        email: 'alice@example.com',
        preferences: { theme: 'dark', language: 'en' }
      }),
      encrypted: true,
      ttl: 86400 // 24 hours
    });
    
    console.log('Data stored successfully!');
    console.log('Transaction ID:', result.txid);
    console.log('Storage Key:', result.key);
    
    return result;
  } catch (error) {
    console.error('Failed to store data:', error.message);
    throw error;
  }
}

// Example: Retrieve stored data
async function getUserData(key) {
  try {
    const data = await nsl.storage.get(key);
    console.log('Retrieved data:', JSON.parse(data.value));
    return data;
  } catch (error) {
    console.error('Failed to retrieve data:', error.message);
    throw error;
  }
}

// Run the example
storeUserData()
  .then(result => getUserData(result.key))
  .catch(console.error);

Configuration

Configure the SDK with various options to suit your application needs:

config.js
const config = {
  // Network configuration
  network: 'testnet', // 'mainnet' | 'testnet' | 'private'
  
  // RPC endpoint (optional - uses default if not provided)
  rpcEndpoint: 'https://testnet1.neo.coz.io:443',
  
  // Account configuration
  privateKey: process.env.PRIVATE_KEY, // WIF format
  // OR use wallet file
  walletPath: './wallet.json',
  walletPassword: process.env.WALLET_PASSWORD,
  
  // Service-specific configurations
  services: {
    storage: {
      defaultEncryption: true,
      compressionLevel: 6,
      maxValueSize: 1024 * 1024 // 1MB
    },
    
    crossChain: {
      confirmations: {
        ethereum: 12,
        bsc: 20,
        polygon: 64
      },
      timeouts: {
        bridge: 300000, // 5 minutes
        verify: 60000   // 1 minute
      }
    },
    
    oracle: {
      updateInterval: 30000, // 30 seconds
      sources: ['chainlink', 'band', 'api3'],
      aggregationMethod: 'median'
    }
  },
  
  // Performance options
  performance: {
    cacheEnabled: true,
    cacheTTL: 300000, // 5 minutes
    requestTimeout: 30000, // 30 seconds
    retryAttempts: 3,
    retryDelay: 1000 // 1 second
  },
  
  // Development options
  debug: process.env.NODE_ENV === 'development',
  logLevel: 'info', // 'error' | 'warn' | 'info' | 'debug'
  
  // Security options
  security: {
    validateResponses: true,
    requireSSL: true,
    allowSelfSignedCerts: false
  }
};

const nsl = new NeoServiceLayer(config);

Environment Variables

Set up your environment variables for secure configuration:

.env
# Neo Service Layer Configuration
NEO_NETWORK=testnet
NEO_PRIVATE_KEY=your_private_key_in_wif_format
NEO_RPC_ENDPOINT=https://testnet1.neo.coz.io:443

# Optional: Wallet file configuration
NEO_WALLET_PATH=./wallet.json
NEO_WALLET_PASSWORD=your_wallet_password

# API Keys (if using external services)
ETHEREUM_RPC_URL=https://mainnet.infura.io/v3/your_key
BSC_RPC_URL=https://bsc-dataseed.binance.org/
POLYGON_RPC_URL=https://polygon-rpc.com/

# Development
NODE_ENV=development
LOG_LEVEL=debug

Storage Service

The Storage Service provides secure, encrypted data storage on the blockchain with optional compression and TTL support.

Basic Operations

storage-examples.js
// Store data with various options
async function storageExamples() {
  
  // 1. Simple key-value storage
  await nsl.storage.store({
    key: 'simple-key',
    value: 'Hello, Neo Service Layer!'
  });
  
  // 2. Store JSON data with encryption
  await nsl.storage.store({
    key: 'user-config',
    value: JSON.stringify({
      theme: 'dark',
      notifications: true,
      language: 'en'
    }),
    encrypted: true
  });
  
  // 3. Store with TTL (time-to-live)
  await nsl.storage.store({
    key: 'session-data',
    value: JSON.stringify({ sessionId: '123', userId: 'alice' }),
    encrypted: true,
    ttl: 3600 // Expires in 1 hour
  });
  
  // 4. Store large data with compression
  await nsl.storage.store({
    key: 'large-dataset',
    value: JSON.stringify(largeDataObject),
    compressed: true,
    encrypted: true
  });
  
  // 5. Store with metadata
  await nsl.storage.store({
    key: 'document-123',
    value: documentContent,
    metadata: {
      contentType: 'application/pdf',
      version: '1.0',
      author: 'alice@example.com',
      tags: ['document', 'important']
    },
    encrypted: true
  });
}

// Retrieve data
async function retrieveExamples() {
  
  // Get simple data
  const simpleData = await nsl.storage.get('simple-key');
  console.log('Simple data:', simpleData.value);
  
  // Get encrypted data (automatically decrypted)
  const configData = await nsl.storage.get('user-config');
  const config = JSON.parse(configData.value);
  console.log('User config:', config);
  
  // Get with metadata
  const docData = await nsl.storage.get('document-123');
  console.log('Document metadata:', docData.metadata);
  console.log('Content type:', docData.metadata.contentType);
  
  // Check if key exists
  const exists = await nsl.storage.exists('user-config');
  console.log('Config exists:', exists);
  
  // List keys with pattern
  const userKeys = await nsl.storage.listKeys('user-*');
  console.log('User keys:', userKeys);
}

// Advanced operations
async function advancedStorageOperations() {
  
  // Batch operations
  const batchOps = [
    { operation: 'store', key: 'batch-1', value: 'Value 1' },
    { operation: 'store', key: 'batch-2', value: 'Value 2' },
    { operation: 'store', key: 'batch-3', value: 'Value 3' }
  ];
  
  await nsl.storage.batch(batchOps);
  
  // Update existing data
  await nsl.storage.update({
    key: 'user-config',
    value: JSON.stringify({
      theme: 'light', // Changed
      notifications: true,
      language: 'en',
      lastModified: new Date().toISOString()
    })
  });
  
  // Delete data
  await nsl.storage.delete('session-data');
  
  // Get storage info
  const info = await nsl.storage.getInfo();
  console.log('Storage stats:', info);
}

Error Handling

storage-error-handling.js
import { NeoServiceLayerError, StorageError } from '@neo-service-layer/sdk';

async function safeStorageOperations() {
  try {
    // Store operation with proper error handling
    const result = await nsl.storage.store({
      key: 'important-data',
      value: JSON.stringify(criticalData),
      encrypted: true
    });
    
    console.log('Storage successful:', result.txid);
    
  } catch (error) {
    // Handle specific error types
    if (error instanceof StorageError) {
      switch (error.code) {
        case 'STORAGE_QUOTA_EXCEEDED':
          console.error('Storage quota exceeded. Consider upgrading plan.');
          break;
          
        case 'ENCRYPTION_FAILED':
          console.error('Failed to encrypt data. Check encryption settings.');
          break;
          
        case 'KEY_ALREADY_EXISTS':
          console.error('Key already exists. Use update() instead.');
          break;
          
        case 'INVALID_KEY_FORMAT':
          console.error('Key format is invalid. Use alphanumeric characters.');
          break;
          
        default:
          console.error('Storage error:', error.message);
      }
    } else if (error instanceof NeoServiceLayerError) {
      // Handle general SDK errors
      console.error('SDK error:', error.message);
      console.error('Error code:', error.code);
    } else {
      // Handle unexpected errors
      console.error('Unexpected error:', error);
    }
  }
}

// Retry mechanism for failed operations
async function storeWithRetry(key, value, maxRetries = 3) {
  let lastError;
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const result = await nsl.storage.store({ key, value, encrypted: true });
      console.log(`Storage successful on attempt ${attempt}`);
      return result;
      
    } catch (error) {
      lastError = error;
      console.warn(`Attempt ${attempt} failed:`, error.message);
      
      if (attempt < maxRetries) {
        // Exponential backoff
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }
  }
  
  throw new Error(`Failed after ${maxRetries} attempts: ${lastError.message}`);
}

Cross-Chain Bridge

Bridge assets and data between Neo and other blockchain networks with built-in security and monitoring.

cross-chain-examples.js
// Bridge assets from Ethereum to Neo
async function bridgeFromEthereum() {
  try {
    // Initiate bridge transfer
    const bridgeRequest = await nsl.crossChain.bridge({
      from: 'ethereum',
      to: 'neo',
      asset: 'USDT',
      amount: '1000.50',
      recipient: 'NXXXXXXXXXAddress', // Neo address
      
      // Source transaction details
      sourceChain: {
        network: 'mainnet',
        contractAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT
        gasPrice: '20000000000', // 20 gwei
        gasLimit: '100000'
      },
      
      // Bridge options
      options: {
        confirmations: 12, // Wait for 12 confirmations
        timeout: 300000,   // 5 minute timeout
        slippage: 0.5      // 0.5% slippage tolerance
      }
    });
    
    console.log('Bridge initiated:', bridgeRequest.bridgeId);
    console.log('Estimated completion:', bridgeRequest.estimatedTime);
    
    // Monitor bridge progress
    return await monitorBridge(bridgeRequest.bridgeId);
    
  } catch (error) {
    console.error('Bridge failed:', error.message);
    throw error;
  }
}

// Monitor bridge status
async function monitorBridge(bridgeId) {
  return new Promise((resolve, reject) => {
    const checkStatus = async () => {
      try {
        const status = await nsl.crossChain.getStatus(bridgeId);
        
        console.log(`Bridge ${bridgeId} status: ${status.state}`);
        console.log(`Progress: ${status.confirmations}/${status.requiredConfirmations}`);
        
        switch (status.state) {
          case 'pending':
            console.log('Waiting for source chain confirmations...');
            setTimeout(checkStatus, 30000); // Check every 30 seconds
            break;
            
          case 'confirmed':
            console.log('Source transaction confirmed, processing...');
            setTimeout(checkStatus, 15000); // Check every 15 seconds
            break;
            
          case 'completed':
            console.log('Bridge completed successfully!');
            console.log('Destination txid:', status.destinationTxid);
            resolve(status);
            break;
            
          case 'failed':
            console.error('Bridge failed:', status.error);
            reject(new Error(status.error));
            break;
            
          default:
            console.log('Unknown status:', status.state);
            setTimeout(checkStatus, 30000);
        }
        
      } catch (error) {
        console.error('Status check failed:', error.message);
        setTimeout(checkStatus, 60000); // Retry in 1 minute
      }
    };
    
    checkStatus();
  });
}

// Bridge from Neo to other chains
async function bridgeFromNeo() {
  const bridgeRequest = await nsl.crossChain.bridge({
    from: 'neo',
    to: 'ethereum',
    asset: 'NEO',
    amount: '10',
    recipient: '0x742d35Cc6554C788aA7c482BfcAfDfC5c63B3aF1', // Ethereum address
    
    // Destination chain options
    destinationChain: {
      network: 'mainnet',
      gasPrice: 'fast', // or specific gas price
      gasLimit: 'auto'  // or specific limit
    }
  });
  
  return bridgeRequest;
}

// Get supported chains and assets
async function getSupportedAssets() {
  const chains = await nsl.crossChain.getSupportedChains();
  console.log('Supported chains:', chains);
  
  for (const chain of chains) {
    const assets = await nsl.crossChain.getSupportedAssets(chain);
    console.log(`Assets on ${chain}:`, assets);
  }
}

// Get bridge history
async function getBridgeHistory(address) {
  const history = await nsl.crossChain.getHistory({
    address: address,
    limit: 50,
    offset: 0,
    fromDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // Last 30 days
  });
  
  console.log('Bridge history:', history);
  return history;
}

DeFi Lending

Create and interact with lending pools, provide liquidity, and manage loans on the Neo Service Layer DeFi platform.

lending-examples.js
// Create a lending pool
async function createLendingPool() {
  try {
    const pool = await nsl.lending.createPool({
      name: 'USDT-NEO Lending Pool',
      baseAsset: 'USDT',
      collateralAsset: 'NEO',
      
      // Pool parameters
      parameters: {
        baseInterestRate: 0.05,      // 5% base APR
        utilizationRate: 0.8,        // 80% optimal utilization
        collateralRatio: 1.5,        // 150% collateral requirement
        liquidationThreshold: 1.25,  // Liquidate at 125%
        reserveFactor: 0.1           // 10% reserve factor
      },
      
      // Initial liquidity
      initialLiquidity: {
        amount: '10000', // 10,000 USDT
        provider: nsl.account.address
      }
    });
    
    console.log('Pool created:', pool.poolId);
    console.log('Pool address:', pool.contractAddress);
    
    return pool;
    
  } catch (error) {
    console.error('Failed to create pool:', error.message);
    throw error;
  }
}

// Provide liquidity to earn interest
async function provideLiquidity(poolId, amount) {
  try {
    const result = await nsl.lending.supply({
      poolId: poolId,
      asset: 'USDT',
      amount: amount,
      
      // Optional: enable as collateral
      enableCollateral: true
    });
    
    console.log('Liquidity provided:', result.txid);
    console.log('LP tokens received:', result.lpTokens);
    
    // Get updated pool info
    const poolInfo = await nsl.lending.getPoolInfo(poolId);
    console.log('Pool utilization:', poolInfo.utilizationRate);
    console.log('Current APY:', poolInfo.supplyAPY);
    
    return result;
    
  } catch (error) {
    console.error('Failed to provide liquidity:', error.message);
    throw error;
  }
}

// Borrow against collateral
async function borrowFunds(poolId, collateralAmount, borrowAmount) {
  try {
    // First, provide collateral
    const collateralResult = await nsl.lending.supply({
      poolId: poolId,
      asset: 'NEO',
      amount: collateralAmount,
      enableCollateral: true
    });
    
    console.log('Collateral provided:', collateralResult.txid);
    
    // Then borrow
    const borrowResult = await nsl.lending.borrow({
      poolId: poolId,
      asset: 'USDT',
      amount: borrowAmount,
      
      // Borrow options
      options: {
        interestRateMode: 'stable', // or 'variable'
        maxSlippage: 0.01          // 1% slippage
      }
    });
    
    console.log('Borrow successful:', borrowResult.txid);
    console.log('Interest rate:', borrowResult.interestRate);
    console.log('Health factor:', borrowResult.healthFactor);
    
    return borrowResult;
    
  } catch (error) {
    console.error('Borrow failed:', error.message);
    throw error;
  }
}

// Monitor loan health and manage positions
async function manageLoanPosition(userAddress) {
  try {
    // Get user's position across all pools
    const positions = await nsl.lending.getUserPositions(userAddress);
    
    for (const position of positions) {
      console.log(`Pool ${position.poolId}:`);
      console.log(`  Supplied: ${position.supplied}`);
      console.log(`  Borrowed: ${position.borrowed}`);
      console.log(`  Health Factor: ${position.healthFactor}`);
      console.log(`  Liquidation Price: ${position.liquidationPrice}`);
      
      // Check if position needs attention
      if (position.healthFactor < 1.3) {
        console.warn(`⚠️  Position at risk! Health factor: ${position.healthFactor}`);
        
        // Auto-repay or add collateral
        await handleRiskyPosition(position);
      }
    }
    
  } catch (error) {
    console.error('Failed to get positions:', error.message);
  }
}

// Handle risky positions
async function handleRiskyPosition(position) {
  try {
    // Option 1: Partial repayment
    const repayAmount = position.borrowed * 0.1; // Repay 10%
    
    await nsl.lending.repay({
      poolId: position.poolId,
      asset: position.borrowAsset,
      amount: repayAmount.toString(),
      repayWithCollateral: false
    });
    
    console.log(`Repaid ${repayAmount} to improve health factor`);
    
    // Option 2: Add more collateral
    // await nsl.lending.supply({...});
    
  } catch (error) {
    console.error('Failed to handle risky position:', error.message);
  }
}

// Withdraw liquidity and close positions
async function withdrawLiquidity(poolId, amount) {
  try {
    const result = await nsl.lending.withdraw({
      poolId: poolId,
      asset: 'USDT',
      amount: amount, // or 'max' to withdraw all
      
      // Withdrawal options
      options: {
        redeemType: 'underlying', // or 'aTokens'
        slippageProtection: true
      }
    });
    
    console.log('Withdrawal successful:', result.txid);
    console.log('Amount received:', result.amountReceived);
    
    return result;
    
  } catch (error) {
    console.error('Withdrawal failed:', error.message);
    throw error;
  }
}

Error Handling

Comprehensive error handling strategies for robust applications.

error-handling.js
import { 
  NeoServiceLayerError, 
  NetworkError, 
  ValidationError,
  InsufficientFundsError,
  ContractError 
} from '@neo-service-layer/sdk';

// Comprehensive error handling
async function robustServiceCall() {
  try {
    const result = await nsl.storage.store({
      key: 'test-key',
      value: 'test-value',
      encrypted: true
    });
    
    return result;
    
  } catch (error) {
    // Handle specific error types
    if (error instanceof ValidationError) {
      console.error('Validation failed:', error.details);
      // Show user-friendly validation messages
      showValidationErrors(error.details);
      
    } else if (error instanceof InsufficientFundsError) {
      console.error('Insufficient funds:', error.required, error.available);
      // Prompt user to add funds
      promptAddFunds(error.required - error.available);
      
    } else if (error instanceof NetworkError) {
      console.error('Network error:', error.message);
      // Retry with exponential backoff
      return await retryWithBackoff(() => robustServiceCall(), 3);
      
    } else if (error instanceof ContractError) {
      console.error('Contract execution failed:', error.message);
      console.error('Gas used:', error.gasUsed);
      // Handle contract-specific errors
      handleContractError(error);
      
    } else if (error instanceof NeoServiceLayerError) {
      console.error('SDK error:', error.code, error.message);
      // Log for debugging
      logError(error);
      
    } else {
      console.error('Unexpected error:', error);
      // Report to error tracking service
      reportError(error);
    }
    
    throw error; // Re-throw if needed
  }
}

// Retry mechanism with exponential backoff
async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) {
  let lastError;
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      
      if (attempt === maxRetries) {
        break; // Don't delay on the last attempt
      }
      
      // Exponential backoff with jitter
      const delay = baseDelay * Math.pow(2, attempt - 1);
      const jitter = Math.random() * 0.1 * delay;
      await new Promise(resolve => setTimeout(resolve, delay + jitter));
      
      console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
    }
  }
  
  throw lastError;
}

// Global error handler setup
function setupGlobalErrorHandling() {
  // Set global error handler for the SDK
  nsl.onError((error, context) => {
    console.error(`SDK Error in ${context.service}.${context.method}:`, error);
    
    // Send to monitoring service
    if (typeof window !== 'undefined' && window.analytics) {
      window.analytics.track('SDK Error', {
        service: context.service,
        method: context.method,
        error: error.message,
        code: error.code
      });
    }
  });
  
  // Set up transaction failure handler
  nsl.onTransactionFailed((txid, error, context) => {
    console.error(`Transaction ${txid} failed:`, error);
    
    // Notify user of transaction failure
    showNotification({
      type: 'error',
      title: 'Transaction Failed',
      message: `Your ${context.operation} transaction failed: ${error.message}`,
      actions: [
        { label: 'Retry', action: () => retryTransaction(context) },
        { label: 'View Details', action: () => showTransactionDetails(txid) }
      ]
    });
  });
}

// Error recovery strategies
class ErrorRecoveryManager {
  constructor(nsl) {
    this.nsl = nsl;
    this.retryQueues = new Map();
  }
  
  async executeWithRecovery(operation, options = {}) {
    const {
      maxRetries = 3,
      retryDelay = 1000,
      fallbackStrategy = null,
      criticalOperation = false
    } = options;
    
    try {
      return await this.retryWithBackoff(operation, maxRetries, retryDelay);
      
    } catch (error) {
      if (fallbackStrategy) {
        console.warn('Primary operation failed, trying fallback...');
        return await fallbackStrategy();
      }
      
      if (criticalOperation) {
        // Queue for later retry
        this.queueForRetry(operation, options);
      }
      
      throw error;
    }
  }
  
  queueForRetry(operation, options) {
    const queueId = `retry-${Date.now()}-${Math.random()}`;
    this.retryQueues.set(queueId, { operation, options, attempts: 0 });
    
    // Process queue periodically
    setTimeout(() => this.processRetryQueue(), 60000); // 1 minute
  }
  
  async processRetryQueue() {
    for (const [queueId, item] of this.retryQueues.entries()) {
      if (item.attempts < 10) { // Max queue attempts
        try {
          await item.operation();
          this.retryQueues.delete(queueId);
          console.log(`Queued operation ${queueId} succeeded`);
        } catch (error) {
          item.attempts++;
          console.warn(`Queued operation ${queueId} failed, attempt ${item.attempts}`);
        }
      } else {
        this.retryQueues.delete(queueId);
        console.error(`Queued operation ${queueId} permanently failed`);
      }
    }
  }
}