Orderly Network: SDK Trading Workflows
End-to-end trading workflows: connect → deposit → trade → monitor → close → withdraw.
When to Use
- Building a complete trading interface
- Implementing the deposit → trade → withdraw flow
- Building custom order/position UI on top of SDK hooks
Prerequisites
- Orderly SDK packages installed (see
orderly-sdk-install-dependency) - Wallet connection configured (see
orderly-sdk-wallet-connection) OrderlyAppProviderwrapping the app
Overview
Connect → Deposit → Trade → Monitor → Close → Withdraw
Recommended path: the reference DEX does not hand-roll the trading flow. It renders
<TradingPage>(from@orderly.network/trading), which already contains the chart, orderbook, order entry, positions, and order management. The hook-based code below is for custom trading UIs where you replace parts ofTradingPage.
1. Connect & Authenticate
AccountStatusEnum is exported from @orderly.network/types (not hooks). The full state machine is documented in orderly-sdk-wallet-connection.
import { useAccount } from '@orderly.network/hooks';
import { AccountStatusEnum } from '@orderly.network/types';
function TradingGuard({ children }: { children: React.ReactNode }) {
const { state, createAccount, createOrderlyKey } = useAccount();
switch (state.status) {
case AccountStatusEnum.NotConnected:
return <ConnectWalletPrompt />;
case AccountStatusEnum.Connected:
return <button => createAccount()}>Create Orderly Account</button>;
case AccountStatusEnum.NotSignedIn:
return <button => createOrderlyKey()}>Enable Trading</button>;
default:
return <>{children}</>;
}
}
AuthGuard
For custom UI, gate content behind AuthGuard (from @orderly.network/ui-connector). Note: TradingPage manages its own auth prompts, so you do not wrap it in AuthGuard.
import { AuthGuard } from '@orderly.network/ui-connector';
<AuthGuard>{/* protected custom UI */}</AuthGuard>;
2. Deposit
useDeposit
deposit() takes no argument — it submits the amount set via setQuantity. Approve ERC-20 allowance first when the token is not native.
import { useDeposit } from '@orderly.network/hooks';
import { toast } from '@orderly.network/ui';
function DepositForm() {
const { balance, allowance, approve, deposit, quantity, setQuantity, depositFee, isNativeToken } =
useDeposit({
srcChainId: 42161,
srcToken: 'USDC',
address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
decimals: 6,
});
const handleDeposit = async () => {
setQuantity('100');
try {
// Approve if allowance is insufficient (allowance is a wei string)
if (!isNativeToken) await approve();
await deposit();
toast.success('Deposit submitted');
} catch (e) {
toast.error((e as Error).message);
}
};
return (
<div>
<input value={quantity} => setQuantity(e.target.value)} placeholder="Amount" />
<p>Wallet balance: {balance}</p>
<p>Deposit fee (wei): {depositFee?.toString()}</p>
<button
</div>
);
}
depositFeeis abigint(in wei). Use a pre-built form (DepositForm/DepositFormWidgetfrom@orderly.network/ui-transfer) for a fully styled experience.
3. Check Account Balance
import { useCollateral } from '@orderly.network/hooks';
function AccountSummary() {
const { totalValue, freeCollateral, availableBalance, unsettledPnL } = useCollateral({ dp: 2 });
// totalValue: number | null — total portfolio value
// freeCollateral: available for new positions
// availableBalance: withdrawable
// unsettledPnL: unrealized PnL across positions
}
4. Place Orders
Recommended: TradingPage
import { TradingPage } from '@orderly.network/trading';
<TradingPage symbol="PERP_BTC_USDC" tradingViewConfig={config.tradingPage.tradingViewConfig} />;
Custom UI: useOrderEntry
Set order fields with setValue/setValues (keys: side, order_type, order_price, order_quantity, reduce_only, tp_trigger_price, sl_price), validate, then submit(). Validation state lives in metaState (not top-level errors).
import { useOrderEntry } from '@orderly.network/hooks';
import { OrderSide, OrderType } from '@orderly.network/types';
function OrderForm({ symbol }: { symbol: string }) {
const { submit, setValues, setValue, helper, isMutating, metaState, formattedOrder } =
useOrderEntry(symbol);
useEffect(() => {
setValues({ side: OrderSide.BUY, order_type: OrderType.LIMIT });
}, [symbol]);
const handleSubmit = async () => {
const result = await helper.validate();
if (result) return; // validation issues are in metaState.errors
try {
await submit({ resetOnSuccess: true });
toast.success('Order submitted');
} catch (e) {
toast.error((e as Error).message);
}
};
return (
<form
=> {
e.preventDefault();
handleSubmit();
}}
>
<input
placeholder="Price"
value={formattedOrder.order_price ?? ''}
=> setValue('order_price', e.target.value)}
/>
<input
placeholder="Quantity"
value={formattedOrder.order_quantity ?? ''}
=> setValue('order_quantity', e.target.value)}
/>
<button type="submit" disabled={isMutating}>
{isMutating ? 'Placing…' : 'Buy'}
</button>
</form>
);
}
Low-level: useMutation
For non-orderly-entry flows (e.g. a bot), post directly. This is not what the DEX template uses.
import { useMutation } from '@orderly.network/hooks';
import { OrderType, OrderSide } from '@orderly.network/types';
const [submitOrder] = useMutation('/v1/order');
await submitOrder({
symbol,
side,
order_type: OrderType.MARKET,
order_quantity: quantity,
});
5. Monitor & Cancel Orders
useOrderStream returns [orders, actions]. Use the OrderStatus enum — not the string 'OPEN' (open orders are OrderStatus.INCOMPLETE).
import { useOrderStream } from '@orderly.network/hooks';
import { OrderStatus } from '@orderly.network/types';
function OpenOrders({ symbol }: { symbol?: string }) {
const [orders, { cancelOrder, cancelAllPendingOrders }] = useOrderStream({
status: OrderStatus.INCOMPLETE,
symbol,
});
return (
<div>
<button => cancelAllPendingOrders(symbol)}>Cancel All</button>
{(orders ?? []).map((o) => (
<div key={o.order_id}>
{o.symbol} {o.side} {o.order_quantity} @ {o.order_price}
<button => cancelOrder(o.order_id, o.symbol)}>✕</button>
</div>
))}
</div>
);
}
6. Monitor Positions
usePositionStream returns a 3-tuple [positions, calc, status]. The third element exposes isLoading (not loading).
import { usePositionStream } from '@orderly.network/hooks';
function PositionsTable() {
const [positions, , { isLoading }] = usePositionStream();
return (
<table>
{(positions?.rows ?? []).map((p) => (
<tr key={p.symbol}>
<td>{p.symbol}</td>
<td>{p.position_qty}</td>
<td>{p.average_open_price}</td>
<td>{p.mark_price}</td>
<td className={p.unrealized_pnl >= 0 ? 'profit' : 'loss'}>
{p.unrealized_pnl?.toFixed(2)}
</td>
</tr>
))}
</table>
);
}
7. Close a Position
Submit a reducing market order via useOrderEntry (set reduce_only: true and the opposite side):
const { setValues, submit } = useOrderEntry(position.symbol);
await setValues({
side: position.position_qty > 0 ? OrderSide.SELL : OrderSide.BUY,
order_type: OrderType.MARKET,
order_quantity: Math.abs(position.position_qty),
reduce_only: true,
});
await submit();
Or use usePositionClose / the positions widget's close action for a one-call close.
8. Withdraw
useWithdraw returns withdraw(inputs) — pass the full inputs object (not a bare amount). Use maxAmount (not the deprecated availableWithdraw).
import { useWithdraw } from '@orderly.network/hooks';
const { withdraw, maxAmount, unsettledPnL, availableBalance, dst } = useWithdraw({
srcChainId: 42161,
token: 'USDC',
decimals: 6,
});
await withdraw({
chainId: 42161,
token: 'USDC',
amount: '100',
allowCrossChainWithdraw: false,
receiver: '0x…', // optional
});
For a styled UI, use WithdrawForm / WithdrawWidget from @orderly.network/ui-transfer.
9. Leverage
Account-level leverage (useLeverage) is not symbol-scoped. For the active symbol use useSymbolLeverage.
import { useLeverage, useSymbolLeverage } from '@orderly.network/hooks';
// Account-level
const { curLeverage, maxLeverage, update, isLoading } = useLeverage();
await update({ leverage: 5 });
// Symbol-level (preferred on the trading page)
const { maxLeverage, update: setSymbolLeverage } = useSymbolLeverage(symbol);
await setSymbolLeverage({ leverage: 5, symbol });
10. Risk Monitoring
import { useMarginRatio } from '@orderly.network/hooks';
const { marginRatio, mmr, currentLeverage, maintenanceMargin } = useMarginRatio();
Complete Trading Page (recommended)
The template's trading page is just TradingPage with config from useOrderlyConfig() — no manual auth guard, no manual order wiring:
// app/pages/perp/Symbol.tsx
import { TradingPage } from '@orderly.network/trading';
import { useOrderlyConfig } from '@/utils/config';
export default function PerpSymbol() {
const params = useParams();
const [symbol, setSymbol] = useState(params.symbol!);
const config = useOrderlyConfig();
return (
<TradingPage
symbol={symbol}
=> {
setSymbol(data.symbol);
navigate(`/perp/${data.symbol}`);
}}
tradingViewConfig={config.tradingPage.tradingViewConfig}
sharePnLConfig={config.tradingPage.sharePnLConfig}
/>
);
}
Route is /perp/:symbol (not /trade).
Best Practices
- Prefer
TradingPageover hand-rolled order/position UI unless you need deep customization. - Use the
OrderStatusenum (INCOMPLETE) for open-order streams — never the string'OPEN'. - Read validation from
metaState(metaState.errors,metaState.validated) when usinguseOrderEntry. - Approve before depositing non-native ERC-20 tokens; check
isNativeToken. - Pass the full inputs object to
withdraw(), and usemaxAmountfor the withdrawable ceiling.
Related Skills
- orderly-sdk-wallet-connection - Wallet + account state machine
- orderly-sdk-react-hooks - Full hook reference
- orderly-sdk-page-components -
TradingPageand portfolio pages - orderly-trading-orders - Order management (REST API)
- orderly-positions-tpsl - Position management (REST API)
- orderly-deposit-withdraw - Fund management (REST API)