Events & Resources¶
Smart contract events provide a historical record of what happened on the blockchain. abi-to-mcp exposes these as MCP resources that AI assistants can query.
Overview¶
graph LR
A[Contract Event] --> B[Event Parser]
B --> C[MCP Resource]
C --> D[Historical Data]
style A fill:#627EEA,color:#fff
style C fill:#4CAF50,color:#fff
style D fill:#9C27B0,color:#fff
What Are Events?¶
Events are logs emitted by smart contracts during transactions:
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
function transfer(address to, uint256 amount) external returns (bool) {
// ... transfer logic ...
emit Transfer(msg.sender, to, amount);
return true;
}
Key properties: - Immutable - Once emitted, cannot be changed - Indexed Parameters - Up to 3 parameters can be indexed for efficient filtering - Gas Efficient - Cheaper than storage for historical data - Off-Chain Access - Can be queried without calling the contract
Events as MCP Resources¶
Each event becomes a queryable MCP resource:
Resource URI Format¶
Examples:
- Transfer → events://transfer
- Approval → events://approval
- OwnershipTransferred → events://ownership_transferred
Generated Resource¶
Solidity Event:
MCP Resource:
@mcp.resource("events://transfer")
async def get_transfer_events(
from_address: str = None,
to_address: str = None,
from_block: int = None,
to_block: int = None,
limit: int = 100
) -> list[dict]:
"""
Query Transfer events from the contract.
Args:
from_address: Filter by sender address (optional)
to_address: Filter by recipient address (optional)
from_block: Starting block number (optional)
to_block: Ending block number (optional)
limit: Maximum number of events to return
Returns:
List of Transfer events with block info
"""
...
Indexed vs Non-Indexed Parameters¶
Indexed Parameters¶
- Can be used for filtering
- Up to 3 per event
- Stored as topics in the log
event Transfer(
address indexed from, // Can filter by this
address indexed to, // Can filter by this
uint256 value // Cannot filter by this
);
Non-Indexed Parameters¶
- Cannot be used for filtering
- Stored in log data
- Still returned in results
Query Examples¶
Get All Transfers¶
Filter by Sender¶
# AI can query: "Show transfers from 0x123..."
events = await get_transfer_events(
from_address="0x123..."
)
Filter by Block Range¶
# AI can query: "Show transfers in the last 1000 blocks"
current_block = await w3.eth.block_number
events = await get_transfer_events(
from_block=current_block - 1000,
to_block=current_block
)
Combined Filters¶
# AI can query: "Show transfers to 0x456... in last 100 blocks"
events = await get_transfer_events(
to_address="0x456...",
from_block=current_block - 100
)
Event Data Structure¶
Each returned event includes:
{
"event": "Transfer",
"block_number": 18500000,
"block_hash": "0x...",
"transaction_hash": "0x...",
"transaction_index": 42,
"log_index": 7,
"args": {
"from": "0x123...",
"to": "0x456...",
"value": "1000000000000000000"
}
}
Common Event Patterns¶
ERC20 Events¶
| Event | Purpose | Indexed Args |
|---|---|---|
Transfer(from, to, value) |
Token transfers | from, to |
Approval(owner, spender, value) |
Spending approvals | owner, spender |
ERC721 Events¶
| Event | Purpose | Indexed Args |
|---|---|---|
Transfer(from, to, tokenId) |
NFT transfers | from, to, tokenId |
Approval(owner, approved, tokenId) |
Single approval | owner, approved, tokenId |
ApprovalForAll(owner, operator, approved) |
Operator approval | owner, operator |
Governance Events¶
| Event | Purpose | Indexed Args |
|---|---|---|
ProposalCreated(proposalId, ...) |
New proposals | proposalId |
VoteCast(voter, proposalId, ...) |
Votes | voter, proposalId |
ProposalExecuted(proposalId) |
Execution | proposalId |
Performance Considerations¶
Block Range Limits¶
Most RPC providers limit how many blocks you can query:
| Provider | Typical Limit |
|---|---|
| Alchemy | 2,000 blocks |
| Infura | 10,000 blocks |
| QuickNode | 10,000 blocks |
| Public RPCs | 1,000 blocks |
Large Queries
Querying all events from block 0 will likely fail or timeout. Always use reasonable block ranges.
Pagination¶
For large result sets, events are paginated:
# Get first 100 events
events = await get_transfer_events(limit=100)
# Get next 100 events (using last block as starting point)
last_block = events[-1]["block_number"]
more_events = await get_transfer_events(
from_block=last_block,
limit=100
)
Excluding Events¶
If you don't need events, you can exclude them:
This: - Reduces generated code size - Simplifies the server - Still includes all tools
Anonymous Events¶
Anonymous events don't have a topic signature:
These are currently not supported by abi-to-mcp because they cannot be reliably filtered.
Use Cases¶
Transaction History¶
"Show me the last 10 transfers from this wallet"
Balance Verification¶
"Verify this wallet received 100 tokens"
events = await get_transfer_events(
to_address="0x...",
from_block=expected_block - 10,
to_block=expected_block + 10
)
# Check if expected transfer exists
Activity Monitoring¶
"Has there been any activity on this contract today?"
blocks_per_day = 7200 # ~12 second blocks
events = await get_transfer_events(
from_block=current_block - blocks_per_day,
limit=10
)
Related¶
- Tool Types - How write operations emit events
- API Reference - ResourceGenerator API
- Guides - Working with token events