Events & Logs
Monolythium emits events at two layers: Cosmos ABCI events (from SDK modules) and EVM logs (from smart contracts). Both are queryable and subscribable in real time.
Cosmos ABCI Events
Every Cosmos SDK transaction emits structured events as key-value attribute pairs. Modules emit events for state changes like transfers, delegations, and governance actions.
Event Structure
Event {
type: "transfer"
attributes: [
{ key: "sender", value: "mono1abc..." },
{ key: "recipient", value: "mono1def..." },
{ key: "amount", value: "1000000000000000000alyth" }
]
}
Query Events via CLI
# Find all transfer events sent by a specific address
monod query txs --events 'transfer.sender=mono1abc...' --chain-id mono_6940-1
# Find delegation events
monod query txs --events 'delegate.validator=monovaloper1xyz...'
# Combine multiple event filters (AND logic)
monod query txs \
--events 'message.module=bank&transfer.recipient=mono1def...'
# Query a specific transaction
monod query tx <txhash> --output json
Query Events via REST
curl "https://api.testnet.mononodes.xyz/cosmos/tx/v1beta1/txs?events=transfer.sender='mono1abc...'" \
| jq '.tx_responses[].events'
Common Cosmos Event Types
| Module | Event Type | Key Attributes |
|---|---|---|
bank | transfer | sender, recipient, amount |
staking | delegate | delegator, validator, amount |
staking | unbond | delegator, validator, amount, completion_time |
staking | redelegate | delegator, source_validator, destination_validator |
gov | proposal_vote | voter, proposal_id, option |
gov | submit_proposal | proposal_id, proposal_type |
distribution | withdraw_rewards | delegator, validator, amount |
x/burn | fee_burn | amount, burned, proposer_reward |
x/validator | register_validator | validator, burn_amount, self_delegation |
EVM Logs
Smart contracts emit logs (events in Solidity) that are stored in transaction receipts. Each log has an address, indexed topics, and a data payload.
Log Structure
Log {
address: "0xContractAddress..."
topics: [
"0xddf252ad...", // topic[0]: event signature hash
"0x000...sender", // topic[1]: indexed param (from)
"0x000...recip" // topic[2]: indexed param (to)
]
data: "0x0000...amount" // non-indexed params (ABI-encoded)
}
Topic 0 is always the keccak256 hash of the event signature:
Transfer(address,address,uint256)
→ 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
Query Logs via eth_getLogs
curl -X POST https://evm.testnet.mononodes.xyz \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getLogs",
"params": [{
"fromBlock": "0x1",
"toBlock": "latest",
"address": "0xContractAddress...",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
]
}],
"id": 1
}'
Filter Parameters
| Parameter | Description |
|---|---|
fromBlock | Start block (hex or "earliest", "latest") |
toBlock | End block (hex or "latest") |
address | Contract address or array of addresses |
topics | Array of topic filters (positional, null for wildcard) |
{
"topics": [
"0xddf252ad...",
null,
"0x000...recipient"
]
}
This matches Transfer events to a specific recipient, from any sender.
Subscribing to Events
EVM WebSocket (eth_subscribe)
import { ethers } from "ethers";
const provider = new ethers.WebSocketProvider(
"wss://evm.testnet.mononodes.xyz"
);
// Subscribe to all new blocks
provider.on("block", (blockNumber) => {
console.log("New block:", blockNumber);
});
// Subscribe to ERC-20 Transfer events on a specific contract
const transferTopic = ethers.id("Transfer(address,address,uint256)");
const filter = {
address: "0xTokenContract...",
topics: [transferTopic],
};
provider.on(filter, (log) => {
const parsed = contractInterface.parseLog(log);
console.log(
`Transfer: ${parsed.args.from} → ${parsed.args.to}, ${ethers.formatEther(parsed.args.value)} tokens`
);
});
CometBFT WebSocket (/subscribe)
import WebSocket from "ws";
const ws = new WebSocket("wss://rpc.testnet.mononodes.xyz/websocket");
ws.on("open", () => {
// Subscribe to transfer events
ws.send(JSON.stringify({
jsonrpc: "2.0",
method: "subscribe",
id: 1,
params: {
query: "transfer.sender='mono1abc...'"
}
}));
// Subscribe to new blocks
ws.send(JSON.stringify({
jsonrpc: "2.0",
method: "subscribe",
id: 2,
params: {
query: "tm.event='NewBlock'"
}
}));
});
ws.on("message", (data) => {
const msg = JSON.parse(data.toString());
if (msg.result?.events) {
console.log("Events:", msg.result.events);
}
});
Parsing Examples
Parse EVM Logs with ethers.js
import { ethers } from "ethers";
const erc20Abi = [
"event Transfer(address indexed from, address indexed to, uint256 value)",
"event Approval(address indexed owner, address indexed spender, uint256 value)",
];
const iface = new ethers.Interface(erc20Abi);
const provider = new ethers.JsonRpcProvider("https://evm.testnet.mononodes.xyz");
// Get logs for a block range
const logs = await provider.getLogs({
address: "0xTokenContract...",
fromBlock: 100000,
toBlock: 100100,
topics: [ethers.id("Transfer(address,address,uint256)")],
});
for (const log of logs) {
const parsed = iface.parseLog(log);
if (parsed) {
console.log({
event: parsed.name,
from: parsed.args.from,
to: parsed.args.to,
value: ethers.formatEther(parsed.args.value),
block: log.blockNumber,
txHash: log.transactionHash,
});
}
}
Parse Cosmos Events with CosmJS
import { StargateClient } from "@cosmjs/stargate";
const client = await StargateClient.connect("https://rpc.testnet.mononodes.xyz");
// Get a specific transaction
const tx = await client.getTx("TXHASH...");
if (tx) {
// tx.events is an array of { type, attributes[] }
for (const event of tx.events) {
if (event.type === "transfer") {
const sender = event.attributes.find(a => a.key === "sender")?.value;
const recipient = event.attributes.find(a => a.key === "recipient")?.value;
const amount = event.attributes.find(a => a.key === "amount")?.value;
console.log(`Transfer: ${sender} → ${recipient}: ${amount}`);
}
}
}
// Search transactions by event
const txs = await client.searchTx("transfer.sender='mono1abc...'");
for (const tx of txs) {
console.log(`Tx ${tx.hash}: height ${tx.height}`);
}
Event Indexing Tips
- Block range limits:
eth_getLogshas a block range cap (typically 1,000-10,000 blocks). Paginate large queries. - Indexed parameters: Only indexed Solidity event parameters appear in
topics. Non-indexed parameters are ABI-encoded indata. - Cosmos event encoding: Attribute values are strings. Numeric values need parsing.
- WebSocket reconnection: Always implement reconnection logic for production subscriptions. WebSocket connections may drop.
Public RPC endpoints enforce rate limits on eth_getLogs and event subscription queries. For heavy indexing workloads, run your own node.
Related
- WebSocket API -- Full WebSocket endpoint reference
- EVM RPC Endpoints -- Supported RPC methods including
eth_getLogs - REST API -- Querying transactions via REST
- Transaction Lifecycle -- When events are emitted during execution