author_submitAndWatchExtrinsic - Bittensor RPC Method
Submit an extrinsic and subscribe to lifecycle status updates on Bittensor. Track transactions from pool admission through finalization with real-time WebSocket events for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration.
Submits a signed extrinsic to Bittensor 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 Bittensor? Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.
When to Use This Method
author_submitAndWatchExtrinsic is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:
- Transaction Lifecycle Tracking -- Receive real-time status events as your extrinsic moves from the pool into a block and reaches finality on Bittensor
- 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 AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- User-Facing Feedback -- Power progress indicators and toast notifications in dApp interfaces with granular status updates
Best Practices
- Requires a WebSocket connection for real-time status updates
- Handles multiple status transitions: Ready, Broadcast, InBlock, Finalized
- Unsubscribe from the watch subscription when the extrinsic is confirmed
- Use
author_submitExtrinsicwith polling if WebSocket is unavailable
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 Bittensor. Monitor mempool activity, verify transaction submission, and analyze network congestion.
author_rotateKeys
Generate new session keys for validator operations on Bittensor. Essential for validator setup, key rotation, and security best practices on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination.