Skip to main content

Why This Matters

Building a global-markets trading product on-chain means integrating with perp DEXes, handling EIP-712 signing flows, managing deposits and withdrawals across bridges, and tracking positions with funding rates. We’ve done that - you get one API that covers everything to get your trading product live in days.

What It Does

Let users trade stocks (AAPL, TSLA, NVDA, etc.), commodities (GOLD, OIL, NATGAS), and forex pairs (EUR, GBP, JPY) as perpetual futures. Build products that offer leveraged exposure to stock, commodity, and forex markets, portfolio diversification beyond crypto, or automated trading strategies - all through a single API. You call our API, we return EIP-712 typed data. Your users sign off-chain (no gas needed for trades). Users trade real-world assets on-chain, you capture fees. How it works: Global Markets runs on Hyperliquid’s builder-deployed perpetual futures DEX. Users deposit USDC on Arbitrum, which bridges to Hyperliquid where they can trade perps tracking real-world asset prices.

Supported Assets

Assets are organized into three categories:
CategoryExamples
StocksAAPL, TSLA, NVDA, AMZN, GOOGL, META, MSFT, and more
CommoditiesGOLD, SILVER, OIL, NATGAS, COPPER, URANIUM, CORN
ForexEUR, GBP, JPY, AUD, CAD, CHF, CNH
Use the /v2/global_markets_perps/opportunities endpoint to get the full live list with open interest, 24h volume, funding rates, and max leverage for each market.

Getting Started

1. Deposit USDC

Depositing is a two-step flow with two different actors:
  1. Compass prepares the permit (gasless for the end-user). Your client calls POST /v2/global_markets_perps/deposit and Compass returns an EIP-2612 permit struct. The end-user signs it off-chain — no gas, no Arbitrum tx from their wallet.
  2. You broadcast the bridge tx (you pay the gas). With the signed permit, you call batchedDepositWithPermit on Hyperliquid’s Bridge2 contract on Arbitrum (0x2Df1c51E09aECF9cacB7bc98cB1742757f163dF7) from a sponsor wallet you fund. Hyperliquid’s validators watch Bridge2 events and credit the user’s HL trading account automatically — no /exchange call is needed.
The gas sponsor for the bridge tx is your wallet, not Compass. Compass does not run a sponsor key on Arbitrum for this product. Pre-fund an Arbitrum hot wallet with enough ETH for bridge gas before going live, and keep its private key in your server-side secrets.

Step 1 — get the permit

Request (POST /v2/global_markets_perps/deposit):
  • owner — the end-user’s EOA address on Arbitrum
  • amount — USDC amount to deposit, human-readable (e.g. "100")
Response:
  • permit — EIP-712 typed data for the USDC permit; pass to the user’s wallet for signing
  • amount_raw — USDC amount in raw 6-decimal units; pass to the bridge call in Step 2
  • destination — the address that will be credited on Hyperliquid (equals the deposit owner EOA — HL keys accounts by EVM address)

Step 2 — broadcast the bridge tx

Call POST /v2/global_markets_perps/deposit/sponsor_prepare. Compass returns a fully-encoded unsigned Bridge2.batchedDepositWithPermit tx. You sign and broadcast it from your sponsor wallet on Arbitrum — no ABI/encoding work on your side, no Compass-side key custody. Request:
  • owner — the user’s EOA address on Arbitrum (the permit signer)
  • amount_raw — from the /deposit response in Step 1
  • deadline — from permit.message.deadline in the Step 1 response
  • signature — the user’s signed permit (65-byte hex, 0x-prefixed)
  • sender — your sponsor wallet address on Arbitrum (sets from and nonce on the returned tx)
Response:
  • transaction — an unsigned Arbitrum tx; sign with your sponsor key and submit via any RPC
Bridge2 can silently no-op without reverting. A success receipt is not proof the deposit landed. After confirmation, scan receipt.logs for a USDC Transfer event (topic 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef) from USDC on Arbitrum (0xaf88d065e77c8cC2239327C5EDb3A432268e5831) before telling the user the deposit succeeded.

