> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rhinestone.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Trade on Hyperliquid

> Fund and trade on Hyperliquid from a smart account in one signed intent.

Use your Rhinestone account to deliver collateral and authorize a Hyperliquid action in one intent. The SDK prepares the action and your account signs it; you never hold or manage a separate trading key.

## Prepare a position

Request 25 USDC of collateral and a \$100 BTC long:

```ts theme={null}
import { hyperCorePerp } from "@rhinestone/sdk";
import { parseUnits } from "viem";
import { base, hyperEvm } from "viem/chains";

const usdcOnHyperEvm = "0xb88339CB7199b77E23DB6E890353E22632Ba630f";

const prepared = await account.prepareTransaction({
  sourceChains: [base],
  targetChain: hyperCorePerp,
  tokenRequests: [{ address: usdcOnHyperEvm, amount: parseUnits("25", 6) }],
  hyperCore: {
    openPerp: {
      asset: "BTC",
      direction: "long",
      notionalUsd: 100,
      slippageBps: 50,
    },
  },
});
```

`tokenRequests` describes **destination collateral**, so use the HyperEVM USDC address even when funding from Base. Rhinestone routes those funds through HyperEVM into your account's HyperCore perp balance. USDC has six decimals: `25_000_000n` is 25 USDC. A first deposit activates the HyperCore account; for this fixed-amount request, the quote includes the additional 1 USDC activation cost.

Leave `recipient` unset: the collateral and action must belong to the account authorizing the intent.

`notionalUsd` is the position size, not the collateral. Alternatively, pass `size` as a decimal string in units of BTC, but never both. This example does **not** set leverage: choose collateral appropriate to the account's existing leverage, margin requirements, and fees. To change leverage, use a [separate action](#use-a-raw-action) before opening the position; a leverage change without new collateral requires an already activated account.

During preparation, the SDK reads the market index, mark price, and precision rules from Hyperliquid. It rounds size down and builds an immediate-or-cancel (`Ioc`) limit order. `slippageBps: 50` allows a price 0.5% through the mark and is also the default. An IOC fills what it can at the limit and cancels the remainder; it does not guarantee a full fill.

<Warning>
  The limit price is fixed during preparation, before signing or bridging. If
  the market moves before the order reaches Hyperliquid, the order can be
  refused even though the collateral has arrived. Collateral delivery and
  exchange execution are not atomic.
</Warning>

## Sign, submit, and check the exchange outcome

Review the selected quote's input, output, fees, and expiry before signing. Follow the [shared transaction flow](/transactions/multichain/end-to-end-transaction-flow) for quote selection and token requirements; smart accounts handle source approvals as part of the intent.

```ts theme={null}
import { IntentFailedError } from "@rhinestone/sdk/errors";

const quote = prepared.quotes.best;
console.log(quote.cost.input, quote.cost.output, quote.cost.fees);

if (Date.now() >= quote.expiresAt * 1_000) {
  throw new Error("Quote expired; prepare and review a new transaction");
}

const signed = await account.signTransaction(prepared, {
  intentId: quote.intentId,
});

if (Date.now() >= quote.expiresAt * 1_000) {
  throw new Error("Quote expired; prepare and sign a new transaction");
}

const submitted = await account.submitTransaction(signed);
console.log(submitted.id, submitted.traceId);

try {
  const status = await account.waitForExecution(submitted);
  if (status.hyperCore?.outcome !== "accepted") {
    throw new Error(
      "No confirmed exchange acceptance; inspect the existing intent",
    );
  }
  console.log("Action accepted; check Hyperliquid for actual fills");
} catch (error) {
  if (error instanceof IntentFailedError) {
    console.error(error.context?.hyperCore, error.context?.operations);
  }
  throw error;
}
```

Keep `quote.intentId` and the submission handle so you can query the same intent after a lost response or interrupted poll:

```ts theme={null}
const latest = await rhinestone.getIntentStatus(quote.intentId);
console.log(latest.status, latest.hyperCore, latest.operations);
```

`waitForExecution` throws `IntentFailedError` for a terminal failure. Read `error.context.hyperCore` as well as the onchain operations. A failed intent can have every operation marked `COMPLETED`: the collateral arrived, but the exchange action did not.

