author_submitAndWatchExtrinsic - Acala RPC Method
Submit an extrinsic and subscribe to lifecycle status updates on Acala. Track transactions from pool admission through finalization with real-time WebSocket events for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX.
Submits a signed extrinsic to Acala and returns a subscription that emits status updates as the transaction progresses through the lifecycle -- from entering the transaction pool, through block inclusion, to finalization. This is a WebSocket-only subscription method.
Why Acala? Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.
When to Use This Method
author_submitAndWatchExtrinsic is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:
- Transaction Lifecycle Tracking -- Receive real-time status events as your extrinsic moves from the pool into a block and reaches finality on Acala
- Confirmation Waiting -- Block until a transaction reaches a specific finality level (e.g.,
inBlockorfinalized) before proceeding with dependent logic - Error Detection -- Detect dropped, invalid, or usurped transactions immediately instead of polling, critical for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- User-Facing Feedback -- Power progress indicators and toast notifications in dApp interfaces with granular status updates
Code Examples
Common Use Cases
1. Transaction Confirmation with Timeout
Wait for finalization with a configurable timeout to avoid hanging indefinitely:
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';
async function sendAndConfirm(api, sender, tx, timeoutMs = 120000) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error('Transaction confirmation timed out'));
}, timeoutMs);
tx.signAndSend(sender, ({ status, dispatchError, events }) => {
if (dispatchError) {
clearTimeout(timer);
if (dispatchError.isModule) {
const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
reject(new Error(`${section}.${name}: ${docs.join(' ')}`));
} else {
reject(new Error(dispatchError.toString()));
}
}
if (status.isFinalized) {
clearTimeout(timer);
resolve({
blockHash: status.asFinalized.toHex(),
events: events.map((e) => `${e.event.section}.${e.event.method}`)
});
}
}).catch((err) => {
clearTimeout(timer);
reject(err);
});
});
}2. Batch Transaction Pipeline
Submit multiple extrinsics sequentially and track each one through finalization:
async function submitBatch(api, sender, calls) {
const results = [];
let nonce = (await api.rpc.system.accountNextIndex(sender.address)).toNumber();
for (const call of calls) {
const result = await new Promise((resolve, reject) => {
call.signAndSend(sender, { nonce: nonce++ }, ({ status, dispatchError }) => {
if (dispatchError) {
const decoded = dispatchError.isModule
? api.registry.findMetaError(dispatchError.asModule)
: { name: dispatchError.toString() };
reject(new Error(`Dispatch error: ${decoded.name}`));
}
if (status.isFinalized) {
resolve({ blockHash: status.asFinalized.toHex(), nonce: nonce - 1 });
}
});
});
results.push(result);
console.log(`Tx nonce=${result.nonce} finalized in ${result.blockHash}`);
}
return results;
}3. Reorg-Aware Event Handling
Handle block retractions gracefully, re-evaluating transaction inclusion after reorganizations:
async function sendWithReorgHandling(api, sender, tx) {
let includedBlock = null;
return new Promise((resolve, reject) => {
tx.signAndSend(sender, ({ status, events }) => {
if (status.isReady) {
console.log('Transaction in ready queue');
}
if (status.isInBlock) {
includedBlock = status.asInBlock.toHex();
console.log(`Included in block: ${includedBlock}`);
}
if (status.isRetracted) {
console.warn(`Block retracted: ${status.asRetracted.toHex()} -- waiting for re-inclusion`);
includedBlock = null;
}
if (status.isFinalized) {
console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
resolve({ finalized: status.asFinalized.toHex(), events });
}
if (status.isDropped || status.isInvalid) {
reject(new Error(`Transaction ${status.type}`));
}
if (status.isUsurped) {
reject(new Error(`Transaction usurped by ${status.asUsurped.toHex()}`));
}
});
});
}Status Flow
┌─────────────────────────────────────┐
│ future (nonce gap) │
└──────────────┬──────────────────────┘
│ nonce becomes current
▼
submit ──► ready ──► broadcast ──► inBlock ──► finalized ✓
│ │
├──► dropped ✗ ├──► retracted (reorg) ──► inBlock (re-included)
├──► invalid ✗ └──► finalityTimeout ✗
└──► usurped ✗Error Handling
Common errors and solutions:
| Error Code | Description | Solution |
|---|---|---|
| 1002 | Verification error | The extrinsic has an invalid signature, wrong format, or failed validation -- re-sign and resubmit |
| 1010 | Invalid transaction | Bad nonce, insufficient balance, or the call is not permitted -- check account state with system_accountNextIndex |
| 1014 | Priority too low | Transaction pool rejected the extrinsic because a higher-priority transaction exists for the same nonce -- increase tip or wait |
| -32603 | Internal error | Node encountered an unexpected error -- retry after delay |
| Connection closed | WebSocket dropped | The subscription is lost -- reconnect and check author_pendingExtrinsics or re-query chain state to determine transaction outcome |
Related Methods
author_submitExtrinsic-- Submit an extrinsic without subscribing to status updates (fire-and-forget)system_accountNextIndex-- Get the next valid nonce for an account, including pending pool transactionsauthor_pendingExtrinsics-- List all extrinsics currently in the transaction poolpayment_queryInfo-- Estimate the fee for an extrinsic before submissionchain_getFinalizedHead-- Get the hash of the latest finalized block
author_pendingExtrinsics
Get pending extrinsics in the transaction pool on Acala. Monitor mempool activity, verify transaction submission, and analyze network congestion.
author_rotateKeys
Generate new session keys for validator operations on Acala. Essential for validator setup, key rotation, and security best practices on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT).