diff --git a/core/src/exchanges/hyperliquid/index.ts b/core/src/exchanges/hyperliquid/index.ts index f5d0fa37..0c05eed7 100644 --- a/core/src/exchanges/hyperliquid/index.ts +++ b/core/src/exchanges/hyperliquid/index.ts @@ -250,13 +250,27 @@ export class HyperliquidExchange extends PredictionMarketExchange { : { limit: { tif: 'Gtc' } }, }; - // Key order matters for msgpack hash: type, orders, grouping + // Key order matters for msgpack hash: type, orders, grouping, builder const action: Record = { type: 'order', orders: [orderWire], grouping: 'na', }; + if (params.builder !== undefined || params.builderFee !== undefined) { + if (!params.builder) { + throw new Error('Hyperliquid builderFee requires builder address'); + } + if (!/^0x[0-9a-fA-F]{40}$/.test(params.builder)) { + throw new Error(`Invalid Hyperliquid builder address: ${params.builder}`); + } + const builderFee = params.builderFee ?? 0; + if (!Number.isInteger(builderFee) || builderFee < 0) { + throw new Error('Hyperliquid builderFee must be a non-negative integer in tenths of a basis point'); + } + action.builder = { b: params.builder.toLowerCase(), f: builderFee }; + } + return { exchange: this.name, params, diff --git a/core/src/server/openapi.yaml b/core/src/server/openapi.yaml index 428cf7d9..45cb13aa 100644 --- a/core/src/server/openapi.yaml +++ b/core/src/server/openapi.yaml @@ -3992,6 +3992,12 @@ components: fee: type: number description: 'Optional fee rate (e.g., 1000 for 0.1%)' + builder: + type: string + description: Hyperliquid builder address to attach to the order action. + builderFee: + type: number + description: Hyperliquid builder fee in tenths of a basis point (e.g. 10 = 1 bp). tickSize: type: number description: Optional override for Limitless/Polymarket diff --git a/core/src/types.ts b/core/src/types.ts index eb122413..b8df178b 100644 --- a/core/src/types.ts +++ b/core/src/types.ts @@ -326,6 +326,10 @@ export interface CreateOrderParams { denom?: 'usdc' | 'shares'; // Hosted mode: amount unit. slippage_pct?: number; // Hosted mode: maximum market-order slippage percentage. fee?: number; // Optional fee rate (e.g., 1000 for 0.1%) + /** Hyperliquid builder address to attach to the order action. */ + builder?: string; + /** Hyperliquid builder fee in tenths of a basis point (e.g. 10 = 1 bp). */ + builderFee?: number; tickSize?: number; // Optional override for Limitless/Polymarket negRisk?: boolean; // Optional override to skip neg-risk lookup (Polymarket) onBehalfOf?: number; // Limitless delegated signing: profile ID to trade on behalf of diff --git a/core/test/exchanges/hyperliquid-build-order.test.ts b/core/test/exchanges/hyperliquid-build-order.test.ts new file mode 100644 index 00000000..6d756627 --- /dev/null +++ b/core/test/exchanges/hyperliquid-build-order.test.ts @@ -0,0 +1,54 @@ +import { HyperliquidExchange } from '../../src/exchanges/hyperliquid'; + +describe('HyperliquidExchange buildOrder builder attribution', () => { + const params = { + marketId: 'hl:#100001751', + outcomeId: '100001751', + side: 'buy' as const, + type: 'limit' as const, + amount: 10, + price: 0.001, + }; + + it('attaches builder attribution when builder params are supplied', async () => { + const exchange = new HyperliquidExchange({ testnet: true }); + const built = await exchange.buildOrder({ + ...params, + builder: '0x370a6D37911294ebE8D45419d007E7e0C4BEC2a9', + builderFee: 10, + }); + + expect(built.raw).toEqual({ + type: 'order', + orders: [{ + a: 100001751, + b: true, + p: '0.001', + s: '10', + r: false, + t: { limit: { tif: 'Gtc' } }, + }], + grouping: 'na', + builder: { + b: '0x370a6d37911294ebe8d45419d007e7e0c4bec2a9', + f: 10, + }, + }); + }); + + it('rejects malformed builder params before signing', async () => { + const exchange = new HyperliquidExchange({ testnet: true }); + + await expect(exchange.buildOrder({ ...params, builderFee: 10 })).rejects.toThrow( + 'Hyperliquid builderFee requires builder address', + ); + await expect(exchange.buildOrder({ ...params, builder: '0x123', builderFee: 10 })).rejects.toThrow( + 'Invalid Hyperliquid builder address', + ); + await expect(exchange.buildOrder({ + ...params, + builder: '0x370a6D37911294ebE8D45419d007E7e0C4BEC2a9', + builderFee: 0.5, + })).rejects.toThrow('Hyperliquid builderFee must be a non-negative integer'); + }); +}); diff --git a/docs/api-reference/openapi.json b/docs/api-reference/openapi.json index 72ee155e..e0d7d3d9 100644 --- a/docs/api-reference/openapi.json +++ b/docs/api-reference/openapi.json @@ -9909,6 +9909,14 @@ "type": "number", "description": "Optional fee rate (e.g., 1000 for 0.1%)" }, + "builder": { + "type": "string", + "description": "Hyperliquid builder address to attach to the order action." + }, + "builderFee": { + "type": "number", + "description": "Hyperliquid builder fee in tenths of a basis point (e.g. 10 = 1 bp)." + }, "tickSize": { "type": "number", "description": "Optional override for Limitless/Polymarket" diff --git a/sdks/python/API_REFERENCE.md b/sdks/python/API_REFERENCE.md index 44a18595..eabcd275 100644 --- a/sdks/python/API_REFERENCE.md +++ b/sdks/python/API_REFERENCE.md @@ -2274,6 +2274,8 @@ price: float # Required for limit orders denom: str # Hosted mode: amount unit. slippage_pct: float # Hosted mode: maximum market-order slippage percentage. fee: float # Optional fee rate (e.g., 1000 for 0.1%) +builder: str # Hyperliquid builder address to attach to the order action. +builder_fee: float # Hyperliquid builder fee in tenths of a basis point (e.g. 10 = 1 bp). tick_size: float # Optional override for Limitless/Polymarket neg_risk: bool # Optional override to skip neg-risk lookup (Polymarket) on_behalf_of: float # Limitless delegated signing: profile ID to trade on behalf of diff --git a/sdks/typescript/API_REFERENCE.md b/sdks/typescript/API_REFERENCE.md index 79ec1a85..bbe5a1fe 100644 --- a/sdks/typescript/API_REFERENCE.md +++ b/sdks/typescript/API_REFERENCE.md @@ -2269,6 +2269,8 @@ price?: number; // Required for limit orders denom?: string; // Hosted mode: amount unit. slippage_pct?: number; // Hosted mode: maximum market-order slippage percentage. fee?: number; // Optional fee rate (e.g., 1000 for 0.1%) +builder?: string; // Hyperliquid builder address to attach to the order action. +builderFee?: number; // Hyperliquid builder fee in tenths of a basis point (e.g. 10 = 1 bp). tickSize?: number; // Optional override for Limitless/Polymarket negRisk?: boolean; // Optional override to skip neg-risk lookup (Polymarket) onBehalfOf?: number; // Limitless delegated signing: profile ID to trade on behalf of