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:
- Discover —
getDexes() returns available authorizations per DEX
- Create —
createAuthorization() builds the payloads to sign
- Sign — User signs each
typedData with their wallet
- Submit —
submitAuthorization() sends the signed payloads
If using PerpsClient, the buildAuthorization() method handles step 2 and automatically injects the agent address for USER_AGENT mode.
Using PerpsClient (recommended)
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:
| Field | Type | Required | Description |
|---|
dex | string | Yes | DEX identifier |
address | string | Yes | User’s wallet address (account owner) |
signerAddress | string | No | Address that will sign the payloads. Required for USER_AGENT mode. Defaults to address. |
authorizations | AuthorizationInput[] | Yes | Authorizations 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:
| Field | Type | Required | Description |
|---|
key | string | Yes | Authorization key from dex.authorizations[].key |
params | Record<string, unknown> | No | Authorization-specific parameters |
Returns: { actions: AuthorizationAction[] }
Each AuthorizationAction:
| Field | Type | Description |
|---|
action | string | Authorization key |
description | string? | Human-readable description |
typedData | object | EIP-712 typed data to sign |
submitAuthorizations
Submit signed authorizations.
await perps.submitAuthorizations(params);
Parameters:
| Field | Type | Required | Description |
|---|
dex | string | Yes | DEX identifier |
address | string | Yes | User’s wallet address (account owner) |
signerAddress | string | No | Address that signed the payloads. Required for USER_AGENT mode. Defaults to address. |
actions | SignedAuthorization[] | Yes | Signed authorization payloads |
Each SignedAuthorization:
| Field | Type | Required | Description |
|---|
action | string | Yes | Authorization action type |
typedData | object | Yes | Original typedData from buildAuthorization |
signature | string | Yes | User’s signature of the typedData |
Returns: AuthorizationsResponse
| Field | Type | Description |
|---|
results | AuthorizationResult[] | Result per submitted action |
Each AuthorizationResult:
| Field | Type | Description |
|---|
action | string | Authorization action key |
success | boolean | Whether the authorization succeeded |
error | string? | 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:
| Field | Type | Required | Description |
|---|
dex | string | Yes | DEX identifier |
address | string | Yes | User’s wallet address |
Returns: RequiredAuthorizationsResult
| Field | Type | Description |
|---|
userAuthorizations | AuthorizationAction[] | Actions requiring user wallet signature (with typed data) |
agentAuthorizations | AuthorizationAction[] | Actions the SDK auto-signs with the agent (with typed data) |
isReady | boolean | Whether 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:
| Field | Type | Required | Description |
|---|
dex | string | Yes | DEX identifier |
address | string | Yes | User’s wallet address |
required | RequiredAuthorizationsResult | Yes | Result from getRequiredAuthorizations() |
userSignedActions | SignedAuthorization[] | Yes | User-signed actions for required.userAuthorizations |
Returns: ExecuteAuthorizationsResult
| Field | Type | Description |
|---|
userResults | AuthorizationsResponse | Results from user-signed authorization submission |
agentResults | AuthorizationsResponse | Results from agent-signed authorization submission (if any) |
API Reference: POST /createAuthorization, POST /authorization