JSON-RPC Guide Shardeum is a fully EVM-compatible Layer 1 blockchain. Developers can interact with the network using standard Ethereum JSON-RPC APIs and familiar tooling such as ethers.js, web3.js, viem, MetaMask, and Hardhat — without requiring protocol-specific changes.
If you have built on Ethereum before, you can build on Shardeum using the same workflows, libraries, and mental models.
This page is intended for application developers building EVM-compatible dApps on Shardeum. Validator, staking, governance, and infrastructure APIs are documented separately.
You can find the Shardeum mainnet and testnet endpoints/explorer details here .
You can add the Shardeum network to MetaMask and other EVM-compatible wallets using the official network configuration.
For the latest chain configuration details and one-click wallet setup, refer to:
https://docs.shardeum.org/docs/overview/endpoints
Always ensure you are using network details from official Shardeum documentation before adding a custom network to your wallet.
Shardeum supports the complete Ethereum JSON-RPC specification. The sections below highlight the most commonly used methods across typical application workflows. For the full specification, refer to the Ethereum JSON-RPC documentation.
Returns the list of addresses available to the connected client (if any). Note: Public RPC endpoints typically do not manage unlocked accounts, so this method may return an empty array. Wallet-managed accounts should be accessed via the wallet provider instead.
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://api.shardeum.org');
const accounts = await provider.send('eth_accounts', []);
console.log(accounts);
Returns the balance of an account at a given address.
// Using ethers.js
const balance = await provider.getBalance('0x...');
POST https://api.shardeum.org
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_getBalance",
"params": ["0x...", "latest"]
}
Returns the number of transactions sent from an address (nonce).
const nonce = await provider.getTransactionCount('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
Returns code at a given address (for smart contracts).
const code = await provider.getCode('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
Signs data with a given address. Note: many wallets discourage eth_sign; prefer personal_sign or EIP-712 typed data signing where possible.
await provider.send('eth_sign', [address, dataToSign]);
Signs a transaction without broadcasting it.
await provider.send('eth_signTransaction', [txObject]);
Returns the current block number.
const blockNumber = await provider.getBlockNumber();
Returns information about a block by block number.
// Get latest block
const block = await provider.getBlock('latest');
// Get specific block with transactions
const blockWithTxs = await provider.getBlock(12345, true);
Returns information about a block by hash.
const block = await provider.getBlock('0x1234...');
Returns the number of transactions in a block by block number.
await provider.send('eth_getBlockTransactionCountByNumber', ['latest']);
Sends a signed transaction.
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://api.shardeum.org');
const wallet = new ethers.Wallet(privateKey, provider);
const tx = {
to: recipientAddress,
value: ethers.parseEther('1.0')
};
const signedTx = await wallet.signTransaction(tx);
const txResponse = await provider.broadcastTransaction(signedTx);
await txResponse.wait();
Executes a new message call immediately without creating a transaction (read-only).
// Call a contract method
const result = await contract.balanceOf('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
// Raw call
await provider.call({
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
data: '0x...'
});
Estimates the gas needed for a transaction.
const gasEstimate = await provider.estimateGas({
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
value: ethers.parseEther('1.0')
});
Returns transaction information by hash.
const tx = await provider.getTransaction('0x1234...');
Returns transaction by block hash and transaction index.
await provider.send('eth_getTransactionByBlockHashAndIndex', [
'0x1234...',
'0x0'
]);
Returns transaction by block number and transaction index.
await provider.send('eth_getTransactionByBlockNumberAndIndex', [
'latest',
'0x0'
]);
Returns the receipt of a transaction by hash.
const receipt = await provider.getTransactionReceipt('0x1234...');
Returns the current gas price in wei.
const gasPrice = await provider.getFeeData();
console.log('Gas price:', gasPrice.gasPrice);
Returns the current max priority fee per gas.
const feeData = await provider.getFeeData();
console.log('Max priority fee:', feeData.maxPriorityFeePerGas);
Returns fee history for a range of blocks.
await provider.send('eth_feeHistory', [
'0x5', // block count
'latest', // newest block
[25, 75] // reward percentiles
]);
Creates a filter object for tracking logs/events.
const filter = {
address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
topics: [ethers.id('Transfer(address,address,uint256)')]
};
const filterId = await provider.send('eth_newFilter', [filter]);
Creates a filter to notify when a new block arrives.
const filterId = await provider.send('eth_newBlockFilter', []);
Creates a filter to notify when new pending transactions arrive.
const filterId = await provider.send('eth_newPendingTransactionFilter', []);
Returns an array of logs/events since last poll.
const changes = await provider.send('eth_getFilterChanges', [filterId]);
Returns an array of all logs matching filter.
const logs = await provider.send('eth_getFilterLogs', [filterId]);
Uninstalls a filter.
await provider.send('eth_uninstallFilter', [filterId]);
Returns logs matching given filter criteria.
const logs = await provider.getLogs({
address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
fromBlock: 0,
toBlock: 'latest',
topics: [ethers.id('Transfer(address,address,uint256)')]
});
Returns the value from a storage position at a given address.
const value = await provider.getStorage(
'0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
0 // storage position
);
Returns the chain ID of the current network.
const chainId = await provider.send('eth_chainId', []);
// Returns: "0x1fb6" (8118 in decimal)
Returns the current network ID.
const networkId = await provider.send('net_version', []);
Returns true if client is actively listening for network connections.
const isListening = await provider.send('net_listening', []);
Returns number of peers currently connected.
const peerCount = await provider.send('net_peerCount', []);
Returns the current client version.
const version = await provider.send('web3_clientVersion', []);
Returns Keccak-256 hash of given data.
const hash = await provider.send('web3_sha3', ['0x68656c6c6f20776f726c64']);
Returns false (Shardeum uses PoS, not PoW).
const isMining = await provider.send('eth_mining', []);
// Always returns: false
Returns 0 (not applicable for PoS).
const hashrate = await provider.send('eth_hashrate', []);
// Always returns: "0x0"
Returns empty array (not applicable for PoS).
const work = await provider.send('eth_getWork', []);
Subscribe to blocks and logs using a WebSocket provider. Note: WebSocket subscriptions are for real-time updates and should not be treated as the sole source of truth. Always reconcile important state using standard RPC queries.
import { ethers } from 'ethers';
const wsProvider = new ethers.WebSocketProvider('wss://api.shardeum.org');
// Subscribe to new blocks
wsProvider.on('block', (blockNumber) => {
console.log('New block:', blockNumber);
});
// Subscribe to contract logs
wsProvider.on(
{ address: contractAddress },
(log) => {
console.log('Log:', log);
}
);
import { ethers } from 'ethers';
// 1. Connect to Shardeum
const provider = new ethers.JsonRpcProvider('https://api.shardeum.org');
const wallet = new ethers.Wallet(privateKey, provider);
// 2. Get nonce
const nonce = await wallet.getNonce();
// 3. Estimate gas
const gasEstimate = await provider.estimateGas({
to: recipientAddress,
value: ethers.parseEther('1.0')
});
// 4. Get gas price
const feeData = await provider.getFeeData();
// 5. Create and send transaction
const tx = await wallet.sendTransaction({
to: recipientAddress,
value: ethers.parseEther('1.0'),
nonce: nonce,
gasLimit: gasEstimate,
gasPrice: feeData.gasPrice
});
// 6. Wait for confirmation
const receipt = await tx.wait();
console.log('Transaction confirmed:', receipt.hash);
// Create contract instance
const contract = new ethers.Contract(contractAddress, abi, provider);
// Listen for Transfer events
contract.on('Transfer', (from, to, amount, event) => {
console.log(`Transfer: ${from} -> ${to}, amount: ${amount}`);
});
// Or use filters for specific events
const filter = contract.filters.Transfer(null, myAddress);
const logs = await contract.queryFilter(filter, -1000, 'latest');
// Read multiple balances efficiently
const addresses = ['0x123...', '0x456...', '0x789...'];
const balances = await Promise.all(
addresses.map(addr => provider.getBalance(addr))
);
console.log('Balances:', balances.map(b => ethers.formatEther(b)));
Public RPC endpoints have rate limits to ensure fair usage:
Requests per second: 10
Requests per minute: 600
Concurrent connections: 5
For production applications with higher throughput requirements, consider:
Running your own RPC node
Using the partner API endpoint (or contact Shardeum team)
Implementing request batching and caching
Always implement proper error handling for RPC calls:
try {
const balance = await provider.getBalance(address);
} catch (error) {
if (error.code === 'NETWORK_ERROR') {
console.error('Network connection failed');
} else if (error.code === 'TIMEOUT') {
console.error('Request timeout - try again');
} else {
console.error('RPC error:', error.message);
}
}
For applications needing real-time data, use WebSocket instead of polling:
const ws = new ethers.WebSocketProvider('wss://api.shardeum.org');
ws.on('block', (blockNumber) => {
console.log('New block:', blockNumber);
});
Group related RPC calls to reduce network overhead:
const [balance, nonce, code] = await Promise.all([
provider.getBalance(address),
provider.getTransactionCount(address),
provider.getCode(address)
]);
Cache data that doesn't change:
const cache = new Map();
async function getBlockWithCache(blockNumber) {
if (cache.has(blockNumber)) {
return cache.get(blockNumber);
}
const block = await provider.getBlock(blockNumber);
cache.set(blockNumber, block);
return block;
}
While Shardeum is fully EVM-compatible, it is a sovereign L1 blockchain with
Proof-of-stake consensus mechanism and delegated staking
Block time that is much faster than Ethereum (~2-3 seconds)
Transactions are final once confirmed
SHM-denominated gas fees with predictable and low pricing.
"Nonce too low" error:
// Get the current nonce from network
const nonce = await provider.getTransactionCount(address, 'latest');
"Insufficient funds" error:
// Check balance before sending
const balance = await provider.getBalance(address);
if (balance < amount) {
throw new Error('Insufficient balance');
}
"Gas estimation failed":
// Manually set gas limit
const tx = await wallet.sendTransaction({
to: address,
value: amount,
gasLimit: 100000 // Set explicitly
});
"Network timeout":
// Increase timeout in provider config
const provider = new ethers.JsonRpcProvider('https://api.shardeum.org', {
timeout: 30000 // 30 seconds
});