2. One-Time Setup

Before placing the first trade, two one-time actions are needed: Enable Unified Account - Call /v2/global_markets_perps/enable_unified_account to check and enable unified margin mode. If already enabled, returns null typed data (no action needed). Approve Builder Fee - Call /v2/global_markets_perps/approve_builder_fee to authorize the builder address that should receive trading fees from this end-user’s orders, naming a maximum fee rate. Sign the returned EIP-712 data and submit via /execute. The builder block is required on this endpoint — the Compass customer specifies the address and rate. The full setup, viewing flow, and troubleshooting live in the Collecting Builder Fees section below.

3. Place Orders

Market Order:
from compass_api_sdk import CompassAPI

with CompassAPI(api_key_auth="YOUR_API_KEY") as compass:
    # Prepare a market order to buy 1 AAPL perp
    order = compass.global_markets_perps.global_markets_perps_market_order(
        owner="0xYourWalletAddress",
        asset="AAPL",
        side="buy",
        size="1",
    )

    # order.typed_data contains EIP-712 data for the user to sign
    # After signing, submit via /execute endpoint
Limit Order: Call /v2/global_markets_perps/limit_order with a price parameter and optional time_in_force (GTC or ALO). Same sign-and-execute flow. Cancel Order: Call /v2/global_markets_perps/cancel_order with the order ID to cancel an open limit order.

4. Execute Signed Actions

All prepare endpoints (market_order, limit_order, cancel_order, withdraw, approve_builder_fee) return EIP-712 typed data. After the user signs, submit the signature to the execute endpoint:
from compass_api_sdk import CompassAPI

with CompassAPI(api_key_auth="YOUR_API_KEY") as compass:
    result = compass.global_markets_perps.global_markets_perps_execute(
        action=order.action,
        nonce=order.nonce,
        signature="0xUserSignature...",
    )

5. Track Positions

Call /v2/global_markets_perps/positions to get open positions with size, entry price, mark price, PnL, liquidation price, leverage, and cumulative funding.
from compass_api_sdk import CompassAPI

with CompassAPI(api_key_auth="YOUR_API_KEY") as compass:
    positions = compass.global_markets_perps.global_markets_perps_positions(
        owner="0xYourWalletAddress",
    )

    for pos in positions.positions:
        print(f"{pos.asset}: {pos.size} @ {pos.entry_price} (PnL: {pos.unrealized_pnl})")

Collecting Builder Fees

When your users open positions, every fill on Hyperliquid charges a trading fee. By attaching a builder block to each order, that fee is routed to an address you control. You choose the rate (up to whatever each end-user has authorized), Hyperliquid pays the fee on settlement, and the USDC accumulates on your builder address — no custody, no invoicing.

Mental Model

Hyperliquid enforces builder fees with a strict two-rule consent model:
1

One-time consent (per end-user, per builder)

Before any order with a builder field will be accepted, the end-user must sign an approveBuilderFee action naming your builder address and the maximum rate they authorize. This signature is submitted once and recorded on the L1 order book.
2

Per-order opt-in

Each order that should pay a fee includes a builder block — {address, max_fee_rate}. The order’s max_fee_rate must be ≤ what the user previously approved. Omit the block and the order pays no builder fee at all.
Where do the trades actually happen? Global Markets runs on Hyperliquid’s xyz HIP-3 builder DEX — an off-chain order book on Hyperliquid L1. The fills and fee payments live entirely on L1, not on HyperEVM (HL’s EVM chain). This is why you will not find any of this on Arbiscan / Etherscan / hyperevmscan.io — see Where to see your fees.

Prerequisites

