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.

Trading on HIP-4 markets follows a four-step flow: initialize an agent key, fetch the market and side you want to trade, place the order, and inspect the result. The SDK handles signing, tick alignment, and notional validation automatically-placeOrder never throws, so you can always branch on result.success.
You must call hip4.auth.initAuth before placing or cancelling any orders. Attempting to trade without authentication will return { success: false, error: "auth not initialized" }.

Place a limit order

1

Initialize authentication

Generate or load an agent key and pass it to initAuth. The agent key signs orders silently on behalf of the user’s wallet.
import { createHIP4Adapter } from "@hip4/sdk";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";

const hip4 = createHIP4Adapter({ testnet: true });
await hip4.initialize();

// Load your pre-approved agent key
const agentKey = process.env.AGENT_PRIVATE_KEY as `0x${string}`;
const agent = privateKeyToAccount(agentKey);

await hip4.auth.initAuth(userAddress, agent);
The agent must be approved on-chain before trading. See the auth-eoa.ts example in the SDK repository for the full approval flow using getAgentApprovalTypedData and submitAgentApproval.
2

Fetch the market and side

Retrieve the market you want to trade. The sides array on each market object carries the pre-computed coin identifier you pass to placeOrder.
import type { DefaultBinaryMarket } from "@hip4/sdk";

const markets = await hip4.events.fetchMarkets({
  type: "defaultBinary",
}) as DefaultBinaryMarket[];

const market = markets[0];
const yesSide = market.sides[0]; // { name: "Yes", coin: "#5160", asset: 100005160 }
const noSide  = market.sides[1]; // { name: "No",  coin: "#5161", asset: 100005161 }
3

Place the limit order

Call hip4.trading.placeOrder with the market ID, the side coin, and your price and amount. Pass markPx to enable pre-submission min-shares validation.
import { getMinShares } from "@hip4/sdk";

// Fetch current mid price for validation
const priceData = await hip4.marketData.fetchPrice(String(market.outcomeId));
const markPx = parseFloat(priceData.outcomes[0]?.midpoint ?? "0.5");

const result = await hip4.trading.placeOrder({
  marketId: String(market.outcomeId),
  outcome:  market.sides[0].coin,   // "#5160"
  side:     "buy",
  type:     "limit",
  price:    String(markPx),
  amount:   String(getMinShares(markPx)),
  markPx,                            // enables min-shares check before signing
  builderAddress: "0xYourAddress",   // optional: builder referral address
  builderFee: 100,                   // optional: 0.1% (100 tenths of a basis point)
});
4

Check the result

Inspect result.success before reading other fields. The status field tells you whether the order filled immediately or is resting on the book.
if (result.success) {
  console.log("Status:", result.status);    // "filled" | "resting"
  console.log("Order ID:", result.orderId);
  console.log("Shares filled:", result.shares);
} else {
  console.error("Order rejected:", result.error);
}

Place a market order

For a market order, set type: "market" and omit price. The SDK uses the FrontendMarket time-in-force with extreme prices (0.99999 for buys, 0.00001 for sells) so the exchange handles best-execution.
const result = await hip4.trading.placeOrder({
  marketId: String(market.outcomeId),
  outcome:  market.sides[0].coin,
  side:     "buy",
  type:     "market",
  amount:   "20",
});

if (result.success) {
  console.log(`Filled ${result.shares ?? "?"} shares - status: ${result.status}`);
} else {
  console.error("Failed:", result.error);
}

Cancel an order

Pass an array of cancel targets to hip4.trading.cancelOrder. Each target requires the marketId, orderId, and the outcome coin (used to resolve the correct asset ID).
await hip4.trading.cancelOrder([
  {
    marketId: "516",
    orderId:  "12345",
    outcome:  "#5160",
  },
]);
cancelOrder throws on failure. Wrap it in a try/catch if you need to handle errors gracefully.

Order params reference

FieldTypeRequiredDescription
marketIdstringYesOutcome ID as a string (e.g. "516")
outcomestringYesSide coin (e.g. "#5160")
side"buy" | "sell"YesDirection
type"limit" | "market"YesOrder type
pricestringLimit onlyLimit price (e.g. "0.65")
amountstringYesNumber of shares
timeInForce"GTC" | "GTD" | "FOK" | "FAK"NoDefault: "GTC"
markPxnumberNoCurrent mark price; enables min-shares validation
builderAddressstringNoBuilder referral address (required when using builderFee)
builderFeenumberNoFee in tenths of basis points - 100 = 0.1%

Pricing rules

Tick-aligned prices - limit order prices are rounded to 5 significant figures before signing. For example, 0.55001 becomes 0.55001 and 0.550012 becomes 0.55001. You don’t need to pre-format prices yourself. Min-notional - orders below 10 USDH notional value are rejected client-side before the order is signed or sent. Notional is calculated as price × amount. Pass markPx to enable this check; omit it to skip it.

Time-in-force options

ValueBehavior
GTCGood Til Cancelled - default for limit orders; rests on the book until filled or cancelled
GTDGood Til Date - currently behaves like GTC (expiration field accepted but not enforced by the exchange)
FOKFill or Kill - mapped to IOC; may result in partial fills
FAKFill and Kill - mapped to IOC
Market orders always use FrontendMarket regardless of the timeInForce field.

Builder fees

Builder fees let you attach a referral address and collect a fee on every order placed through your integration. Set builderFee in tenths of basis points (100 = 0.1%) and provide builderAddress. Both fields must be present together.
const result = await hip4.trading.placeOrder({
  // ...other params
  builderAddress: "0xYourBuilderAddress",
  builderFee: 50, // 0.05%
});