JavaScript SDK
The official JavaScript/TypeScript SDK for Neo Service Layer. Build powerful dApps with a simple, intuitive API that abstracts blockchain complexity.
Installation
Install the Neo Service Layer SDK using your preferred package manager:
npm install @neo-service-layer/sdk
yarn add @neo-service-layer/sdk
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:
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:
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:
# 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
// 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
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.
// 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.
// 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.
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`);
}
}
}
}