Your builder address must hold ≥100 USDC on its Hyperliquid perps account before any approveBuilderFee action will succeed. This is a hard Hyperliquid rule. If the builder is underfunded, /execute responds with 502 and body "Hyperliquid error: Builder has insufficient balance to be approved". Pre-fund the address once with at least 100 USDC and the rule is satisfied forever.
Checklist before your first trade:
  1. Builder address — a regular EOA you control. Don’t use a smart-contract address unless you’ve validated HL accepts it.
  2. Builder pre-funding — deposit ≥100 USDC into the builder’s Hyperliquid perps account. Use the HL UI, an L1 transfer from another HL account, or bridge from Arbitrum through Bridge2 + a usdClassTransfer action.
  3. End-user margin — the end-user must have enough USDC margin on HL to actually place the order. Compass provides /v2/global_markets_perps/deposit for the Arbitrum → HL bridge step.

TypeScript Walkthrough

Pin the SDK to a tested release and point at staging while validating:
npm install @compass-labs/api-sdk@2.2.53-rc.0 viem
Step 1 — One-time builder approval
const approve =
  await compass.globalMarketsPerps.globalMarketsPerpsApproveBuilderFee({
    owner: endUser.address,
    builder: { address: BUILDER_ADDRESS, maxFeeRate: MAX_FEE_RATE },
  });

const approveSig = await endUser.signTypedData(approve.typedData as any);

await compass.globalMarketsPerps.globalMarketsPerpsExecute({
  action: approve.action,
  nonce: approve.nonce,
  signature: approveSig,
});
Run this once per (end-user, builder) pair. To later charge a higher rate, re-run with a larger maxFeeRate — the user signs again. Lower rates require no re-approval. Step 2 — Place an order that pays the fee
const order =
  await compass.globalMarketsPerps.globalMarketsPerpsMarketOrder({
    owner: endUser.address,
    asset: "AAPL",
    side: "buy",
    size: "0.1",
    slippagePercent: 1.0,
    reduceOnly: false,
    // Optional. Omit to place the order with no builder fee.
    builder: { address: BUILDER_ADDRESS, maxFeeRate: MAX_FEE_RATE },
  });

const orderSig = await endUser.signTypedData(order.typedData as any);

await compass.globalMarketsPerps.globalMarketsPerpsExecute({
  action: order.action,
  nonce: order.nonce,
  signature: orderSig,
});
The same builder field is accepted on globalMarketsPerpsLimitOrder. cancel_order and withdraw do not take a builder field — fees are charged on fills only.
The builder field is optional on market_order and limit_order. Omit it to place a no-fee order — useful for testing, gifted trades, or running a no-monetization mode. It remains required on approve_builder_fee, since that endpoint’s whole purpose is to record consent for a specific builder.
Step 3 — Verify the fee was earmarked
const activity = await compass.globalMarketsPerps.globalMarketsPerpsActivity(
  { owner: endUser.address, builder: BUILDER_ADDRESS },
);

console.log(activity.fills?.[0]);
// {
//   asset: "AAPL",
//   side: "buy",
//   price: "297.74",
//   size: "0.03",
//   fee: "0.001664",
//   builderFee: "0.000893",   // ← portion of the user's fee earmarked for you
//   ...
// }

console.log(activity.builderApproval);
// {
//   builder: "0xYOUR_BUILDER_ADDRESS",
//   max_fee_tenth_bps: 10,        // 0.01% (10 tenth-bps == 1 basis point)
//   approved: true,
// }
The builderFee figure here is what HL has earmarked. The actual credit to your builder address may post later — see Settlement Timing.

Where to See Your Fees

Because Hyperliquid trades live on an off-chain L1 order book, there is no Etherscan-style transaction trace for the fee payment. Use the right view for the level you care about: 1. Lifetime totals — userFees info API. Updates as soon as HL recognizes the fee.
# Lifetime fees earned (across all roles — taker, maker, builder)
curl -s -X POST https://api.hyperliquid.xyz/info \
  -H 'Content-Type: application/json' \
  -d '{"type":"userFees","user":"0xYOUR_BUILDER_ADDRESS"}' \
  | jq

