Skip to main content
Portal’s React Native SDK lets your users trade perpetual futures on venues such as Hyperliquid through the portal.perps.yieldxyz API. This guide covers discovering venues and markets, funding a trading account, opening and managing leveraged positions, and signing, submitting and tracking each action with the Portal wallet.

Overview

With portal.perps.yieldxyz you can:
  • Discover the trading venues (providers) and their markets, with mark and oracle prices, funding rates, leverage ranges, fees and price candles
  • Fund a trading account and complete any one-time venue approvals
  • Open, manage and close leveraged long or short positions, and withdraw collateral
  • Execute an action in one call: the SDK signs each transaction with the Portal wallet in the format it requires (EIP-712 typed data or an on-chain transaction), submits it to Yield.xyz and tracks the action to a final status
  • Track positions, orders, account balances, past actions and venue events
The Yield.xyz Perps integration page explains how perpetual-futures trading works. Every method except executeAction and signAndSubmitAction resolves with the Portal API response, wrapped as { data }; those two resolve with the execution result itself. Venues are identified by an opaque providerId (for example hyperliquid); real chains, where they appear, use CAIP-2 (eip155:42161 for Arbitrum One).
This guide uses Hyperliquid, the launch venue, as the example. A venue’s supported actions and the args each action takes come from getProviders (supportedActions and argumentSchemas), and how each transaction is signed follows the signingFormat Yield.xyz returns on it, not the venue.

Prerequisites

Before using perps operations, ensure you have:
  • A properly initialized Portal client (see Getting started)
  • An active wallet with collateral (USDC) and gas on the funding chain (see Create a wallet)
  • Yield.xyz integration enabled in your Portal Dashboard (see Yield.xyz Perps)
  • A gatewayConfig entry for Arbitrum One (eip155:42161), the default signingChainId that EIP-712 signing requests are routed through, and for every chain an on-chain leg runs on. The default gatewayConfig covers the 11 built-in chains, Arbitrum One included. A custom gatewayConfig is used exactly as supplied, so keep eip155:42161 in it (see Custom RPC configuration).
There is no testnet for perps. The Yield.xyz Perps API is production-only: every action is real money on mainnet. Perpetual futures are high-risk: leverage amplifies losses, and a position can be liquidated, losing its entire margin, within minutes.
Perps require a Portal client without account abstraction. Hyperliquid verifies every action by recovering the signer from an EIP-712 signature, so funds deposited from a smart-contract wallet can be neither traded nor withdrawn. The Portal API rejects createAction and submitTransaction from an account abstraction enabled client with INTEGRATION_REQUIRES_EOA_CLIENT.
The examples in this guide use a portal instance:
Every account method (createAction, getPositions, getOrders, getBalances, getActions, getActivity, getEvents) takes an optional address that defaults to the Portal wallet’s EVM address (portal.addresses). A custom address is for the read methods: signAndSubmitAction signs only transactions built for the Portal wallet’s address, so an action created for another address fails with SIGNER_ADDRESS_MISMATCH.
On React Native, a request that fails at the network level (with no HTTP status) can resolve with an empty response instead of rejecting, leaving data undefined. Check data before using the result of a read or createAction. signAndSubmitAction handles this itself; executeAction throws a plain Error (see Handling errors).

Discovering venues

getProviders lists the trading venues, the actions each supports and the JSON schema of each action’s args. getProvider returns one venue.
Use the providerId values getProviders returns rather than a fixed list, and treat argumentSchemas as the source of truth for the args each action needs on a venue.

Discovering markets

getMarkets lists the tradable markets, getMarket returns one by id, and getMarketCandles returns price candles for a chart.
Each market carries baseAsset and quoteAsset, leverageRange ([min, max]), supportedMarginModes, markPrice, oraclePrice, fundingRate with fundingRateIntervalHours, makerFee and takerFee, openInterest and minSize. Treat market ids as opaque values returned by getMarkets; do not build them yourself. The examples use hyperliquid-eth-usdc for illustration; use an id that getMarkets returned. getMarketCandles requires an interval (1m, 5m, 15m, 1h, 4h or 1d) and a from timestamp, with an optional to.

Funding a trading account and approving the venue

A perps venue holds collateral in a trading account, separate from the wallet’s on-chain balance. Before a user can open a position, fund that account and complete any one-time approvals the venue lists in its supportedActions. Like every perps action, funding takes two calls: createAction builds the action with its unsigned transactions, and signAndSubmitAction signs, submits and tracks it (see Signing and execution).
bridge2 deposits below 5 USDC are not credited, and the funds are lost. Check the amount before you build a bridge2 fund action.
fund returns on-chain EVM_TRANSACTION legs on the source chain (for example a USDC approval and the transfer), so the wallet needs gas there. args.amount is in units of the source token, not USD: from native ETH (fromToken without address) the same '10' means 10 ETH. fundingMethod selects the route: bridge2 bridges USDC from Arbitrum One, and lifi routes from other EVM chains and tokens. See Funding sources and limits for the venue’s limits.
A fund action reaches SUCCESS when the source-chain transaction confirms, not when the collateral lands on the venue. Poll getBalances until availableBalance reflects the deposit before you show a funded state.
Build the one-time approvals a venue lists in supportedActions the same way. Hyperliquid, for example, can place orders through an agent wallet the user authorizes once with approveAgent, and may require approveBuilderFee:

Opening a position

Open a position with action: 'open'. The fields that size the order, and their units, are defined by the venue’s argumentSchemas.open, so read that schema before you build the arguments.
createAction takes: createAction resolves with { data: action }. The action carries its id, its status, a summary of the trade (direction, size, leverage, collateral, fee, estimatedLiquidationPrice and similar; simpler or null for non-trade actions such as fund) and its transactions, each with a signingFormat and signablePayload. Nothing is signed yet.

Managing a position

Every management action follows the same createAction and signAndSubmitAction flow; only action and args differ. Each venue’s argumentSchemas describes the exact args.
Each position and order also lists pendingActions, the actions available for it now, with pre-filled args you can pass to createAction (see Positions, orders and balances).

Closing a position and withdrawing

Close a position with action: 'close', then withdraw collateral from the trading account with action: 'withdraw'.
On Hyperliquid, withdrawals return USDC on Arbitrum One to the signing address, with a flat fee and a minimum; see Funding sources and limits.

Positions, orders and balances

getPositions and getOrders take at least one venue (providerId or providerIds). getBalances takes exactly one venue and returns a single balance.
A position carries marketId, side, size, entryPrice, markPrice, leverage, marginMode, margin, unrealizedPnl, funding, liquidationPrice and pendingActions. A balance carries accountValue, usedMargin, availableBalance and unrealizedPnl. Calling getPositions or getOrders without a venue, or getBalances with none or several, throws before any request is sent.

Signing and execution

How an action is executed

signAndSubmitAction(action, options?) signs and submits an action’s transactions, then tracks it:
  1. It goes through the action’s transactions in order. A transaction awaits a signature only while it is CREATED. Before signing one, the SDK reads the action back: a transaction Yield.xyz no longer lists as CREATED is skipped, and the payload signed is the one Yield.xyz lists now.
  2. It signs each transaction according to its signingFormat:
    • EIP712_TYPED_DATA (Hyperliquid orders and account changes such as updateLeverage): signed off-chain with eth_signTypedData_v4, routed through signingChainId (Arbitrum One by default), and submitted as signedPayload with the action id. The typed data is signed as Yield.xyz returns it: its domain chainId (1337 on Hyperliquid) is not rewritten and is never used for routing. The SDK only adds types.EIP712Domain when it is missing.
    • EVM_TRANSACTION (on-chain legs such as the fund approval and transfer): sent with eth_sendTransaction on the transaction’s own chain, confirmed on-chain, then submitted as transactionHash.
  3. It polls the action until it reaches a final status (SUCCESS, FAILED, CANCELED or STALE), signing any transaction Yield.xyz adds meanwhile, for example while the action is WAITING_FOR_NEXT.
  4. It resolves when the action reaches SUCCESS. An action that ends FAILED, CANCELED or STALE throws ACTION_FAILED, and any other failure throws a YieldXyzPerpsExecutionError.
Yield.xyz reports a submitted transaction as QUEUED. Only CREATED transactions are signed, so running signAndSubmitAction again on the same action never re-signs a transaction Yield.xyz has already recorded. An on-chain leg broadcast without its hash being returned is not recorded yet and stays CREATED, so a blind re-run can broadcast it again (see Resuming a failed execution). Before signing, the SDK checks that the signingFormat is EIP712_TYPED_DATA or EVM_TRANSACTION (Solana and Cosmos payloads are not supported yet), that the transaction and an on-chain payload’s from were built for the Portal wallet’s EVM address (portal.addresses), and that the chain the request is routed through has a gatewayConfig entry. That chain check applies to per-call signers too. For on-chain legs, only from, to, data, gasLimit (sent as gas) and value are forwarded: the signer sets the nonce and fees. Each signature goes through Portal’s signing flow. Unless autoApprove is true, approve each request in your portal_signingRequested handler (see Add custom signature hooks). A rejected request stops the execution with SIGNATURE_REJECTED. Without a portal_signingRequested handler, the request throws before anything is signed, so the execution stops with SIGNING_FAILED.

Executing in one call

executeAction(params, options?) calls createAction and then signAndSubmitAction. Use the two-call flow when you want to show the summary before the user signs.
When createAction fails, executeAction throws the Portal API error as it is, or a plain Error when createAction resolved with an empty response: no action exists yet, so there is nothing to resume.

Options

Both methods take the same YieldXyzPerpsExecuteOptions:

Progress events

