author_submitExtrinsic - Kusama RPC Method
Submit a signed extrinsic to Kusama for inclusion in a block. Broadcast transactions for token transfers, staking operations, governance votes, and smart contract calls.
Submits a fully signed extrinsic to Kusama for inclusion in a future block. The extrinsic enters the transaction pool and is propagated to other nodes. This is the primary method for broadcasting any on-chain operation, including balance transfers, staking, governance, and pallet interactions.
Why Kusama? Build on Polkadot's canary network for real-world testing with live economic conditions with 7-day governance cycles (vs 1 month on Polkadot), lower bonding requirements, live KSM token economy, and first-to-market feature testing.
When to Use This Method
author_submitExtrinsic is essential for experimental dApp developers, parachain teams, and early adopters validating new features:
- Token Transfers -- Send native tokens or assets between accounts on Kusama
- Staking and Governance -- Submit staking nominations, validator operations, and governance votes for parachain experimentation, early feature deployment, and production-grade testing with real value
- Smart Contract Interaction -- Call ink! or EVM smart contracts deployed on the chain
- Automated Systems -- Build bots, keepers, and automated transaction pipelines that submit extrinsics programmatically
Code Examples
Common Use Cases
1. Transfer with Fee Pre-Check
Verify fees and balance before submitting a transfer:
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';
async function safeTransfer(api, sender, recipient, amount) {
const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
// Step 1: Estimate fee
const info = await transfer.paymentInfo(sender.address);
const fee = info.partialFee.toBigInt();
console.log(`Estimated fee: ${info.partialFee.toHuman()}`);
// Step 2: Check balance
const account = await api.query.system.account(sender.address);
const free = account.data.free.toBigInt();
const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
const totalCost = BigInt(amount) + fee;
if (free - totalCost < existentialDeposit) {
throw new Error(`Insufficient balance. Need ${totalCost}, have ${free}`);
}
// Step 3: Submit
const hash = await transfer.signAndSend(sender);
console.log(`Submitted: ${hash.toHex()}`);
return hash;
}2. Batch Transaction Submission
Submit multiple operations in a single extrinsic:
async function submitBatch(api, sender, calls) {
const batch = api.tx.utility.batchAll(calls);
// Estimate total fee
const info = await batch.paymentInfo(sender.address);
console.log(`Batch fee: ${info.partialFee.toHuman()} for ${calls.length} calls`);
// Submit with event tracking
return new Promise((resolve, reject) => {
batch.signAndSend(sender, ({ status, events, dispatchError }) => {
if (dispatchError) {
if (dispatchError.isModule) {
const decoded = api.registry.findMetaError(dispatchError.asModule);
reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
} else {
reject(new Error(dispatchError.toString()));
}
}
if (status.isFinalized) {
const successEvents = events.filter(({ event }) =>
api.events.system.ExtrinsicSuccess.is(event)
);
resolve({
blockHash: status.asFinalized.toHex(),
success: successEvents.length > 0,
events: events.length
});
}
});
});
}
// Usage: batch multiple transfers
const calls = [
api.tx.balances.transferKeepAlive(recipient1, amount1),
api.tx.balances.transferKeepAlive(recipient2, amount2),
api.tx.balances.transferKeepAlive(recipient3, amount3)
];
const result = await submitBatch(api, sender, calls);3. Nonce Management for Sequential Transactions
Submit multiple transactions in rapid succession with correct nonce handling:
async function submitSequential(api, sender, extrinsics) {
// Get the starting nonce
let nonce = await api.rpc.system.accountNextIndex(sender.address);
const hashes = [];
for (const ext of extrinsics) {
const hash = await ext.signAndSend(sender, { nonce });
hashes.push(hash.toHex());
console.log(`Submitted with nonce ${nonce}: ${hash.toHex()}`);
nonce = nonce.addn(1);
}
return hashes;
}Error Handling
Common errors and solutions:
| Error Code | Description | Solution |
|---|---|---|
| 1010 | Invalid Transaction | Check signature, nonce, era, and that the extrinsic is properly SCALE-encoded |
| 1010 (bad signature) | Signature verification failed | Ensure the signing key matches the sender account and the correct genesis hash was used |
| 1010 (outdated) | Nonce too low | The nonce has already been used -- query the current nonce and rebuild the extrinsic |
| 1010 (stale) | Mortality period expired | The extrinsic era has passed -- rebuild with a current era or use an immortal era |
| 1012 | Transaction pool full | The node's pool is at capacity -- retry later or increase tip |
| -32603 | Internal error | Node may be syncing or experiencing issues -- check node health |
Related Methods
author_pendingExtrinsics-- Check the transaction pool for pending extrinsicspayment_queryInfo-- Estimate fees before submittingsystem_accountNextIndex-- Get the next valid nonce for an accountstate_call-- Call runtime APIs (e.g., for nonce viaAccountNonceApi)chain_getBlock-- Verify extrinsic inclusion in a block
system_properties
Get chain properties on Kusama including token symbol, decimals, and the address-format prefix when the chain exposes one. Essential for token formatting, wallet configuration, and address handling.
author_pendingExtrinsics
Get pending extrinsics in the transaction pool on Kusama. Monitor mempool activity, verify transaction submission, and analyze network congestion.