# Same, scoped to the xyz HIP-3 DEX (where Global Markets trades live)
curl -s -X POST https://api.hyperliquid.xyz/info \
  -H 'Content-Type: application/json' \
  -d '{"type":"userFees","user":"0xYOUR_BUILDER_ADDRESS","dex":"xyz"}' \
  | jq
Look for feesEarnedAsBuilder / builderFees / similar — Hyperliquid adds builder-specific roll-ups incrementally, exact key names vary by release. 2. Current spendable balance — clearinghouseState.
Balances on HIP-3 DEXes are siloed per-DEX. Builder fees from Global Markets accrue on the xyz DEX, not the main perps DEX. Query both — your main account looking unchanged is expected.
# Main perps account — pre-funding, main-perps withdrawals
curl -s -X POST https://api.hyperliquid.xyz/info \
  -H 'Content-Type: application/json' \
  -d '{"type":"clearinghouseState","user":"0xYOUR_BUILDER_ADDRESS"}' \
  | jq '.marginSummary.accountValue, .withdrawable'

# xyz DEX account — where Global Markets builder fees land
curl -s -X POST https://api.hyperliquid.xyz/info \
  -H 'Content-Type: application/json' \
  -d '{"type":"clearinghouseState","user":"0xYOUR_BUILDER_ADDRESS","dex":"xyz"}' \
  | jq '.marginSummary.accountValue, .withdrawable'
3. Per-event audit trail — userNonFundingLedgerUpdates. Every non-funding balance change on your builder address.
curl -s -X POST https://api.hyperliquid.xyz/info \
  -H 'Content-Type: application/json' \
  -d '{
    "type":"userNonFundingLedgerUpdates",
    "user":"0xYOUR_BUILDER_ADDRESS",
    "startTime": 1779000000000
  }' \
  | jq
4. Hyperliquid official portfolio UI. https://app.hyperliquid.xyz/portfolio/0xYOUR_BUILDER_ADDRESS — there’s a DEX selector at the top (defaults to “Main”); switch to xyz to see balances there. 5. Compass dashboard. Every end-user attributed to your API key shows up in the Hyperliquid tab at https://compasslabs.ai/dashboard/hyperliquid with their positions, recent fills (each fill carries builder_fee), and their current approval state.

Settlement Timing

The builderFee field on a fill is earmarked the instant the trade matches, but the credit to your builder address may post later:
  • Immediate — credit appears in clearinghouseState (xyz-DEX-scoped) within seconds.
  • Hourly settlement — HL settles certain HIP-3 builder-fee accruals at the funding interval (top of each UTC hour). If your trade was at 15:27 UTC and you query at 15:35 UTC, the credit may not have posted yet. Re-check after 16:00 UTC.
userFees is the most reliable “did HL recognize the fee at all?” signal — it ticks up regardless of when the credit posts to your balance.

Economics

For a single fill:
  • fee on the fill — total fee charged to the end-user, in USDC.
  • builderFee on the fill — portion of fee earmarked for your builder, in USDC. Equals fee × (builder.max_fee_rate / total_user_fee_rate).
  • The remainder of fee accrues to Hyperliquid (validators / DEX operator).
At max_fee_rate = "0.01%" (1bp) the typical split is roughly:
ComponentApproximate share
Builder fee → your address~54% of the gross fee
HL validator + xyz DEX fee~46% of the gross fee

Withdrawing Your Fees

Builder fees accumulate as USDC margin on your builder’s HL perps account (xyz-DEX-scoped for Global Markets). To move them to Arbitrum:
  1. Transfer USDC from the xyz DEX-scoped perps account to the main perps account (HL usdClassTransfer with dex: "xyz").
  2. Withdraw from HL to Arbitrum via Compass’s /v2/global_markets_perps/withdraw (sign + /execute), or via HL’s own UI / Python SDK.
  3. The withdrawal arrives as a Bridge2 USDC Transfer to your builder address on Arbitrum — this step shows up on Arbiscan.

