Filter, query, and decode smart contract events and logs using Nethereum (.NET). Use this skill whenever the user asks about event filtering, log querying, listening for contract events, decoding event logs, Transfer events, Approval events, or any EVM log processing with C# or .NET.
NuGet: Nethereum.Web3
dotnet add package Nethereum.Web3
Map a Solidity event to a C# class using [Event] and [Parameter] attributes. The fourth argument (true) marks indexed (topic) parameters.
using System.Numerics;
using Nethereum.ABI.FunctionEncoding.Attributes;
using Nethereum.Contracts;
[Event("Transfer")]
public class TransferEventDTO : IEventDTO
{
[Parameter("address", "_from", 1, true)]
public string From { get; set; }
[Parameter("address", "_to", 2, true)]
public string To { get; set; }
[Parameter("uint256", "_value", 3, false)]
public BigInteger Value { get; set; }
}
Nethereum ships TransferEventDTO and for ERC-20 in .
ApprovalEventDTONethereum.Contracts.Standards.ERC20.ContractDefinitionvar web3 = new Web3("https://mainnet.infura.io/v3/YOUR_KEY");
var contractAddress = "0xYourContractAddress";
// From web3
var transferEvent = web3.Eth.GetEvent<TransferEventDTO>(contractAddress);
// From contract handler
var contractHandler = web3.Eth.GetContractHandler(contractAddress);
var transferEvent = contractHandler.GetEvent<TransferEventDTO>();
var filterInput = transferEvent.CreateFilterInput(
BlockParameter.CreateEarliest(),
BlockParameter.CreateLatest());
var allTransfers = await transferEvent.GetAllChangesAsync(filterInput);
foreach (var transfer in allTransfers)
{
Console.WriteLine(
$"From: {transfer.Event.From} To: {transfer.Event.To} Value: {transfer.Event.Value}");
}
Indexed parameters become topics the node filters server-side.
var filterByFrom = transferEvent.CreateFilterInput(fromAddress);
var results = await transferEvent.GetAllChangesAsync(filterByFrom);
Pass null for the first topic to skip it:
var filterByTo = transferEvent.CreateFilterInput<string, string>(null, toAddress);
var results = await transferEvent.GetAllChangesAsync(filterByTo);
var filterBoth = transferEvent.CreateFilterInput(fromAddress, toAddress);
var results = await transferEvent.GetAllChangesAsync(filterBoth);
var filterFromArray = transferEvent.CreateFilterInput(new[] { addr1, addr2 });
var filterToArray = transferEvent.CreateFilterInput(null, new[] { recipA, recipB });
var filterBothArrays = transferEvent.CreateFilterInput(
new[] { addr1, addr2 },
new[] { recipA, recipB });
Install a filter and poll for changes instead of querying history:
var contractHandler = web3.Eth.GetContractHandler(contractAddress);
var eventFilter = contractHandler.GetEvent<TransferEventDTO>();
var filterId = await eventFilter.CreateFilterAsync();
// ... transactions happen ...
var newEvents = await eventFilter.GetFilterChangesAsync(filterId);
foreach (var evt in newEvents)
{
Console.WriteLine($"New transfer: {evt.Event.Value}");
}
var receipt = await web3.Eth.GetTransactionReceipt
.SendRequestAsync(transactionHash);
var transfers = receipt.DecodeAllEvents<TransferEventDTO>();
foreach (var transfer in transfers)
{
Console.WriteLine(
$"From: {transfer.Event.From} To: {transfer.Event.To} Value: {transfer.Event.Value}");
}
From a JArray of logs:
var decoded = receipt.Logs.DecodeAllEvents<TransferEventDTO>();
[Event("ItemCreated")]
public class ItemCreatedEventDTO : IEventDTO
{
[Parameter("uint256", "itemId", 1, true)]
public BigInteger ItemId { get; set; }
[Parameter("address", "result", 2, false)]
public string Result { get; set; }
}
var items = receipt.DecodeAllEvents<ItemCreatedEventDTO>();
// No contract address -- matches all contracts
var eventForAny = new Event<TransferEventDTO>(web3.Client);
var filterInput = eventForAny.CreateFilterInput();
var allTransfers = await eventForAny.GetAllChangesAsync(filterInput);
// Scoped to one contract
var eventForOne = new Event<TransferEventDTO>(web3.Client, contractAddress);
var results = await eventForOne.GetAllChangesAsync(eventForOne.CreateFilterInput());
using Nethereum.Contracts.Standards.ERC20.ContractDefinition;
// TransferEventDTO - Transfer(address indexed _from, address indexed _to, uint256 _value)
// ApprovalEventDTO - Approval(address indexed _owner, address indexed _spender, uint256 _value)
var transfers = receipt.DecodeAllEvents<TransferEventDTO>();
var approvals = receipt.DecodeAllEvents<ApprovalEventDTO>();
Fluent, lambda-based API for building event filters -- reference indexed properties by name with compile-time checking:
// Filter by sender
var filter = new FilterInputBuilder<TransferEventDTO>()
.AddTopic(t => t.From, "0xSenderAddress")
.Build(contractAddress);
var results = await transferEvent.GetAllChangesAsync(filter);
// OR matching
var filter = new FilterInputBuilder<TransferEventDTO>()
.AddTopic(t => t.From, new[] { "0xAddr1", "0xAddr2" })
.Build(contractAddress);
// Multiple indexed params + block range
var filter = new FilterInputBuilder<TransferEventDTO>()
.AddTopic(t => t.From, "0xFrom")
.AddTopic(t => t.To, "0xTo")
.Build(contractAddress, BlockParameter.CreateEarliest(), BlockParameter.CreateLatest());
// Multiple contracts
var filter = new FilterInputBuilder<TransferEventDTO>()
.AddTopic(t => t.From, "0xSender")
.Build(new[] { contract1, contract2 });
Event<T> -- typed event handler, created from web3 or clientFilterInputBuilder<T> -- fluent lambda-based filter builder for indexed topicsIEventDTO -- marker interface for event DTOs[Event("Name")] -- maps DTO to Solidity event name[Parameter("type", "name", order, indexed)] -- maps DTO property to event parameterEventLog<T> -- wrapper with .Event (decoded DTO), .Log (raw FilterLog)NewFilterInput -- filter configuration with address, topics, block rangeBlockParameter.CreateEarliest() / BlockParameter.CreateLatest() -- block range bounds| Task | Method |
|---|---|
| Get event handler from web3 | web3.Eth.GetEvent<T>(address) |
| Get event handler from contract | contractHandler.GetEvent<T>() |
| Query historical events | event.GetAllChangesAsync(filterInput) |
| Install live filter | event.CreateFilterAsync() |
| Poll live filter | event.GetFilterChangesAsync(filterId) |
| Decode from receipt | receipt.DecodeAllEvents<T>() |
| Decode from JArray logs | receipt.Logs.DecodeAllEvents<T>() |
| Filter by first indexed param | event.CreateFilterInput(value) |
| Filter by second indexed param | event.CreateFilterInput<T1,T2>(null, value) |
| Filter by both indexed params | event.CreateFilterInput(val1, val2) |
| Filter by address arrays | event.CreateFilterInput(new[]{...}, new[]{...}) |
| Cross-contract query | new Event<T>(web3.Client) |