Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.outcome.xyz/llms.txt

Use this file to discover all available pages before exploring further.

The Trading Adapter (adapter.trading) lets you submit and cancel orders on HIP-4 prediction market outcomes. Before you can place any orders you must authenticate using adapter.auth.initAuth() - the trading adapter returns an error result (rather than throwing) if you attempt to place an order without a valid signer. Order placement performs local validation for tick alignment, notional size, and minimum shares before signing, so most errors are caught before hitting the exchange.
You must call adapter.auth.initAuth(walletAddress, signer) before using any trading methods. See the Auth Adapter page for the full authentication flow, including agent key setup.

placeOrder(params)

Places a single order on a HIP-4 outcome. This method never throws - check the success field and the error message on the returned result instead. For limit orders, the SDK performs these local checks before signing:
  • Tick-aligns the price to 5 significant figures.
  • Validates that the notional value (price × amount) meets the minimum (10 USDH) unless skipMinNotionalCheck is true.
  • When markPx is provided, validates that amount meets the minimum shares threshold.
For market orders, the SDK fetches the current mid and applies 8% slippage (ceiling for buys, floor for sells), then clamps the result to [0.0001, 0.9999].
const result = await adapter.trading.placeOrder({
  marketId: "516",
  outcome: "#5160",
  side: "buy",
  type: "limit",
  price: "0.65",
  amount: "100",
});

if (!result.success) {
  console.error(result.error);
}

Parameters

marketId
string
required
The outcome ID as a string (e.g. "516").
outcome
string
required
The side identifier. Use the coin string (e.g. "#5160" for side 0, "#5161" for side 1). This determines which side of the outcome you are trading.
side
string
required
"buy" or "sell".
type
string
required
"market" or "limit".
price
string
Limit price as a decimal string (0–1). Required for limit orders. Ignored for market orders (the SDK computes price from the mid with slippage).
amount
string
required
Order size in shares.
timeInForce
string
default:"GTC"
Time-in-force for limit orders. One of "GTC", "GTD", "FOK", "FAK". See the TIF mapping table below. Ignored for market orders.
markPx
number
Current market price (0–1). When provided, the SDK enforces a minimum shares check: amount >= getMinShares(markPx), which ensures the order meets the 10 USDH minimum notional.
builderAddress
string
Optional referral address that receives builder fees. Checksummed addresses are accepted; the SDK lowercases the value before signing.
builderFee
number
Builder fee in tenths of a basis point. 0 = no fee. 100 = 0.1%. Maximum is 1000 (1.0%).
skipMinNotionalCheck
boolean
When true, skips the SDK’s local minimum-notional and minimum-shares pre-checks. Use this for position-closing flows where the residual size may be below 10 USDH but the exchange still accepts the order.

TIF mapping

The SDK maps SDK-level TIF values to Hyperliquid order types as follows:
SDK timeInForceOrder typeHyperliquid TIFNotes
"GTC" (default)"limit"GtcGood-till-cancelled
"GTD""limit"GtcNo true GTD support; behaves as GTC
"FOK""limit"IocTrue FOK semantics not enforced
"FAK""limit"IocImmediate-or-cancel
(any)"market"FrontendMarketSlippage-protected best-execution

Return type

Promise<PredictionOrderResult> - never throws.
success
boolean
required
true if the order was accepted by the exchange.
orderId
string
Hyperliquid order ID. Present when the order filled or is resting.
status
string
"filled" - fully executed. "resting" - sitting in the book. "error" - exchange rejected the order. "unknown" - unrecognized response.
shares
string
Filled size. Only present when status === "filled".
error
string
Error message. Present when success === false.

Examples

import { createHIP4Adapter } from "@hip4/sdk";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import {
  getAgentApprovalTypedData,
  submitAgentApproval,
} from "@hip4/sdk";

const adapter = createHIP4Adapter({ testnet: false });
await adapter.initialize();

// --- Authentication (agent key flow) ---
const agentKey = generatePrivateKey();
const agent = privateKeyToAccount(agentKey);
const typedData = getAgentApprovalTypedData(agent.address, "My App", Date.now(), false);
const sig = await walletClient.signTypedData(typedData);
await submitAgentApproval(sig, agent.address, "My App", Date.now(), false);
await adapter.auth.initAuth(userAddress, agent);

// --- Place a limit buy order ---
const limitResult = await adapter.trading.placeOrder({
  marketId: "516",
  outcome: "#5160",        // side 0 (e.g. "Yes")
  side: "buy",
  type: "limit",
  price: "0.65",
  amount: "100",
  timeInForce: "GTC",
  markPx: 0.65,            // enables minimum-shares validation
});

if (limitResult.success) {
  console.log("Order status:", limitResult.status);
  // "filled" or "resting"
  if (limitResult.status === "filled") {
    console.log("Filled shares:", limitResult.shares);
  }
} else {
  console.error("Order failed:", limitResult.error);
}

// --- Place a market sell order ---
const marketResult = await adapter.trading.placeOrder({
  marketId: "516",
  outcome: "#5161",        // side 1 (e.g. "No")
  side: "sell",
  type: "market",
  amount: "50",
});

// --- Limit buy with builder fee ---
const builderResult = await adapter.trading.placeOrder({
  marketId: "516",
  outcome: "#5160",
  side: "buy",
  type: "limit",
  price: "0.70",
  amount: "25",
  builderAddress: "0xYourBuilderAddress",
  builderFee: 100,         // 0.1%
});