Troubleshooting

Your builder address has less than 100 USDC margin on its HL perps account. Pre-fund it once with ≥100 USDC and retry. Verify with:
curl -s -X POST https://api.hyperliquid.xyz/info \
  -H 'Content-Type: application/json' \
  -d '{"type":"clearinghouseState","user":"0xYOUR_BUILDER_ADDRESS"}' \
  | jq '.marginSummary.accountValue'
The runnable test script (see below) pre-flights this check before asking the user to sign anything.
The end-user hasn’t signed an approveBuilderFee for this builder address, or the rate on the order exceeds the approved max. Either re-run Step 1 with a higher maxFeeRate, or lower the per-order maxFeeRate on the order itself.Pre-check with /activity?builder=…:
const a = await compass.globalMarketsPerps.globalMarketsPerpsActivity({
  owner: endUser.address,
  builder: BUILDER_ADDRESS,
});
console.log(a.builderApproval);
// { builder: "0x...", max_fee_tenth_bps: 10, approved: true }
Two most likely causes:
  1. DEX scope — query clearinghouseState with dex: "xyz". The fee landed on the xyz-DEX-scoped account, not main perps.
  2. Settlement timing — HL may settle the credit at the next hourly funding interval. Re-query after the next UTC hour rolls.
The userFees info call is the canary: it ticks up as soon as HL recognizes the fee, regardless of where/when the balance credit posts.
Expected — Global Markets trades happen on HL’s off-chain L1 order book, not on any EVM chain. No EVM transaction is emitted for the fee. Use the HL info API queries or https://app.hyperliquid.xyz/portfolio/0xYOUR_BUILDER_ADDRESS (xyz DEX selector) instead.
HL builder approval is keyed to a regular EOA. Multisigs and contract wallets may not be honoured as builders. Use a controlled EOA whose private key you can rotate (e.g. a dedicated treasury EOA, or a sub-account on the customer’s wallet provider).

Runnable Test Script

A complete TypeScript script that exercises the full approve-then-trade-then-verify flow against staging lives at api/scripts/examples/builder-fees in the monorepo. It pre-flights the builder-funding rule, signs both typed-data payloads with viem, polls /activity for the resulting fill, and prints the collected fee.

Key Concepts

Perpetual Futures - These are not spot tokens. Users trade perps that track real-world asset prices. Positions accrue funding rates and can be leveraged. EIP-712 Signing - Unlike Yield or Credit which return unsigned Ethereum transactions, Global Markets returns EIP-712 typed data for Hyperliquid’s off-chain order system. Users sign off-chain and submit signatures - no gas is needed for trades. Prepare + Execute Flow - Every action follows a two-step flow: (1) call a prepare endpoint to get EIP-712 typed data, (2) user signs, (3) call /execute with the signature. USDC on Arbitrum - Deposits and withdrawals use USDC on Arbitrum, bridged to/from Hyperliquid.

Use Cases

Brokerage App: Build a stock trading app where users trade stock perps with crypto. Show familiar stock tickers, price charts, and portfolio views while executing trades on-chain. Example: User deposits 1,000 USDC, buys 2 AAPL perps at 200 each. AAPL rises 5%, user’s position is worth 1,020. Close position for 20 profit. Diversification Tool: Let crypto-native users diversify into stock, commodity, and forex exposure without leaving the on-chain ecosystem. Offer one-click allocation across stocks, gold, and forex. Example: User allocates 10,000 USDC — 40% NVDA, 30% GOLD, 30% EUR — all as perps with a single deposit from their existing Arbitrum wallet. Automated Trading: Build bots that execute strategies across stock, commodity, and forex markets on-chain. Arbitrage between crypto and real-world asset perps, or run momentum strategies on stock perps.

Next Steps

Earn

Earn yield on idle USDC while not trading.

Credit

Borrow against crypto holdings for more trading capital.

Bridging

Move assets across chains to fund your trading account.