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.

This guide walks you through the full integration path, meaning that by the end, you’ll have a working adapter, a list of available markets, and order submissions functional and verifiable (via the Hyperliquid exchange).
Use testnet: true while you’re getting started. The Hyperliquid testnet gives you a free environment to experiment without risking real funds.
1

Install the SDK

Add @outcome.xyz/hip4 to your project:
pnpm add @outcome.xyz/hip4
2

Create and initialize the adapter

Import createHIP4Adapter and call initialize() to warm up the event cache. Every other SDK method depends on this step completing first.
import { createHIP4Adapter } from "@outcome.xyz/hip4";

const hip4 = createHIP4Adapter({ testnet: true });
await hip4.initialize();
The adapter automatically connects to the correct Hyperliquid testnet endpoints for both REST and WebSocket traffic. Switch to { testnet: false } when you’re ready for mainnet.
3

Fetch markets

Call fetchMarkets() to retrieve live prediction markets. The response is an array of typed market objects - each one carries pre-computed side identifiers, expiry dates, and outcome metadata.
import type { DefaultBinaryMarket } from "@outcome.xyz/hip4";

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

for (const market of markets) {
  console.log(market.underlying); // "BTC"
  console.log(market.targetPrice); // 69070
  console.log(market.expiry); // Date object
  console.log(market.period); // "1d"
  console.log(market.sides[0].coin); // "#5160"  (Yes side)
  console.log(market.sides[1].name); // "No"
}
defaultBinary markets are recurring price binary markets (e.g. “Will BTC be above $69,070 at expiry?”). You can also filter by "labelledBinary" or "multiOutcome", or omit the type filter to get all markets.
4

Set up authentication

Trading requires an approved ephemeral agent key. The agent key signs orders on your behalf without exposing your main wallet’s private key. The approval flow is a one-time operation per agent.
import {
  getAgentApprovalTypedData,
  submitAgentApproval,
} from "@outcome.xyz/hip4";
import { createWalletClient, http } from "viem";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { arbitrum } from "viem/chains";

// Your user's main wallet (from environment - never hardcode private keys)
const userAccount = privateKeyToAccount(
  process.env.PRIVATE_KEY as `0x${string}`,
);
const walletClient = createWalletClient({
  account: userAccount,
  chain: arbitrum,
  transport: http(),
});

// Generate a fresh ephemeral agent key
const agentPrivateKey = generatePrivateKey();
const agentAccount = privateKeyToAccount(agentPrivateKey);

// Build the EIP-712 approval payload and have the user sign it
const nonce = Date.now();
const typedData = getAgentApprovalTypedData(
  agentAccount.address,
  "My App",
  nonce,
  false, // false = testnet
);

const signature = await walletClient.signTypedData({
  domain: typedData.domain,
  types: typedData.types,
  primaryType: typedData.primaryType,
  message: typedData.message,
});

// Submit the approval to the Hyperliquid exchange
const approval = await submitAgentApproval(
  signature,
  agentAccount.address,
  "My App",
  nonce,
  false, // false = testnet
);

if (!approval.success) {
  throw new Error(`Agent approval failed: ${approval.error}`);
}

// Initialize the adapter with the approved agent
await hip4.auth.initAuth(userAccount.address, agentAccount);

console.log(hip4.auth.getAuthStatus().status); // "ready"
After initAuth() completes, the adapter signs all subsequent orders automatically using the agent key.
5

Place a limit order

With auth initialized, call hip4.trading.placeOrder() to submit a limit order. The method returns a result object - it never throws.
Placing orders on mainnet (testnet: false) uses real funds. Make sure your account has sufficient USDH balance before submitting.
import { getMinShares } from "@outcome.xyz/hip4";
import type { DefaultBinaryMarket } from "@outcome.xyz/hip4";

// Pick a market (reusing the list from step 3)
const market = markets[0] as DefaultBinaryMarket;

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

// Place a limit buy on the Yes side
const result = await hip4.trading.placeOrder({
  marketId: String(market.outcomeId),
  outcome: market.sides[0].coin, // Yes side coin, e.g. "#5160"
  side: "buy",
  type: "limit",
  price: String(markPx),
  amount: String(getMinShares(markPx)), // minimum shares for 10 USDH notional
  markPx, // enables pre-submission min-shares check
});

if (result.success) {
  console.log("Order status:", result.status);
  if (result.orderId) console.log("Order ID:", result.orderId);
  if (result.shares) console.log("Filled shares:", result.shares);
} else {
  console.error("Order rejected:", result.error);
}
getMinShares(markPx) computes the minimum number of shares required to meet the 10 USDH notional floor at the given price. Passing markPx to placeOrder enables the SDK to validate this before signing and submitting.

Next steps

  • Read the core concepts to understand market types, coin naming, and the two signing flows.
  • See the trading guide for market orders, cancellations, and builder fee configuration.
  • Explore the real-time data guide to subscribe to live order books and price streams.