// --- Close a small residual position, skipping notional check ---
const closeResult = await adapter.trading.placeOrder({
  marketId: "516",
  outcome: "#5160",
  side: "sell",
  type: "limit",
  price: "0.60",
  amount: "3",
  skipMinNotionalCheck: true,
});

cancelOrder(params[])

Cancels one or more resting orders. Unlike placeOrder, this method throws on failure.
await adapter.trading.cancelOrder([
  { marketId: "516", orderId: "12345", outcome: "#5160" },
]);

Parameters

cancelOrder accepts an array of cancel requests:
[].marketId
string
required
The outcome ID as a string.
[].orderId
string
required
The Hyperliquid order ID to cancel. Obtain this from placeOrder’s orderId field or from adapter.account.fetchOpenOrders().
[].outcome
string
Optional side identifier (e.g. "#5160"). Providing this resolves the correct side asset ID. When omitted, the SDK defaults to side 0.

Return type

Promise<void> - resolves when all cancels are confirmed. Throws if authentication is missing or the exchange rejects the request.
The current implementation always resolves the cancel asset ID to side 0 unless you provide the outcome field. If you placed an order on side 1 ("#5161"), you must pass outcome: "#5161" to cancel it correctly.

Example

// Cancel a single resting order
await adapter.trading.cancelOrder([
  { marketId: "516", orderId: "12345", outcome: "#5160" },
]);

// Cancel multiple orders at once
await adapter.trading.cancelOrder([
  { marketId: "516", orderId: "12345", outcome: "#5160" },
  { marketId: "516", orderId: "67890", outcome: "#5161" },
]);

// Fetch open orders first, then cancel them
const openOrders = await adapter.account.fetchOpenOrders(userAddress);
const cancels = openOrders.map((order) => ({
  marketId: String(order.outcomeId),
  orderId: String(order.oid),
  outcome: order.coin,
}));
await adapter.trading.cancelOrder(cancels);

Token conversions

Four protocol-level share-conversion primitives that move value between USDH and outcome tokens without touching the orderbook. They settle directly against your spot balances at the bundle-equivalence mint price - no spread paid, no slippage, no liquidity required. See the Converting tokens concept for the math, and the Token conversions guide for end-to-end code. All four methods use the same userOutcome action envelope, sign via L1 agent signing, and return a WalletActionResult. None of them throw.
type WalletActionResult = {
  success: boolean;
  error?: string;
  filledSz?: string;
  avgPx?: string;
  oid?: number;
};
Conversions affect your spot balances immediately on success: true. The SDK does not preview or simulate. Validate inputs before calling.

splitOutcome(params)

Burn X USDH and mint X Yes shares + X No shares of one outcome.
await adapter.trading.splitOutcome({
  outcome: 5160,
  amount: "100",
});

Parameters

outcome
number
required
Numeric outcome ID. Matches market.outcomeId on a fetched HIP-4 market.
amount
string
required
USDH amount to split, as a decimal string (e.g. "12.5"). The SDK strips trailing zeros to match Hyperliquid’s wire format. X USDH burned → X Yes + X No minted.

Return type

Promise<WalletActionResult> - never throws.

mergeOutcome(params)

The inverse of splitOutcome. Burn X Yes + X No of one outcome and mint X USDH.
await adapter.trading.mergeOutcome({
  outcome: 5160,
  amount: "100",
});

// Or burn the max available:
await adapter.trading.mergeOutcome({ outcome: 5160, amount: null });

Parameters

outcome
number
required
Numeric outcome ID.
amount
string | null
required
Paired-share count to merge, as a decimal string. Pass null to merge the maximum available - the protocol burns min(yes_balance, no_balance) shares.

Return type

Promise<WalletActionResult> - never throws.

mergeQuestion(params)

Burn X Yes shares from every outcome of a question (including the fallback) and mint X USDH. Lets you redeem a full Yes-bundle for collateral before the question resolves.
await adapter.trading.mergeQuestion({
  question: 42,
  amount: "10",
});

// Or redeem the max available:
await adapter.trading.mergeQuestion({ question: 42, amount: null });

Parameters

question
number
required
Numeric question ID. For multi-outcome and price-bucket markets, this is market.questionId. Default-binary markets have no parent question - use mergeOutcome instead.
amount
string | null
required
Yes-share count to redeem from each member outcome, as a decimal string. Pass null to redeem the maximum - min(yes_balance) across every outcome of the question.

Return type

Promise<WalletActionResult> - never throws.

negateOutcome(params)

Burn X No shares of one outcome and mint X Yes shares of every other outcome in the same question (including the fallback). Converts “I don’t think this wins” into “I think one of the others wins” without touching the orderbook.
await adapter.trading.negateOutcome({
  question: 42,
  outcome: 5160,
  amount: "5",
});

Parameters

question
number
required
Numeric question ID containing the source outcome.
outcome
number
required
Source outcome ID whose No shares are being converted. Must belong to question.
amount
string
required
No-share count to convert, as a decimal string. After the call, your No balance on outcome drops by amount, and you hold amount additional Yes shares of every other outcome under question.

Return type

Promise<WalletActionResult> - never throws.
The on-wire sub-action key is negateOutcome, matching Hyperliquid’s testnet “Convert Outcomes” UI. Hyperliquid’s docs body shows negateQuestion in places - that’s a typo in their docs. The SDK sends negateOutcome.