Summary
Acala liquid staking lets you deposit DOT into Acala's staking module and receive LDOT, a transferable derivative token that keeps earning staking rewards while you use it elsewhere in DeFi. This article explains how the flow works, what LDOT represents, and the RPC and API surface you need to read staking state or build a liquid staking integration. It also covers the operational questions developers hit first: which endpoints to use, how to query exchange rates and unbonding state, and when a shared public endpoint is enough versus when a dedicated node fits better.
Acala liquid staking is the part of the Acala network that lets you stake DOT without locking your capital into a single illiquid position. You deposit DOT, the protocol stakes it through its staking module, and you receive LDOT — a derivative token that represents your staked position and stays transferable. LDOT can be held, moved, or used in other Acala DeFi primitives while the underlying DOT keeps accruing staking rewards.
If you are a developer or infrastructure buyer, the interesting part is not the token mechanics alone. It is the data surface: how you read staking state, how you track the LDOT exchange rate, how you detect unbonding windows, and which RPC endpoint you point your backend at. This article walks through both sides — the staking model and the API calls you will actually write.
Quick recommendation: shared endpoint or dedicated node?
Before you write any integration code, decide what kind of workload you are running. Acala liquid staking touches the chain in two very different ways, and they have different infrastructure needs.
| Workload | Typical call pattern | What to start with |
|---|---|---|
| Wallet or dashboard reading balances and exchange rate | Low-volume, periodic reads | A shared RPC endpoint from a managed provider |
| Staking bot that watches unbonding queues and reward accrual | Frequent polling, sometimes subscriptions | A dedicated node or a provider plan with predictable throughput |
| Indexer that backfills historical staking events | Large block ranges, archive-style reads | Archive-capable infrastructure, not a public endpoint |
| dApp that submits staking extrinsics on behalf of users | Write path plus confirmation tracking | A provider you can fail over between, with monitoring |
A shared endpoint is usually fine for read-only dashboards and prototypes. The moment you poll staking state on a tight interval, backfill history, or submit extrinsics that users depend on, the shared path becomes the bottleneck. That is the point where a dedicated node or a managed RPC plan with clear throughput expectations is the safer choice. OnFinality offers both RPC API access and dedicated node infrastructure for Acala, so you can start shared and move to dedicated without changing your application code.
How the Acala liquid staking flow actually works
The flow has four moving parts you should understand before you touch the API.
- Deposit. You send DOT to the staking module. The protocol bonds that DOT to its validator set.
- Mint. In return you receive LDOT at the current exchange rate. LDOT is not a fixed 1:1 claim — it represents a share of the pooled staked position.
- Accrue. As the underlying DOT earns staking rewards, the value backing each LDOT grows. The exchange rate between LDOT and DOT drifts upward over time.
- Redeem. When you want DOT back, you redeem LDOT. Depending on the module's state, redemption may be immediate or may enter an unbonding queue that settles over an unbonding period.
The key implication for developers: LDOT is not a static balance. If your UI shows "1 LDOT = 1 DOT" you are showing the wrong number. You need to read the current exchange rate from chain state and recompute the user's underlying DOT value every time you display it.
Reading staking state over RPC
Acala is a Substrate-based chain, so you interact with it through the Polkadot.js-style JSON-RPC interface and the runtime's storage and extrinsic metadata. The exact storage keys and extrinsic names come from the runtime, so always resolve them from the chain's metadata rather than hardcoding them from a blog post — including this one. The pattern below shows the shape of the calls.
First, confirm you are talking to the right chain and that your endpoint responds:
curl -s https://acala.api.onfinality.io/public \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "system_chain",
"params": []
}'
A healthy response returns the chain name. If you get a timeout or an empty body, that is your first debugging signal — check the endpoint, not your staking logic.
Next, read the runtime version and metadata so your client knows which storage items and extrinsics exist:
curl -s https://acala.api.onfinality.io/public \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "state_getRuntimeVersion",
"params": []
}'
From there, a Polkadot.js client can query the staking module's storage directly. In JavaScript, the pattern looks like this:
import { ApiPromise, WsProvider } from '@polkadot/api';
const provider = new WsProvider('wss://acala.api.onfinality.io/public-ws');
const api = await ApiPromise.create({ provider });
// Resolve the staking module and its storage from live metadata
const staking = api.query.homa || api.query.homaLite;
// Example: read total staked and the current exchange rate
const totalStaked = await staking.totalStakingBonded();
const exchangeRate = await staking.liquidTokenExchangeRate();
console.log('Total staked:', totalStaked.toString());
console.log('LDOT exchange rate:', exchangeRate.toString());
The module name and storage item names depend on the runtime version. If api.query.homa is undefined, inspect api.query keys at runtime and confirm the module that is actually deployed on the chain you are connected to. This is the single most common source of "my staking call returns nothing" bugs.
Querying the LDOT exchange rate and unbonding state
Two numbers matter most for any liquid staking integration: the exchange rate and the unbonding state.
Exchange rate. This is what converts an LDOT balance into its underlying DOT value. Read it from chain state, cache it briefly, and refresh it on a schedule that matches how often it actually changes. Polling it every block is usually wasteful; polling it once per epoch is usually too slow for a live dashboard. Pick a cadence and document it.
Unbonding state. When a user redeems, the DOT may not be available instantly. Your UI should distinguish between "redeemable now" and "in unbonding queue, settles in N eras." That means reading the queue length and the unbonding period from the runtime, not guessing.
| What you want to show | Where it comes from | Refresh cadence |
|---|---|---|
| User's LDOT balance | Account storage for the liquid token | On demand / per block |
| Underlying DOT value | LDOT balance × current exchange rate | Every few minutes |
| Pending redemption | Unbonding queue storage | Every few minutes |
| Time until settlement | Unbonding period from runtime constants | On runtime upgrade |
| Reward accrual trend | Exchange rate history | Hourly or daily |
If you need historical exchange rates for charts, you are now in archive territory. A public endpoint is not the right tool for backfilling months of state; plan for archive-capable infrastructure.
Acala chain settings at a glance
Keep these settings consistent across your wallet config, your backend, and your monitoring.
| Setting | Value |
|---|---|
| Network | Acala (Polkadot parachain) |
| Token | ACA (native), DOT (staking asset), LDOT (liquid derivative) |
| Interface | Substrate JSON-RPC and WebSocket |
| Public HTTP endpoint | https://acala.api.onfinality.io/public |
| Public WebSocket endpoint | wss://acala.api.onfinality.io/public-ws |
| Transport support | HTTP and WebSocket |
You can find the current endpoint list and network details on the Acala network page. If you are integrating through an EVM-compatible path, note that Acala's EVM layer is a separate concern from the Substrate staking module — liquid staking state lives on the Substrate side, so do not expect an ERC-20-style call to return it.
Common failure modes and how to debug them
Liquid staking integrations fail in a small number of predictable ways. Here is the short diagnostic list.
- Storage item returns null. Your client is on a runtime where the module or item name changed, or you are connected to a different chain. Check
system_chainandstate_getRuntimeVersionfirst. - Exchange rate looks frozen. You are reading a cached value, or your polling interval is longer than the rate's update cadence. Log the raw value and the block number together.
- Extrinsic submits but never finalizes. You are tracking inclusion, not finalization. Subscribe to finalized heads, not just new heads, before you mark a staking action complete.
- WebSocket disconnects under load. Long-lived subscriptions need reconnect logic with backoff. Treat a dropped socket as normal, not exceptional.
- Reads get slow at peak. Shared endpoints absorb traffic from many callers. If your latency is unpredictable, that is a signal to move to a dedicated node rather than to retry harder.
A minimal monitoring probe keeps you ahead of most of these:
#!/usr/bin/env bash
# Probe Acala RPC health and record the block height
RESP=$(curl -s --max-time 5 https://acala.api.onfinality.io/public \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"chain_getHeader","params":[]}')
echo "$(date -u +%FT%TZ) $RESP" >> acala-rpc-health.log
Run this on a schedule, alert when the height stops advancing, and you will catch endpoint problems before your users do.
Build versus buy for a staking backend
If you are running a liquid staking dashboard, a rewards tracker, or a bot that acts on unbonding windows, you have a build-versus-buy decision on infrastructure.
Running your own Acala node gives you full control and no shared throughput ceiling, but it means provisioning, syncing, upgrading on runtime changes, and monitoring the node yourself. For a team whose product is the staking logic, that is often a distraction.
A managed RPC provider removes the node operations but introduces a dependency: you need to know the provider's throughput expectations, whether they support WebSocket subscriptions, whether archive reads are available, and how you fail over. When you evaluate options, put the questions in this order:
- Does it support both HTTP and WebSocket for Acala?
- Can it handle your polling cadence without unpredictable latency?
- Is there an archive path if you need historical state?
- What happens when the endpoint degrades — can you switch without redeploying?
- Is pricing predictable at your request volume?
OnFinality provides Acala RPC through its API service and offers dedicated nodes when you need isolated throughput. You can review RPC pricing and the full list of supported RPC networks to see how Acala fits alongside the other chains you run. For a broader framework, the RPC provider selection guide covers the evaluation criteria in more depth.
Key Takeaways
- Acala liquid staking turns staked DOT into LDOT, a transferable derivative that keeps earning rewards.
- LDOT is not 1:1 with DOT — always read the live exchange rate from chain state before displaying value.
- Acala is Substrate-based, so you query staking state through the Polkadot.js-style JSON-RPC and runtime metadata, not an ERC-20 call.
- Resolve module and storage names from live metadata; hardcoded names break on runtime upgrades.
- Track finalized heads, not just new heads, before marking a staking extrinsic complete.
- Shared endpoints suit dashboards and prototypes; dedicated nodes suit bots, indexers, and write-heavy paths.
- Monitor block height on a schedule so endpoint degradation surfaces before users notice.
Frequently Asked Questions
Is LDOT always worth the same as DOT? No. LDOT represents a share of a pooled staked position, so its DOT value changes as rewards accrue. Read the exchange rate from chain state.
Can I read liquid staking state through an EVM call? The staking module lives on the Substrate side of Acala. Use the Substrate JSON-RPC interface rather than expecting an ERC-20-style contract call to return staking state.
Why does my staking storage query return null?
Usually because the module or storage item name changed in a runtime upgrade, or because you are connected to a different chain. Verify with system_chain and state_getRuntimeVersion.
Do I need a dedicated node for a staking dashboard? Not necessarily. A shared endpoint is fine for low-volume reads. Move to a dedicated node when your polling cadence, subscription count, or write path makes shared throughput unpredictable.
How often should I refresh the exchange rate? Match your cadence to how often the value actually changes. Polling every block is usually wasteful; polling once per epoch is usually too slow for a live UI.
Can I backfill historical staking data on a public endpoint? Public endpoints are not intended for large historical reads. Plan for archive-capable infrastructure if you need to backfill state or events.