This tutorial teaches you how to query Monad blockchain data using JavaScript and ethers.js. All examples are read-only operations that don’t require any MON tokens, making this perfect for getting started.
Get your own node endpoint todayStart for free and get your app to production levels immediately. No credit card required.You can sign up with your GitHub, X, Google, or Microsoft account.
Prerequisites
- Node.js installed (v16 or higher)
- Basic JavaScript knowledge
Install ethers.js:
Connect to Monad testnet
Create a provider to connect to Monad:
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
Monad testnet details:
- Chain ID: 10143
- Block time: ~1 second
- 100% EVM compatible
Get latest block number
The simplest query - get the current block height:
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
async function getLatestBlock() {
const blockNumber = await provider.getBlockNumber();
console.log(`Latest block: ${blockNumber}`);
}
getLatestBlock();
Check account balance
Query the MON balance of any address:
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
async function getBalance() {
const address = "0x0000000000000000000000000000000000001000";
const balance = await provider.getBalance(address);
console.log(`Balance: ${ethers.formatEther(balance)} MON`);
}
getBalance();
Get transaction count (nonce)
Check how many transactions an address has sent:
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
async function getTransactionCount() {
const address = "0xa54F56e8Cfff25b17105d6073aB0f0E7DA087225";
const nonce = await provider.getTransactionCount(address);
console.log(`Transaction count: ${nonce}`);
}
getTransactionCount();
Fetch block by hash
Retrieve detailed block information:
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
async function getBlock() {
const blockHash = "0xf3cf930f1b4d9637134d09f126c57c30c3f4f40edf10ba502486b26d14b4f944";
const block = await provider.getBlock(blockHash);
console.log(`Block number: ${block.number}`);
console.log(`Timestamp: ${new Date(block.timestamp * 1000).toISOString()}`);
console.log(`Transactions: ${block.transactions.length}`);
console.log(`Gas used: ${block.gasUsed.toString()}`);
}
getBlock();
Get transaction details
Inspect a specific transaction:
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
async function getTransaction() {
const txHash = "0xffc7cdc354942338ee028ab1c54ef395b908d6e062ef57821e2869ef37945221";
const tx = await provider.getTransaction(txHash);
console.log(`From: ${tx.from}`);
console.log(`To: ${tx.to}`);
console.log(`Value: ${ethers.formatEther(tx.value)} MON`);
console.log(`Gas price: ${ethers.formatUnits(tx.gasPrice, "gwei")} gwei`);
}
getTransaction();
Get transaction receipt
Check execution results and logs:
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
async function getReceipt() {
const txHash = "0xffc7cdc354942338ee028ab1c54ef395b908d6e062ef57821e2869ef37945221";
const receipt = await provider.getTransactionReceipt(txHash);
console.log(`Status: ${receipt.status === 1 ? "Success" : "Failed"}`);
console.log(`Gas used: ${receipt.gasUsed.toString()}`);
console.log(`Block: ${receipt.blockNumber}`);
console.log(`Logs: ${receipt.logs.length}`);
}
getReceipt();
Call a smart contract
Read data from a contract without sending a transaction. This example calls the name() function on the Wrapped Monad (WMON) contract:
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
async function callContract() {
// WMON contract address
const contractAddress = "0x760AfE86e5de5fa0Ee542fc7B7B713e1c5425701";
// name() function selector
const data = "0x06fdde03";
const result = await provider.call({
to: contractAddress,
data: data
});
// Decode the string result
const name = ethers.AbiCoder.defaultAbiCoder().decode(["string"], result)[0];
console.log(`Token name: ${name}`);
}
callContract();
Check if address is a contract
Determine whether an address is a smart contract or an externally owned account (EOA):
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
async function isContract() {
const address = "0xAc586b65F3cd0627D2D05AdB8EF551C9d2D76E12";
const code = await provider.getCode(address);
if (code === "0x") {
console.log("Address is an EOA (not a contract)");
} else {
console.log(`Contract found, bytecode length: ${(code.length - 2) / 2} bytes`);
}
}
isContract();
Read contract storage
Access raw storage slots of a contract:
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
async function getStorage() {
const contractAddress = "0xAc586b65F3cd0627D2D05AdB8EF551C9d2D76E12";
const slot = 0; // Storage slot 0
const value = await provider.getStorage(contractAddress, slot);
console.log(`Storage slot 0: ${value}`);
}
getStorage();
Build a simple block monitor
Since Monad doesn’t currently support WebSocket subscriptions, use polling to monitor new blocks:
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
async function monitorBlocks() {
let lastBlock = await provider.getBlockNumber();
console.log(`Starting monitor at block ${lastBlock}`);
// Poll every second (Monad has ~1 second blocks)
setInterval(async () => {
try {
const currentBlock = await provider.getBlockNumber();
if (currentBlock > lastBlock) {
// New block(s) detected
for (let i = lastBlock + 1; i <= currentBlock; i++) {
const block = await provider.getBlock(i);
console.log(`Block ${i}: ${block.transactions.length} txs, gas used: ${block.gasUsed.toString()}`);
}
lastBlock = currentBlock;
}
} catch (error) {
console.error("Error fetching block:", error.message);
}
}, 1000);
}
monitorBlocks();
Complete example
Here’s a script that demonstrates multiple queries:
const { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL");
async function main() {
console.log("=== Monad Blockchain Query Demo ===\n");
// Network info
const network = await provider.getNetwork();
console.log(`Chain ID: ${network.chainId}`);
// Latest block
const blockNumber = await provider.getBlockNumber();
console.log(`Latest block: ${blockNumber}`);
// Get block details
const block = await provider.getBlock(blockNumber);
console.log(`Block timestamp: ${new Date(block.timestamp * 1000).toISOString()}`);
console.log(`Transactions in block: ${block.transactions.length}`);
// Check a balance
const address = "0x0000000000000000000000000000000000001000";
const balance = await provider.getBalance(address);
console.log(`\nBalance of ${address}:`);
console.log(`${ethers.formatEther(balance)} MON`);
// Call WMON contract
const wmonAddress = "0x760AfE86e5de5fa0Ee542fc7B7B713e1c5425701";
const nameData = "0x06fdde03";
const result = await provider.call({ to: wmonAddress, data: nameData });
const tokenName = ethers.AbiCoder.defaultAbiCoder().decode(["string"], result)[0];
console.log(`\nWMON token name: ${tokenName}`);
// Gas price
const feeData = await provider.getFeeData();
console.log(`\nCurrent gas price: ${ethers.formatUnits(feeData.gasPrice, "gwei")} gwei`);
}
main().catch(console.error);
Monad-specific notes
Key differences from other EVM chains:
- 1-second finality: Blocks are finalized immediately, no reorganizations
- No pending transactions:
eth_getTransactionByHash only returns confirmed transactions
- No WebSocket subscriptions yet: Use polling for real-time data
- Block gas limit: 300M gas per block
Next steps
Now that you can query blockchain data, you can:
- Build dashboards to visualize network activity
- Monitor specific addresses or contracts
- Create alerts for on-chain events
- Develop analytics tools
For sending transactions, you’ll need testnet MON tokens from a faucet. Last modified on December 8, 2025