Skip to main content
Before trading on a DEX, the user must complete certain authorizations. The SDK provides methods to discover, build, sign, and submit these authorizations.
The examples on this page use Hyperliquid (dex: 'hyperliquid'). Authorization keys like ApproveAgent and ApproveBuilderFee are Hyperliquid-specific — other DEXes will have different authorization requirements. Use getDexes() to discover available authorizations for each DEX.

Overview

The authorization flow has four steps:
  1. DiscovergetDexes() returns available authorizations per DEX
  2. CreatecreateAuthorization() builds the payloads to sign
  3. Sign — User signs each typedData with their wallet
  4. SubmitsubmitAuthorization() sends the signed payloads
If using PerpsClient, the buildAuthorization() method handles step 2 and automatically injects the agent address for USER_AGENT mode.

Step 1: Discover authorizations

import { getDexes } from '@lifi/perps-sdk';

const { dexes } = await getDexes(client);
const hl = dexes.find((d) => d.key === 'hyperliquid');

console.log(hl.authorizations);
// [
//   { key: 'ApproveAgent', name: 'Approve Agent', params: [{ name: 'agentAddress', ... }] },
//   { key: 'ApproveBuilderFee', name: 'Approve Builder Fee', params: [] },
// ]

Step 2: Build authorization payloads

const { actions } = await perps.buildAuthorization({
  dex: 'hyperliquid',
  address: userAddress,
  authorizations: [
    { key: 'ApproveAgent' },        // agentAddress auto-injected in USER_AGENT mode
    { key: 'ApproveBuilderFee' },
  ],
});
The backend checks which authorizations are already active. Only unsatisfied authorizations are returned in actions[]. If all are already satisfied, actions will be empty.

Step 3: Sign each action

const signedActions = await Promise.all(
  actions.map(async (a) => ({
    action: a.action,
    typedData: a.typedData,
    // Using viem walletClient:
    signature: await walletClient.signTypedData({ ...a.typedData }),
    // Or using a raw EIP-1193 provider:
    // signature: await provider.request({
    //   method: 'eth_signTypedData_v4',
    //   params: [address, JSON.stringify(a.typedData)],
    // }),
  }))
);

Step 4: Submit signed authorizations

const { results } = await perps.submitAuthorizations({
  dex: 'hyperliquid',
  address: userAddress,
  actions: signedActions,
});

for (const result of results) {
  console.log(result.action, result.success ? 'OK' : result.error);
}
Authorization submission returns 202 Accepted. On-chain processing is asynchronous. Poll getAccount() to verify authorizations are active before placing orders.

Using Service Functions (advanced)

For more control, use the low-level service functions directly:
import { createAuthorization, submitAuthorization } from '@lifi/perps-sdk';

// Build payloads
const { actions } = await createAuthorization(client, {
  dex: 'hyperliquid',
  address: userAddress,
  authorizations: [
    { key: 'ApproveAgent', params: { agentAddress: '0x...' } },
    { key: 'ApproveBuilderFee' },
  ],
});

// Sign each action
const signedActions = await Promise.all(
  actions.map(async (a) => ({
    action: a.action,
    typedData: a.typedData,
    signature: await walletClient.signTypedData({ ...a.typedData }),
  }))
);

// Submit
const { results } = await submitAuthorization(client, {
  dex: 'hyperliquid',
  address: userAddress,
  actions: signedActions,
});

for (const result of results) {
  console.log(result.action, result.success ? 'OK' : result.error);
}

PerpsClient Methods

buildAuthorization