| `hyperCore.outcome` | Meaning and next step                                                                                                                                     |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pending`           | No terminal exchange result yet. Continue checking the existing intent.                                                                                   |
| `accepted`          | Hyperliquid accepted the action. Inspect fills, open orders, and positions to determine the actual execution.                                             |
| `refused`           | Hyperliquid rejected the action. Correct or reprice it before preparing another transaction; check the collateral already delivered.                      |
| `partial`           | Some orders in a single action were placed and others were rejected. Inspect Hyperliquid before retrying; resending the whole batch can duplicate orders. |
| `unknown`           | The action may have executed, but no definitive answer was recorded. Reconcile with Hyperliquid before retrying.                                          |
| `error`             | The action did not complete successfully, for example because its stage expired. Inspect the intent and Hyperliquid account before retrying.              |

`partial` describes mixed order results within a batch, not a partially filled individual order. A single IOC that only partly fills can still report `accepted`. Do not depend on an error `reason` being present, or treat a missing `hyperCore` result as evidence of trade success.

If delivery succeeded, a rejected trade does not return the deposited USDC to the source chain: it stays in the HyperCore balance. Recheck that balance before requesting more collateral. Never create a second intent merely because submission or polling timed out.

## Close or reduce a position

For an activated account with an open BTC position, prepare a close without delivering more collateral:

```ts theme={null}
const preparedClose = await account.prepareTransaction({
  sourceChains: [hyperEvm],
  targetChain: hyperCorePerp,
  hyperCore: {
    closePerp: { asset: "BTC" },
  },
});
```

Sign, submit, and inspect the outcome using the same flow above, with `preparedClose` instead of `prepared`. Keep a real EVM `sourceChains` entry: HyperCore has no account RPC. The transaction still needs fees or sponsorship even though it requests no tokens.

The SDK reads the account's current position and builds a reduce-only IOC for its full size. Pass `size`, such as `"0.0005"`, to request a partial close. Preparation fails with `NoOpenPerpPositionError` if no position exists. Check the remaining position after execution rather than assuming the IOC closed it completely.

Closing a position is not a withdrawal. Collateral remains on HyperCore; these SDK helpers do not move it back to an EVM chain. Plan a separate withdrawal integration before funding a production account; withdrawals are not part of this API.

## Use a raw action

Use `hyperCore.action` for actions not covered by the declarative open and close fields. For example, set BTC leverage on an already activated account:

```ts theme={null}
import { getPerpMarket } from "@rhinestone/sdk/hypercore";

const btc = await getPerpMarket("BTC");
const preparedLeverage = await account.prepareTransaction({
  sourceChains: [hyperEvm],
  targetChain: hyperCorePerp,
  hyperCore: {
    action: {
      type: "updateLeverage",
      asset: btc.assetIndex,
      isCross: true,
      leverage: 5,
    },
  },
});
```

Sign and submit `preparedLeverage`, then wait for acceptance before preparing the opening transaction. Leverage changes do not resize an existing position.

The released `HyperCoreAction` type supports `order`, `cancel`, `cancelByCloid`, `modify`, `batchModify`, `updateLeverage`, and `updateIsolatedMargin`. Supply Hyperliquid's action shape, including valid asset indices, decimal precision, and any order IDs. Use [market reads](/wallets/custom-signer/sdk-reference/hyper-core/get-perp-market) instead of assuming a ticker's index.

<Note>
  SDK 2.16.1 supports **one action per transaction**: choose exactly one of
  `openPerp`, `closePerp`, or `action`. It does not expose an `actions` array. A
  single `order` action can contain multiple orders, which can succeed or fail
  independently. Submit HyperCore intents sequentially for each account, not
  concurrently.
</Note>

This page covers the default perp venue. `hyperCoreSpot` is a separate destination for spot USDC delivery and compatible raw spot actions; it does not share the perp margin balance. Do not mix spot and perp asset indices, or use these examples for HIP-3 markets.

## What your signature authorizes

Your signature covers one concrete action, including its price and size. Changing the trade requires a new prepared transaction and a new signature. You do not separately sign a Hyperliquid exchange request or approve a reusable trading key, and the authorization does not extend to withdrawals or transfers.

Rhinestone delivers any requested collateral, submits the action to Hyperliquid, and records Hyperliquid's answer as the intent's exchange outcome.

For your trading UI, use [getPerpMarkets](/wallets/custom-signer/sdk-reference/hyper-core/get-perp-markets) to list markets and [getPerpPosition](/wallets/custom-signer/sdk-reference/hyper-core/get-perp-position) with `account.getAddress()` and a ticker to read the account's position. Preparation can fail before any submission—for example, for an unknown asset, an order below the minimum, or unavailable market data. Handle these separately from exchange outcomes; `isHyperCoreError` from `@rhinestone/sdk/errors` identifies these SDK errors.