onProgress receives a YieldXyzPerpsExecutionProgress with the step, the actionId and the fields that apply to that step: An EIP-712 transaction goes signing → submitted; an on-chain leg goes signing → confirming → confirmed → submitted. index counts the transactions this call signed or submitted before this one.

Return value

Both methods resolve with a YieldXyzPerpsExecutionResult:

Using your own signers

Pass signTypedData, signAndSendTransaction or waitForConfirmation to replace one part of the signing, for example to add a confirmation step. The signing chain must still have a gatewayConfig entry, and every transaction must have been built for the Portal wallet’s EVM address.
signTypedData receives the wallet address, the JSON-encoded typed data and the CAIP-2 chainId to route through, and resolves with the 65-byte signature. signAndSendTransaction receives the eth_sendTransaction params and the CAIP-2 chain id, and resolves with the transaction hash. Both resolve with undefined when the request is declined.

Resuming a failed execution

A failed execution throws a YieldXyzPerpsExecutionError that carries the actionId, the transactionId and action, the latest known state of the action. Resume with signAndSubmitAction(error.action), or with an action read back with getAction: transactions Yield.xyz already recorded are never signed again. When the error carries a broadcastHash, that on-chain leg was already broadcast and Yield.xyz may not have recorded it yet. Pass it in broadcastHashes, keyed by transaction id, so the SDK confirms and submits that hash instead of signing and broadcasting the leg again.
Not every failure should be retried as is:

Handling errors

Three kinds of error reach your app:
  • Portal API errors, thrown by the read methods and createAction, and carried as cause by execution errors. On React Native they are HttpErrors with the HTTP status, the stable error id, details and the API’s errorMessage. getYieldXyzPerpsApiError(error) returns the API error whether it was thrown directly or wrapped.
  • YieldXyzPerpsExecutionError, thrown by signAndSubmitAction and executeAction. Check it with YieldXyzPerpsExecutionError.is(error) and branch on reason.
  • A plain Error, thrown by executeAction when createAction resolves with an empty response (see the note at the top of this guide). It is neither of the above, and nothing was signed: create the action again.
The Portal API answers with 400 (invalid request, a precondition the venue rejected such as insufficient margin, a missing or rejected Yield.xyz API key, or one of the ids below), 404, 429, 503, or 500 with id: INTEGRATION_RESPONSE_SCHEMA_DRIFT. When the venue returns a machine-readable code, it is in details.code. See Errors on the integration page. While executing, the SDK retries rate-limited (429) submissions and tolerates up to five consecutive transient failures while reading the action back. After an ambiguous submission failure, it resubmits a transaction hash once, and only if the action shows Yield.xyz did not record it; it never resubmits a signature. Signing is never retried. YieldXyzPerpsErrorId values: YieldXyzPerpsExecutionErrorReason values: The error also carries broadcastHash (set once an on-chain leg was broadcast), transactionHash and cause.

Activity and events

getActions lists the wallet’s actions (filter by providerId, marketId, type, status or statuses) and getAction reads one. getEvents lists venue-side outcomes the user did not initiate, such as order fills, liquidations and stop-loss or take-profit triggers, and getEvent reads one. getActivity interleaves actions and events in a single feed, newest first.
The list methods take offset and limit (from 1 to 100) and resolve with a page: { data: { total, offset, limit, items } }. getEvents also filters by eventType or eventTypes, marketId, perpActionId, providerOrderId and fromDate / toDate (ISO-8601).

Low-level APIs

signAndSubmitAction covers signing, submitting and tracking. If you sign the transactions yourself, use the low-level methods with the Enclave MPC API signing flow as the reference:
submitTransaction throws before any request is sent when neither signedPayload nor transactionHash is given, or when signedPayload comes without actionId. There is no step method in Perps: poll getAction until the action reaches a final status, and sign the transactions Yield.xyz adds while it is WAITING_FOR_NEXT.

Best practices

  1. Show the summary (direction, size, leverage, collateral, estimatedLiquidationPrice) before the user signs: Portal adds no trading guardrails
  2. Fund and approve first: a position cannot open until the trading account has collateral and the venue’s approvals are complete
  3. Respect the venue’s limits before building an action, for example Hyperliquid’s 5 USDC bridge2 minimum and its minimum order notional
  4. Sign soon after building: on-chain legs carry the gas limit Yield.xyz estimated when the action was built, so create a new action if the user waits long before signing
  5. Persist error.action and broadcastHashes so a failed execution can be resumed without broadcasting an on-chain leg twice
  6. Handle INTEGRATION_RESPONSE_SCHEMA_DRIFT as a feature outage rather than a user error
  7. Keep gas on the wallet for the on-chain funding legs

Supported venues

Perps launches on Hyperliquid; the exact list of live venues is returned by getProviders. On-chain funding legs settle on real chains (for example Arbitrum One, eip155:42161), and each needs a gatewayConfig entry to be signed.

Next Steps