@shogun-sdk/money-legos
Version:
Shogun Money Legos: clients and types for quotes, memes, prices, balances, fees, validations, etc.
124 lines (102 loc) • 3.67 kB
text/typescript
import { InfoAPI } from './rest/info.js';
import { ExchangeAPI } from './rest/exchange.js';
import { RateLimiter } from './utils/rateLimiter.js';
import * as CONSTANTS from './types/constants.js';
import { CustomOperations } from './rest/custom.js';
import { ethers } from 'ethers';
import { SymbolConversion } from './utils/symbolConversion.js';
import { AuthenticationError } from './utils/errors.js';
export interface HyperliquidConfig {
privateKey?: string;
testnet?: boolean;
walletAddress?: string;
vaultAddress?: string;
maxReconnectAttempts?: number;
}
export class Hyperliquid {
public info: InfoAPI;
public exchange: ExchangeAPI;
public custom: CustomOperations;
public symbolConversion: SymbolConversion;
private rateLimiter: RateLimiter;
private isValidPrivateKey: boolean = false;
private _initialized: boolean = false;
private _initializing: Promise<void> | null = null;
private vaultAddress?: string | null = null;
constructor(params: HyperliquidConfig = {}) {
const { privateKey, vaultAddress } = params;
const baseURL = CONSTANTS.BASE_URL;
this.rateLimiter = new RateLimiter();
this.symbolConversion = new SymbolConversion(baseURL, this.rateLimiter);
this.vaultAddress = vaultAddress || null;
// Initialize info API
this.info = new InfoAPI(baseURL, this.rateLimiter, this.symbolConversion, this);
// Create proxy objects for exchange and custom
this.exchange = this.createAuthenticatedProxy(ExchangeAPI);
this.custom = this.createAuthenticatedProxy(CustomOperations);
if (privateKey) {
this.initializePrivateKey(privateKey);
}
}
public async connect(): Promise<void> {
if (!this._initialized) {
if (!this._initializing) {
this._initializing = this.initialize();
}
await this._initializing;
}
}
private async initialize(): Promise<void> {
if (this._initialized) return;
try {
// Initialize symbol conversion first
await this.symbolConversion.initialize();
this._initialized = true;
this._initializing = null;
} catch (error) {
this._initializing = null;
throw error;
}
}
public async ensureInitialized(): Promise<void> {
await this.connect();
}
private initializePrivateKey(privateKey: string): void {
try {
const formattedPrivateKey = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`;
new ethers.Wallet(formattedPrivateKey); // Validate the private key
this.exchange = new ExchangeAPI(
formattedPrivateKey,
this.rateLimiter,
this.symbolConversion,
this,
this.vaultAddress,
);
this.custom = new CustomOperations(this.exchange, this.info, formattedPrivateKey, this.symbolConversion, this);
this.isValidPrivateKey = true;
} catch (error) {
console.warn('Invalid private key provided. Some functionalities will be limited.');
this.isValidPrivateKey = false;
}
}
private createAuthenticatedProxy<T extends object>(_Class: new (...args: any[]) => T): T {
return new Proxy({} as T, {
get: (target, prop) => {
if (!this.isValidPrivateKey) {
throw new AuthenticationError('Invalid or missing private key. This method requires authentication.');
}
return target[prop as keyof T];
},
});
}
// Modify existing methods to check initialization
public isAuthenticated(): boolean {
this.ensureInitialized();
return this.isValidPrivateKey;
}
disconnect(): void {
this.ensureInitialized();
}
}
export * from './types/index.js';
export * from './utils/signing.js';