diff --git a/agent/package.json b/agent/package.json index c8fa68b8235..92942e4aa4d 100644 --- a/agent/package.json +++ b/agent/package.json @@ -87,6 +87,7 @@ "@elizaos/plugin-arthera": "workspace:*", "@elizaos/plugin-allora": "workspace:*", "@elizaos/plugin-opacity": "workspace:*", + "@elizaos/plugin-hyperliquid": "workspace:*", "@elizaos/plugin-akash": "workspace:*", "readline": "1.3.0", "ws": "8.18.0", @@ -99,4 +100,4 @@ "ts-node": "10.9.2", "tsup": "8.3.5" } -} \ No newline at end of file +} diff --git a/agent/src/index.ts b/agent/src/index.ts index 09c5b60b407..b3f8fb0fcf0 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -84,7 +84,7 @@ import { webSearchPlugin } from "@elizaos/plugin-web-search"; import { giphyPlugin } from "@elizaos/plugin-giphy"; import { letzAIPlugin } from "@elizaos/plugin-letzai"; import { thirdwebPlugin } from "@elizaos/plugin-thirdweb"; - +import { hyperliquidPlugin } from "@elizaos/plugin-hyperliquid"; import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era"; import { OpacityAdapter } from "@elizaos/plugin-opacity"; @@ -758,6 +758,12 @@ export async function createAgent( ? artheraPlugin : null, getSecret(character, "ALLORA_API_KEY") ? alloraPlugin : null, + getSecret(character, "HYPERLIQUID_PRIVATE_KEY") + ? hyperliquidPlugin + : null, + getSecret(character, "HYPERLIQUID_TESTNET") + ? hyperliquidPlugin + : null, getSecret(character, "AKASH_MNEMONIC") && getSecret(character, "AKASH_WALLET_ADDRESS") ? akashPlugin diff --git a/packages/plugin-hyperliquid/.npmignore b/packages/plugin-hyperliquid/.npmignore new file mode 100644 index 00000000000..078562eceab --- /dev/null +++ b/packages/plugin-hyperliquid/.npmignore @@ -0,0 +1,6 @@ +* + +!dist/** +!package.json +!readme.md +!tsup.config.ts \ No newline at end of file diff --git a/packages/plugin-hyperliquid/README.md b/packages/plugin-hyperliquid/README.md new file mode 100644 index 00000000000..a671c61b0e8 --- /dev/null +++ b/packages/plugin-hyperliquid/README.md @@ -0,0 +1,111 @@ +# Hyperliquid Plugin for Eliza + +This plugin enables interaction with the Hyperliquid DEX through Eliza, providing spot trading capabilities. + +## Features + +- 💱 Spot Trading + - Market orders (immediate execution) + - Limit orders (price-specific) + - Smart price validation to prevent mistakes +- 📊 Price Checking + - Real-time price information + - 24h price change + - Volume statistics +- 🔄 Order Management + - Cancel all open orders + - Clear feedback on execution + +## Installation + +Add the plugin to your Eliza configuration: + +```json +{ + "plugins": ["@elizaos/plugin-hyperliquid"] +} +``` + +## Configuration + +Set the following environment variables: + +```env +HYPERLIQUID_PRIVATE_KEY=your_private_key # Required for trading and cancelling orders +HYPERLIQUID_TESTNET=true_or_false # Optional, defaults to false +``` + +## Available Actions + +### 1. SPOT_TRADE + +Place spot market or limit orders. + +Examples: + +``` +# Market Orders +"buy 1 PIP" -> Buys 1 PIP at market price +"sell 2 HYPE" -> Sells 2 HYPE at market price +"market buy 1 ETH" -> Buys 1 ETH at market price + +# Limit Orders +"buy 1 PIP at 20 USDC" -> Places buy order for 1 PIP at 20 USDC +"sell 0.5 HYPE at 21 USDC" -> Places sell order for 0.5 HYPE at 21 USDC +``` + +### 2. PRICE_CHECK + +Get current price information for any token. + +Examples: + +``` +"What's the price of PIP?" +"Check HYPE price" +"Get ETH price" +``` + +Returns: Current price, 24h change, and volume. + +### 3. CANCEL_ORDERS + +Cancel all your open orders. + +Examples: + +``` +"Cancel all orders" +"Cancel my orders" +``` + +## Price Validation + +The plugin includes smart price validation to prevent mistakes: + +- Market Orders: Validates price is within ±50% of market price +- Limit Orders: + - Buy orders must be below market price + - Sell orders must be above market price + - Warns if price is very different from market (±80%) + +## Error Handling + +The plugin provides clear error messages for common issues: + +- Invalid token symbols +- Price validation failures +- Network connection issues +- Order execution failures + +## Security Notes + +- Store your private key securely using environment variables +- Test with small amounts first +- Use testnet for initial testing +- Monitor your orders regularly +- Double-check prices before confirming trades + +## License + +MIT diff --git a/packages/plugin-hyperliquid/eslint.config.mjs b/packages/plugin-hyperliquid/eslint.config.mjs new file mode 100644 index 00000000000..92fe5bbebef --- /dev/null +++ b/packages/plugin-hyperliquid/eslint.config.mjs @@ -0,0 +1,3 @@ +import eslintGlobalConfig from "../../eslint.config.mjs"; + +export default [...eslintGlobalConfig]; diff --git a/packages/plugin-hyperliquid/package.json b/packages/plugin-hyperliquid/package.json new file mode 100644 index 00000000000..14754a20f52 --- /dev/null +++ b/packages/plugin-hyperliquid/package.json @@ -0,0 +1,21 @@ +{ + "name": "@elizaos/plugin-hyperliquid", + "version": "0.1.7", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "@elizaos/core": "workspace:*", + "hyperliquid": "^1.5.6", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsup": "8.3.5" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch", + "lint": "eslint --fix --cache ." + } +} diff --git a/packages/plugin-hyperliquid/src/actions/cancelOrders.ts b/packages/plugin-hyperliquid/src/actions/cancelOrders.ts new file mode 100644 index 00000000000..7c315ce5362 --- /dev/null +++ b/packages/plugin-hyperliquid/src/actions/cancelOrders.ts @@ -0,0 +1,88 @@ +import { + Action, + ActionExample, + IAgentRuntime, + Memory, + State, + HandlerCallback, + elizaLogger, +} from "@elizaos/core"; +import { Hyperliquid } from "hyperliquid"; + +export const cancelOrders: Action = { + name: "CANCEL_ORDERS", + similes: ["CANCEL_ALL_ORDERS", "CANCEL", "CANCEL_ALL"], + description: "Cancel all open orders on Hyperliquid", + validate: async (runtime: IAgentRuntime) => { + return !!runtime.getSetting("HYPERLIQUID_PRIVATE_KEY"); + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + options: Record, + callback?: HandlerCallback + ) => { + try { + // Initialize SDK + const sdk = new Hyperliquid({ + privateKey: runtime.getSetting("HYPERLIQUID_PRIVATE_KEY"), + testnet: runtime.getSetting("HYPERLIQUID_TESTNET") === "true", + enableWs: false, + }); + await sdk.connect(); + + elizaLogger.info("Cancelling all open orders..."); + const result = await sdk.custom.cancelAllOrders(); + elizaLogger.info("Cancel result:", result); + + if (callback) { + const cancelledCount = + result?.response?.data?.statuses?.length || 0; + callback({ + text: + cancelledCount > 0 + ? `Successfully cancelled ${cancelledCount} open order${cancelledCount > 1 ? "s" : ""}` + : "No open orders to cancel", + content: result, + }); + } + + return true; + } catch (error) { + elizaLogger.error("Error cancelling orders:", error); + if (callback) { + callback({ + text: `Error cancelling orders: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Cancel all my orders", + }, + }, + { + user: "{{agent}}", + content: { + text: "I'll cancel all your open orders.", + action: "CANCEL_ORDERS", + }, + }, + { + user: "{{agent}}", + content: { + text: "Successfully cancelled 2 open orders", + }, + }, + ], + ] as ActionExample[][], +}; + +export default cancelOrders; diff --git a/packages/plugin-hyperliquid/src/actions/priceCheck.ts b/packages/plugin-hyperliquid/src/actions/priceCheck.ts new file mode 100644 index 00000000000..8a7b6469a74 --- /dev/null +++ b/packages/plugin-hyperliquid/src/actions/priceCheck.ts @@ -0,0 +1,148 @@ +import { + Action, + ActionExample, + IAgentRuntime, + Memory, + State, + HandlerCallback, + composeContext, + elizaLogger, + generateObjectDeprecated, + ModelClass, +} from "@elizaos/core"; +import { Hyperliquid } from "hyperliquid"; +import { HyperliquidError } from "../types.js"; +import { priceCheckTemplate } from "../templates.js"; + +export const priceCheck: Action = { + name: "PRICE_CHECK", + similes: ["CHECK_PRICE", "GET_PRICE", "PRICE", "CURRENT_PRICE"], + description: "Get current price for a token on Hyperliquid", + validate: async () => true, // Public endpoint + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + options: Record, + callback?: HandlerCallback + ) => { + try { + // Initialize or update state + state = !state + ? await runtime.composeState(message) + : await runtime.updateRecentMessageState(state); + + const context = composeContext({ + state, + template: priceCheckTemplate, + }); + + const content = await generateObjectDeprecated({ + runtime, + context, + modelClass: ModelClass.SMALL, + }); + + if (!content?.symbol) { + throw new HyperliquidError( + "Could not determine which token price to check" + ); + } + + elizaLogger.info("Checking price for token:", content.symbol); + + // Initialize SDK + const sdk = new Hyperliquid({ + enableWs: false, + }); + await sdk.connect(); + + // Get market data + const [meta, assetCtxs] = + await sdk.info.spot.getSpotMetaAndAssetCtxs(); + + // Find token and market + const tokenIndex = meta.tokens.findIndex( + (token) => + token.name.toUpperCase() === content.symbol.toUpperCase() + ); + if (tokenIndex === -1) { + throw new HyperliquidError( + `Could not find token ${content.symbol}` + ); + } + + const marketIndex = assetCtxs.findIndex( + (ctx) => ctx.coin === `${content.symbol}-SPOT` + ); + if (marketIndex === -1) { + throw new HyperliquidError( + `Could not find market for ${content.symbol}` + ); + } + + const marketCtx = assetCtxs[marketIndex]; + if (!marketCtx || !marketCtx.midPx) { + throw new HyperliquidError( + `Could not get market price for ${content.symbol}` + ); + } + + const price = Number(marketCtx.midPx); + const dayChange = ( + ((price - Number(marketCtx.prevDayPx)) / + Number(marketCtx.prevDayPx)) * + 100 + ).toFixed(2); + const volume = Number(marketCtx.dayNtlVlm).toFixed(2); + + if (callback) { + callback({ + text: `${content.symbol} price: ${price.toFixed(2)} USDC (24h change: ${dayChange}%, volume: ${volume} USDC)`, + content: { + symbol: content.symbol, + price: price, + dayChange: dayChange, + volume: volume, + }, + }); + } + + return true; + } catch (error) { + elizaLogger.error("Error checking price:", error); + if (callback) { + callback({ + text: `Error checking price: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "What's the current price of PIP?", + }, + }, + { + user: "{{agent}}", + content: { + text: "I'll check the current PIP price for you.", + action: "PRICE_CHECK", + }, + }, + { + user: "{{agent}}", + content: { + text: "PIP price: 19.73 USDC (24h change: -1.82%, volume: 1053445.75 USDC)", + }, + }, + ], + ] as ActionExample[][], +}; + +export default priceCheck; diff --git a/packages/plugin-hyperliquid/src/actions/spotTrade.ts b/packages/plugin-hyperliquid/src/actions/spotTrade.ts new file mode 100644 index 00000000000..cdc3e3e1004 --- /dev/null +++ b/packages/plugin-hyperliquid/src/actions/spotTrade.ts @@ -0,0 +1,263 @@ +import { + Action, + ActionExample, + IAgentRuntime, + Memory, + State, + HandlerCallback, + composeContext, + elizaLogger, + generateObjectDeprecated, + ModelClass, +} from "@elizaos/core"; +import { Hyperliquid } from "hyperliquid"; +import { + SpotOrderSchema, + HyperliquidError, + PRICE_VALIDATION, +} from "../types.js"; +import { spotTradeTemplate } from "../templates.js"; + +export const spotTrade: Action = { + name: "SPOT_TRADE", + similes: ["SPOT_ORDER", "SPOT_BUY", "SPOT_SELL"], + description: "Place a spot trade order on Hyperliquid", + validate: async (runtime: IAgentRuntime) => { + return !!runtime.getSetting("HYPERLIQUID_PRIVATE_KEY"); + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + options: Record, + callback?: HandlerCallback + ) => { + try { + // Initialize or update state + state = !state + ? await runtime.composeState(message) + : await runtime.updateRecentMessageState(state); + + const context = composeContext({ + state, + template: spotTradeTemplate, + }); + + const content = await generateObjectDeprecated({ + runtime, + context, + modelClass: ModelClass.SMALL, + }); + + if (!content) { + throw new HyperliquidError( + "Could not parse trading parameters from conversation" + ); + } + + elizaLogger.info( + "Raw content from LLM:", + JSON.stringify(content, null, 2) + ); + + // Validate order parameters + const validatedOrder = SpotOrderSchema.parse(content); + elizaLogger.info("Validated order:", validatedOrder); + + // Initialize SDK + const sdk = new Hyperliquid({ + privateKey: runtime.getSetting("HYPERLIQUID_PRIVATE_KEY"), + testnet: runtime.getSetting("HYPERLIQUID_TESTNET") === "true", + enableWs: false, + }); + await sdk.connect(); + + // Get market data + const [meta, assetCtxs] = + await sdk.info.spot.getSpotMetaAndAssetCtxs(); + + // Find token and market + const tokenIndex = meta.tokens.findIndex( + (token) => + token.name.toUpperCase() === + validatedOrder.coin.toUpperCase() + ); + if (tokenIndex === -1) { + throw new HyperliquidError( + `Could not find token ${validatedOrder.coin}` + ); + } + const tokenInfo = meta.tokens[tokenIndex]; + elizaLogger.info("Found token:", tokenInfo.name); + + const marketIndex = assetCtxs.findIndex( + (ctx) => ctx.coin === `${validatedOrder.coin}-SPOT` + ); + if (marketIndex === -1) { + throw new HyperliquidError( + `Could not find market for ${validatedOrder.coin}` + ); + } + const marketCtx = assetCtxs[marketIndex]; + if (!marketCtx || !marketCtx.midPx) { + throw new HyperliquidError( + `Could not get market price for ${validatedOrder.coin}` + ); + } + + // Calculate prices + const midPrice = Number(marketCtx.midPx); + const isMarketOrder = !validatedOrder.limit_px; + let finalPrice: number; + + if (isMarketOrder) { + // For market orders, use current price with slippage + const slippage = PRICE_VALIDATION.SLIPPAGE; + finalPrice = validatedOrder.is_buy + ? midPrice * (1 + slippage) + : midPrice * (1 - slippage); + + // Validate market order price + if ( + finalPrice < + midPrice * PRICE_VALIDATION.MARKET_ORDER.MIN_RATIO || + finalPrice > + midPrice * PRICE_VALIDATION.MARKET_ORDER.MAX_RATIO + ) { + throw new HyperliquidError( + `Market order price (${finalPrice.toFixed(2)} USDC) is too far from market price (${midPrice.toFixed(2)} USDC). This might be due to low liquidity.` + ); + } + } else { + // For limit orders + finalPrice = validatedOrder.limit_px; + + // Validate limit order price is optimal + if (validatedOrder.is_buy && finalPrice > midPrice) { + throw new HyperliquidError( + `Cannot place buy limit order at ${finalPrice.toFixed(2)} USDC because it's above market price (${midPrice.toFixed(2)} USDC). To execute immediately, use a market order. For a limit order, set a price below ${midPrice.toFixed(2)} USDC.` + ); + } else if (!validatedOrder.is_buy && finalPrice < midPrice) { + throw new HyperliquidError( + `Cannot place sell limit order at ${finalPrice.toFixed(2)} USDC because it's below market price (${midPrice.toFixed(2)} USDC). To execute immediately, use a market order. For a limit order, set a price above ${midPrice.toFixed(2)} USDC.` + ); + } + + // Log warning if price is very different from market + if ( + finalPrice < + midPrice * + PRICE_VALIDATION.LIMIT_ORDER.WARNING_MIN_RATIO || + finalPrice > + midPrice * + PRICE_VALIDATION.LIMIT_ORDER.WARNING_MAX_RATIO + ) { + elizaLogger.warn( + `Limit price (${finalPrice.toFixed(2)} USDC) is very different from market price (${midPrice.toFixed(2)} USDC). Make sure this is intentional.`, + { + finalPrice, + midPrice, + ratio: finalPrice / midPrice, + } + ); + } + } + + // Prepare and place order + const rounded_px = Number(finalPrice.toFixed(tokenInfo.szDecimals)); + const orderRequest = { + coin: `${validatedOrder.coin}-SPOT`, + asset: 10000 + marketIndex, + is_buy: validatedOrder.is_buy, + sz: validatedOrder.sz, + limit_px: rounded_px, + reduce_only: false, + order_type: isMarketOrder + ? { market: {} } + : { limit: { tif: "Gtc" as const } }, + }; + + elizaLogger.info("Placing order:", orderRequest); + const result = await sdk.exchange.placeOrder(orderRequest); + + // Check if order was rejected + if ( + result.status === "ok" && + result.response?.type === "order" && + result.response.data?.statuses?.[0]?.error + ) { + throw new HyperliquidError( + result.response.data.statuses[0].error + ); + } + + // Send success callback + if (callback) { + const action = validatedOrder.is_buy ? "buy" : "sell"; + const executionPrice = + result.response?.data?.statuses?.[0]?.px || rounded_px; + callback({ + text: `Successfully placed ${isMarketOrder ? "a market" : "a limit"} order to ${action} ${validatedOrder.sz} ${validatedOrder.coin} at ${executionPrice}`, + content: result, + }); + } + + return true; + } catch (error) { + elizaLogger.error("Error placing spot order:", error); + if (callback) { + callback({ + text: `Error placing spot order: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Buy 0.1 HYPE at 20 USDC", + }, + }, + { + user: "{{agent}}", + content: { + text: "I'll place a spot buy order for 0.1 HYPE at 20 USDC.", + action: "SPOT_TRADE", + }, + }, + { + user: "{{agent}}", + content: { + text: "Successfully placed a limit order to buy 0.1 HYPE at 20 USDC", + }, + }, + ], + [ + { + user: "{{user1}}", + content: { + text: "Sell 2 HYPE at 21 USDC", + }, + }, + { + user: "{{agent}}", + content: { + text: "I'll place a spot sell order for 2 HYPE at 21 USDC.", + action: "SPOT_TRADE", + }, + }, + { + user: "{{agent}}", + content: { + text: "Successfully placed a limit order to sell 2 HYPE at 21 USDC", + }, + }, + ], + ] as ActionExample[][], +}; + +export default spotTrade; diff --git a/packages/plugin-hyperliquid/src/index.ts b/packages/plugin-hyperliquid/src/index.ts new file mode 100644 index 00000000000..a5e282f539d --- /dev/null +++ b/packages/plugin-hyperliquid/src/index.ts @@ -0,0 +1,16 @@ +import { Plugin } from "@elizaos/core"; +import { spotTrade } from "./actions/spotTrade"; +import { priceCheck } from "./actions/priceCheck"; +import { cancelOrders } from "./actions/cancelOrders"; + +export const hyperliquidPlugin: Plugin = { + name: "hyperliquid", + description: "Hyperliquid plugin", + actions: [spotTrade, priceCheck, cancelOrders], + providers: [], + evaluators: [], + services: [], + clients: [], +}; + +export default hyperliquidPlugin; diff --git a/packages/plugin-hyperliquid/src/templates.ts b/packages/plugin-hyperliquid/src/templates.ts new file mode 100644 index 00000000000..6ff80265375 --- /dev/null +++ b/packages/plugin-hyperliquid/src/templates.ts @@ -0,0 +1,57 @@ +export const spotTradeTemplate = `Look at your LAST RESPONSE in the conversation where you confirmed a trade request. +Based on ONLY that last message, extract the trading details: + +For Hyperliquid spot trading: +- Market orders (executes immediately at best available price): + "buy 1 HYPE" -> { "coin": "HYPE", "is_buy": true, "sz": 1 } + "sell 2 HYPE" -> { "coin": "HYPE", "is_buy": false, "sz": 2 } + "market buy 1 HYPE" -> { "coin": "HYPE", "is_buy": true, "sz": 1 } + "market sell 2 HYPE" -> { "coin": "HYPE", "is_buy": false, "sz": 2 } + +- Limit orders (waits for specified price): + "buy 1 HYPE at 20 USDC" -> { "coin": "HYPE", "is_buy": true, "sz": 1, "limit_px": 20 } + "sell 0.5 HYPE at 21 USDC" -> { "coin": "HYPE", "is_buy": false, "sz": 0.5, "limit_px": 21 } + "limit buy 1 HYPE at 20 USDC" -> { "coin": "HYPE", "is_buy": true, "sz": 1, "limit_px": 20 } + "limit sell 0.5 HYPE at 21 USDC" -> { "coin": "HYPE", "is_buy": false, "sz": 0.5, "limit_px": 21 } + +\`\`\`json +{ + "coin": "", + "is_buy": "", + "sz": "", + "limit_px": "" +} +\`\`\` + +Note: +- Just use the coin symbol (HYPE, ETH, etc.) +- sz is the size/quantity to trade (exactly as specified in the message) +- limit_px is optional: + - If specified (with "at X USDC"), order will be placed at that exact price + - If not specified, order will be placed at current market price +- Words like "market" or "limit" at the start are optional but help clarify intent + +Recent conversation: +{{recentMessages}}`; + +export const priceCheckTemplate = `Look at your LAST RESPONSE in the conversation where you confirmed which token price to check. +Based on ONLY that last message, extract the token symbol. + +For example: +- "I'll check PIP price for you" -> { "symbol": "PIP" } +- "Let me check the price of HYPE" -> { "symbol": "HYPE" } +- "I'll get the current ETH price" -> { "symbol": "ETH" } + +\`\`\`json +{ + "symbol": "" +} +\`\`\` + +Note: +- Just return the token symbol (PIP, HYPE, ETH, etc.) +- Remove any suffixes like "-SPOT" or "USDC" +- If multiple tokens are mentioned, use the last one + +Recent conversation: +{{recentMessages}}`; diff --git a/packages/plugin-hyperliquid/src/types.ts b/packages/plugin-hyperliquid/src/types.ts new file mode 100644 index 00000000000..d2cece49f3c --- /dev/null +++ b/packages/plugin-hyperliquid/src/types.ts @@ -0,0 +1,78 @@ +import { z } from "zod"; + +// Base configuration types +export interface HyperliquidConfig { + privateKey: string; + testnet?: boolean; + walletAddress?: string; +} + +// Enhanced schemas with better validation +export const SpotOrderSchema = z.object({ + coin: z.string().min(1), + is_buy: z.boolean(), + sz: z.number().positive(), + limit_px: z.number().positive().nullable(), + reduce_only: z.boolean().default(false), + order_type: z + .object({ + limit: z.object({ + tif: z.enum(["Ioc", "Gtc"]), + }), + }) + .default({ limit: { tif: "Gtc" } }), +}); + +// Inferred types from schemas +export type SpotOrder = z.infer; + +// Response types +export interface OrderResponse { + coin: string; + orderId: string; + status: "open" | "filled" | "cancelled" | "rejected"; + size: number; + price: number; + is_buy: boolean; +} + +// Error handling types +export class HyperliquidError extends Error { + constructor( + message: string, + public code?: number, + public details?: unknown + ) { + super(message); + this.name = "HyperliquidError"; + } +} + +// Constants +export const ORDER_STATUS = { + OPEN: "open", + FILLED: "filled", + CANCELLED: "cancelled", + REJECTED: "rejected", +} as const; + +export const PRICE_VALIDATION = { + MARKET_ORDER: { + MIN_RATIO: 0.5, // -50% from mid price + MAX_RATIO: 1.5, // +50% from mid price + }, + LIMIT_ORDER: { + WARNING_MIN_RATIO: 0.2, // -80% from mid price + WARNING_MAX_RATIO: 5, // +500% from mid price + }, + SLIPPAGE: 0.01, // 1% slippage for market orders +} as const; + +export type OrderStatus = keyof typeof ORDER_STATUS; + +// Balance types +export interface BalanceResponse { + coin: string; + free: number; + locked: number; +} diff --git a/packages/plugin-hyperliquid/tsconfig.json b/packages/plugin-hyperliquid/tsconfig.json new file mode 100644 index 00000000000..33e9858f482 --- /dev/null +++ b/packages/plugin-hyperliquid/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "./src", + "declaration": true + }, + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/packages/plugin-hyperliquid/tsup.config.ts b/packages/plugin-hyperliquid/tsup.config.ts new file mode 100644 index 00000000000..1a96f24afa1 --- /dev/null +++ b/packages/plugin-hyperliquid/tsup.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], // Ensure you're targeting CommonJS + external: [ + "dotenv", // Externalize dotenv to prevent bundling + "fs", // Externalize fs to use Node.js built-in module + "path", // Externalize other built-ins if necessary + "@reflink/reflink", + "@node-llama-cpp", + "https", + "http", + "agentkeepalive", + "safe-buffer", + // Add other modules you want to externalize + ], +});