Build authorization payloads for the user to sign.
const response = await perps.buildAuthorization(params);
Parameters:
FieldTypeRequiredDescription
dexstringYesDEX identifier
addressstringYesUser’s wallet address (account owner)
signerAddressstringNoAddress that will sign the payloads. Required for USER_AGENT mode. Defaults to address.
authorizationsAuthorizationInput[]YesAuthorizations to build
In USER_AGENT mode, the PerpsClient automatically sets signerAddress to the agent wallet address. In USER mode, the user signs directly so signerAddress is not needed.
Each AuthorizationInput:
FieldTypeRequiredDescription
keystringYesAuthorization key from dex.authorizations[].key
paramsRecord<string, unknown>NoAuthorization-specific parameters
Returns: { actions: AuthorizationAction[] } Each AuthorizationAction:
FieldTypeDescription
actionstringAuthorization key
descriptionstring?Human-readable description
typedDataobjectEIP-712 typed data to sign

submitAuthorizations

Submit signed authorizations.
await perps.submitAuthorizations(params);
Parameters:
FieldTypeRequiredDescription
dexstringYesDEX identifier
addressstringYesUser’s wallet address (account owner)
signerAddressstringNoAddress that signed the payloads. Required for USER_AGENT mode. Defaults to address.
actionsSignedAuthorization[]YesSigned authorization payloads
Each SignedAuthorization:
FieldTypeRequiredDescription
actionstringYesAuthorization action type
typedDataobjectYesOriginal typedData from buildAuthorization
signaturestringYesUser’s signature of the typedData
Returns: AuthorizationsResponse
FieldTypeDescription
resultsAuthorizationResult[]Result per submitted action
Each AuthorizationResult:
FieldTypeDescription
actionstringAuthorization action key
successbooleanWhether the authorization succeeded
errorstring?Error message if failed

getRequiredAuthorizations

Determine which authorizations (if any) the user needs before trading. Sends the expected authorizations to the backend, which checks on-chain state and returns only those not yet satisfied.
const required = await perps.getRequiredAuthorizations({
  dex: 'hyperliquid',
  address: userAddress,
});

if (required.isReady) {
  // No authorizations needed — ready to trade
} else {
  // Typed data already included — sign directly
  const signedActions = await Promise.all(
    required.userAuthorizations.map(async (a) => ({
      action: a.action,
      typedData: a.typedData,
      signature: await walletClient.signTypedData({ ...a.typedData }),
    }))
  );
  // ... submit with executeAuthorizations ...
}
Parameters:
FieldTypeRequiredDescription
dexstringYesDEX identifier
addressstringYesUser’s wallet address
Returns: RequiredAuthorizationsResult
FieldTypeDescription
userAuthorizationsAuthorizationAction[]Actions requiring user wallet signature (with typed data)
agentAuthorizationsAuthorizationAction[]Actions the SDK auto-signs with the agent (with typed data)
isReadybooleanWhether all authorizations are satisfied (ready to trade)

executeAuthorizations

Submit user-signed actions, then auto-sign and submit any agent authorizations.
const required = await perps.getRequiredAuthorizations({
  dex: 'hyperliquid',
  address: userAddress,
});

if (!required.isReady) {
  // Typed data already included — sign directly
  const signedActions = await Promise.all(
    required.userAuthorizations.map(async (a) => ({
      action: a.action,
      typedData: a.typedData,
      signature: await walletClient.signTypedData({ ...a.typedData }),
    }))
  );

  // Execute all authorizations (user + agent)
  const result = await perps.executeAuthorizations({
    dex: 'hyperliquid',
    address: userAddress,
    required,
    userSignedActions: signedActions,
  });

  console.log(result.userResults);   // Results from user-signed submissions
  console.log(result.agentResults);  // Results from agent-signed submissions (if any)
}
Parameters:
FieldTypeRequiredDescription
dexstringYesDEX identifier
addressstringYesUser’s wallet address
requiredRequiredAuthorizationsResultYesResult from getRequiredAuthorizations()
userSignedActionsSignedAuthorization[]YesUser-signed actions for required.userAuthorizations
Returns: ExecuteAuthorizationsResult
FieldTypeDescription
userResultsAuthorizationsResponseResults from user-signed authorization submission
agentResultsAuthorizationsResponseResults from agent-signed authorization submission (if any)
API Reference: POST /createAuthorization, POST /authorization