lynx-client
v0.0.34
Published
Client library for interacting with Lynx publicly available services
Readme
Lynx Client SDK
A TypeScript SDK for interacting with the Lynx trading protocol across multiple EVM-compatible blockchains.
Features
- 🔒 Type-safe interfaces for all Lynx smart contracts
- 🌐 Multi-chain support (16+ EVM chains)
- 📊 Built-in utilities for price, leverage, and fraction conversions
- 🏗️ Support for both CommonJS and ESM module systems
- 🔍 Read-only lens contracts for efficient data aggregation
- 💼 Complete trading lifecycle management
Installation
npm install lynx-clientyarn add lynx-clientQuick Start
import {
connectToLatestRegistry,
LynxSystemAddresses,
createTradersPortalService,
buildPositionRequestIdentifier,
buildPositionRequestParams
} from 'lynx-client';
import { ethers } from 'ethers';
// Initialize provider
const provider = new ethers.JsonRpcProvider('YOUR_RPC_URL');
const signer = await provider.getSigner();
// Get chain-specific addresses
const addresses = LynxSystemAddresses.arbitrum; // or .sonic, .base, etc.
// Connect to contracts
const registry = connectToLatestRegistry(provider, addresses.registryProxyAddress);
// Create a service for trader operations
const tradersPortal = createTradersPortalService(signer, addresses.tradersPortalAddress);Core Concepts
Supported Chains
Engine Chains
The Lynx protocol is deployed on the following chains:
- Sonic (146)
- Boba (288)
- Flare (14)
- Fantom (250)
Source Chains
Assets can be bridged from these additional chains:
- Ethereum Mainnet (1)
- Optimism (10)
- BSC (56)
- Polygon (137)
- Fuse (122)
- Goat (2345)
- Mantle (5000)
- Base (8453)
- Mode (34443)
- Arbitrum (42161)
- Celo (42220)
- Linea (59144)
- Zircuit (48900)
import { LynxSystemAddresses, TSupportedChainIds } from 'lynx-client';
// Get addresses for a specific chain
const sonicAddresses = LynxSystemAddresses.sonic;
const arbitrumAddresses = LynxSystemAddresses.arbitrum;Contract Services
The SDK provides service wrappers for all Lynx contracts, organized using a consistent grouped interface pattern:
Service Architecture
All services follow a grouped interface pattern for better organization:
// Example: TradingFloor Service Structure
const tradingFloor = createTradingFloorService(signer, address);
// Grouped by functionality
tradingFloor.positionQueries // Position-related queries
tradingFloor.settlementAndPool // Settlement and pool operations
tradingFloor.traderTracking // Trader tracking functions
tradingFloor.tradingParameters // Trading parameters
tradingFloor.fees // Fee information
tradingFloor.deprecated // Deprecated functions
// Standardized accessors
tradingFloor.pendableAdmin // Admin functions
tradingFloor.pendableImplementation // Implementation functions
tradingFloor.lynxVersionedContract // Version information
tradingFloor.contractReferences // Other contract addresses
tradingFloor.constants // Protocol constants// Example: OnBehalfTrading Service Structure
const onBehalfTrading = new OnBehalfTradingService(signer, address);
// Grouped by functionality
onBehalfTrading.permissionQueries // Permission validation and queries
onBehalfTrading.permissionManagement // Grant/revoke permissions
onBehalfTrading.configuration // Contract configuration
onBehalfTrading.adminFunctions // Admin operations
// Standardized accessors
onBehalfTrading.pendableAdmin // Admin functions
onBehalfTrading.lynxVersionedContract // Version information
onBehalfTrading.contractReferences // Other contract addressesTrading Operations
import {
createTradersPortalService,
buildPositionRequestIdentifier,
buildPositionRequestParams,
TOpenOrderType
} from 'lynx-client';
const tradersPortal = createTradersPortalService(signer, tradersPortalAddress);
// Build position parameters using human-readable values
const identifier = buildPositionRequestIdentifier({
trader: await signer.getAddress(),
pairId: 1, // BTC/USD
settlementAsset: "0x...", // USDC address
});
const params = buildPositionRequestParams({
isLong: true,
collateral: 100, // 100 USDC
leverage: 25, // 25x leverage
minPrice: 50000, // $50,000
maxPrice: 50100, // $50,100
takeProfit: 55000, // $55,000
stopLoss: 48000 // $48,000
});
// Open a position
await tradersPortal.traderRequests.openNewPosition(
identifier,
params,
TOpenOrderType.MARKET,
"0x", // referrer domain
"0x", // referrer code
true, // run cap tests
ethers.parseEther("0.01") // native fee
);Lens Services (Read-Only Data)
import {
createLexLensService,
createTradingFloorLensService,
RateModelLensService
} from 'lynx-client';
// LEX Pool data (accepts both provider and signer)
const lexLens = createLexLensService(signerOrProvider, lexLensAddress);
const poolState = await lexLens.getLexPoolState(lexPoolAddress);
const pairConfigs = await lexLens.getAllPairConfigurationsInLex(lexPoolAddress);
// Trading floor data (accepts both provider and signer)
const tradingFloorLens = createTradingFloorLensService(signerOrProvider, tradingFloorLensAddress);
const position = await tradingFloorLens.getCompletePositionData(tradingFloorAddress, positionId);
// Rate Model data (read-only)
const rateModelLens = new RateModelLensService(provider, rateModelLensAddress);
const rateModels = await rateModelLens.getCompleteRateModelsInfo(lexPools);📖 For detailed rate model usage, see Rate Model Lens Guide
Utility Functions
Price Conversions
import { floatToPriceBn, priceBnToFloat } from 'lynx-client';
// Convert human-readable price to contract format (8 decimals)
const priceBn = floatToPriceBn(50000.50); // $50,000.50 → BigInt
// Convert contract price to human-readable
const price = priceBnToFloat(priceBn); // BigInt → 50000.50Leverage Conversions
import { floatUnitsToScaledLeverage, scaledLeverageToUnits } from 'lynx-client';
// Convert human leverage to contract format
const scaledLeverage = floatUnitsToScaledLeverage(25); // 25x → 2500
// Convert contract leverage to human-readable
const leverage = scaledLeverageToUnits(2500); // 2500 → 25xCollateral Conversions
import { floatToChipsBn, chipsBnToUnits } from 'lynx-client';
// Convert human-readable amount to contract format (18 decimals)
const collateralBn = floatToChipsBn(100.5); // 100.5 tokens → BigInt
// Convert contract amount to human-readable
const collateral = chipsBnToUnits(collateralBn); // BigInt → 100.5Fraction Conversions
import { floatToFractionBn, fractionBnToFloat } from 'lynx-client';
// Convert percentage to contract format
const fractionBn = floatToFractionBn(0.05); // 5% → 5000
// Convert contract fraction to percentage
const fraction = fractionBnToFloat(5000n); // 5000 → 0.05 (5%)Advanced Usage
Position Management
// Update position with new TP/SL
await tradersPortal.traderRequests.updatePositionDoubleFieldTpAndSl(
positionId,
floatToPriceBn(55000), // New TP: $55,000
floatToPriceBn(48000), // New SL: $48,000
ethers.parseEther("0.01") // Fee
);
// Close position at market
await tradersPortal.traderRequests.setExistingPositionToMarketClose(
positionId,
floatToPriceBn(49900), // Min acceptable price
floatToPriceBn(50100), // Max acceptable price
ethers.parseEther("0.01") // Fee
);Working with Triggers
import { createTriggersService } from 'lynx-client';
const triggers = createTriggersService(signer, triggersAddress);
// Get trigger configurations
const configs = await triggers.getTriggersConfigurations();
const pairConfigs = await triggers.getPairConfigsInTriggers(1); // Pair ID 1Delegated Trading with OnBehalfTrading
The OnBehalfTrading service enables users to grant trading permissions to other addresses with specific limits and durations:
import { OnBehalfTradingService } from 'lynx-client';
const onBehalfTrading = new OnBehalfTradingService(signer, onBehalfTradingAddress);
// Grant permission to a trader
await onBehalfTrading.permissionManagement.grantPermission(
traderAddress,
tokenAddress,
ethers.parseUnits("1000", 18), // 1000 token limit
86400n // 24 hours duration
);
// Check if permission is valid
const isValid = await onBehalfTrading.permissionQueries.isPermissionValid(
ownerAddress,
traderAddress,
tokenAddress
);
// Get remaining amount
const remaining = await onBehalfTrading.permissionQueries.getRemainingAmount(
ownerAddress,
traderAddress,
tokenAddress
);
// Revoke permissions
await onBehalfTrading.permissionManagement.revokePermission(
traderAddress,
tokenAddress
);
// Get all active permissions for an owner
const allPermissions = await onBehalfTrading.permissionQueries.getAllActivePermissions(
ownerAddress
);LEX Pool Operations
// Get complete LEX pool state
const completeState = await lexLens.getCompleteStateForLex(lexPoolAddress);
// Get supplier state
const supplierState = await lexLens.getLexPoolSupplierState(
lexPoolAddress,
supplierAddress
);
// Get all fee configurations
const feeConfigs = await lexLens.getAllFeeConfigurationsInLex(lexPoolAddress);Standardized Contract Interfaces
The SDK provides standardized interfaces for common contract patterns, ensuring consistent access across all services:
Common Interfaces
ILynxVersionedContract
All Lynx contracts implement versioning for transparency and upgradeability tracking:
import { createTradingFloorService } from 'lynx-client';
const tradingFloor = createTradingFloorService(signer, tradingFloorAddress);
// Access version information through standardized property
const name = await tradingFloor.lynxVersionedContract.getContractName();
const version = await tradingFloor.lynxVersionedContract.getContractVersion();
const contractName = await tradingFloor.lynxVersionedContract.CONTRACT_NAME();
const contractVersion = await tradingFloor.lynxVersionedContract.CONTRACT_VERSION();IPendableAdmin
Contracts with two-step admin transfer for enhanced security:
// All services with admin functionality use the same accessor
const currentAdmin = await tradingFloor.pendableAdmin.admin();
const pendingAdmin = await tradingFloor.pendableAdmin.pendingAdmin();
// Same pattern works across all services
const triggersAdmin = await triggers.pendableAdmin.admin();
const pnlrAdmin = await pnlr.pendableAdmin.admin();IPendableImplementation
Upgradeable contracts with two-step implementation updates:
// Access implementation details through standardized property
const implementation = await tradingFloor.pendableImplementation.implementation();
const pendingImpl = await tradingFloor.pendableImplementation.pendingImplementation();
// OrderBook also uses the same pattern
const orderBookImpl = await orderBook.pendableImplementation.implementation();Standardized Accessor Patterns
All services follow consistent naming conventions for common functionality:
| Property | Interface | Description |
|----------|-----------|-------------|
| lynxVersionedContract | ILynxVersionedContract | Contract version information |
| pendableAdmin | IPendableAdmin | Admin transfer functionality |
| pendableImplementation | IPendableImplementation | Implementation upgrade functionality |
| contractReferences | Service-specific | References to other contracts |
| constants | Service-specific | Protocol constants and scales |
Example with multiple services:
// TradingFloor Service
const tf = createTradingFloorService(signer, address);
await tf.pendableAdmin.admin(); // Admin functionality
await tf.pendableImplementation.implementation(); // Implementation info
await tf.lynxVersionedContract.getContractVersion(); // Version info
await tf.contractReferences.registry(); // Other contract addresses
// PNLR Service (no implementation pattern)
const pnlr = createPNLRService(signer, address);
await pnlr.pendableAdmin.admin(); // Admin functionality
await pnlr.lynxVersionedContract.getContractVersion(); // Version info
// Triggers Service (no implementation pattern)
const triggers = createTriggersService(signer, address);
await triggers.pendableAdmin.admin(); // Admin functionality
await triggers.contractReferences.orderBook(); // Other contract addressesType Safety
The SDK provides comprehensive TypeScript types for all contract interactions:
import {
TPositionRequestIdentifierStruct,
TPositionRequestParamsStruct,
TSinglePositionStruct,
TPairConfigsInTriggersStruct,
ILynxVersionedContract,
IPendableAdmin,
IPendableImplementation
} from 'lynx-client';
// All contract structs are properly typed
const position = await tradingFloorLens.getCompletePositionData(tradingFloorAddress, id);
console.log(position.trader); // string
console.log(position.collateral); // bigint
console.log(position.leverage); // bigint
// Common interfaces ensure type safety
const service: { pendableAdmin: IPendableAdmin } = tradingFloor;
const admin = await service.pendableAdmin.admin(); // Type-safe accessError Handling
The SDK validates inputs to prevent common errors:
try {
// This will throw an error - can't use both price and fraction TP
const params = buildPositionRequestParams({
isLong: true,
collateral: 100,
leverage: 25,
minPrice: 50000,
maxPrice: 50100,
takeProfit: 55000, // Price-based TP
takeProfitByFraction: 0.1 // Fraction-based TP - ERROR!
});
} catch (error) {
console.error("Cannot specify both takeProfit and takeProfitByFraction");
}Type Safety
The SDK provides strong type safety with clear distinctions between different numeric types:
Return Type Conventions
bigint: Used for asset amounts, collateral, and values requiring high precisionnumber: Used for IDs, timestamps, prices, leverage, and human-readable valuesstring: Used for addresses and text valuesboolean: Used for flags and states
Lens Service Types
All Lens services return properly typed data structures:
// Example: TradingFloorLens returns typed position data
const position = await lensService.getCompletePositionData(tradingFloor, positionId);
// position.pairId: number (not bigint)
// position.collateral: bigint (asset amount)
// position.leverage: number (human-readable)
// position.openPrice: number (human-readable price)Constants and Enums
import {
TOpenOrderType,
TOrderCloseType,
TPositionField,
TPairIds,
CHIP_DECIMALS,
PRICES_SCALE,
LEVERAGE_MULTIPLIER_SCALE
} from 'lynx-client';
// Order types
const marketOrder = TOpenOrderType.MARKET;
const limitOrder = TOpenOrderType.LIMIT;
// Pair IDs
const btcUsd = TPairIds.BTC_USD;
const ethUsd = TPairIds.ETH_USD;Best Practices
Always use builder functions for creating position requests:
// ✅ Good const params = buildPositionRequestParams({ ... }); // ❌ Avoid manual construction const params = { long: true, collateral: 100n, ... };Use utility functions for conversions:
// ✅ Good const priceBn = floatToPriceBn(50000); // ❌ Avoid manual calculations const priceBn = BigInt(50000 * 10**8);Handle contract errors appropriately:
try { await tradersPortal.traderRequests.openNewPosition(...); } catch (error) { if (error.reason === "Insufficient collateral") { // Handle specific error } }
Migration Guide
From Previous Versions
If you're upgrading from an earlier version, note these changes to standardized accessors:
// Old pattern (mixed grouping)
const admin = await tradingFloor.administration.admin();
const version = await tradingFloor.contractInfo.getContractVersion();
// New pattern (standardized accessors)
const admin = await tradingFloor.pendableAdmin.admin();
const version = await tradingFloor.lynxVersionedContract.getContractVersion();Key changes:
administration→ Split intopendableAdminandcontractReferencescontractInfo→ Split intolynxVersionedContractandcontractReferences- Consistent naming across all services
Development
Building
npm run buildTesting
npm testContributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
ISC
