Nethereum.Contracts 5.8.0

Prefix Reserved
dotnet add package Nethereum.Contracts --version 5.8.0
                    
NuGet\Install-Package Nethereum.Contracts -Version 5.8.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Nethereum.Contracts" Version="5.8.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nethereum.Contracts" Version="5.8.0" />
                    
Directory.Packages.props
<PackageReference Include="Nethereum.Contracts" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Nethereum.Contracts --version 5.8.0
                    
#r "nuget: Nethereum.Contracts, 5.8.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Nethereum.Contracts@5.8.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Nethereum.Contracts&version=5.8.0
                    
Install as a Cake Addin
#tool nuget:?package=Nethereum.Contracts&version=5.8.0
                    
Install as a Cake Tool

Nethereum.Contracts

Nethereum.Contracts is the core library for interacting with Ethereum smart contracts via RPC. It provides strongly-typed contract interaction, automatic ABI encoding/decoding, event filtering, and comprehensive support for deployment, function calls, and event handling.

Features

  • Strongly-Typed Contract Interaction - Generate C# classes from ABI
  • Contract Deployment - Deploy contracts with constructor parameters
  • Function Calls - Call and send contract functions
  • Event Handling - Query and decode contract events
  • ERC Standards - Built-in support for ERC20, ERC721, ERC1155, ERC1271, ERC1820, ERC165, ERC2535, ERC6492
  • EIP Standards - EIP-3009 (transfer with authorization), EIP-6093 (custom errors)
  • ENS Support - Ethereum Name Service resolution
  • Multicall - Batch multiple contract calls
  • Query Handlers - Simplified contract queries
  • Transaction Handlers - Simplified contract transactions
  • AOT Compatible - Works with Native AOT compilation

Installation

dotnet add package Nethereum.Contracts

Dependencies

Nethereum:

  • Nethereum.ABI - ABI encoding/decoding
  • Nethereum.RPC - RPC functionality
  • Nethereum.Hex - Hex utilities
  • Nethereum.Util.Rest - REST utilities for HTTP metadata

External:

  • ADRaffy.ENSNormalize (v0.1.5) - ENS name normalization (UTS-46/ENSIP-15)

Key Concepts

Contract Interaction Patterns

Nethereum.Contracts supports three main patterns for interacting with smart contracts:

1. Direct ABI Usage - Simple, dynamic approach using ABI JSON strings 2. Strongly-Typed DTOs - Type-safe with IntelliSense (recommended) 3. Code Generation - Automated C# class generation from ABI (production recommended)

Architecture

The library is organized around these core concepts:

  • Contract - Represents a deployed smart contract at a specific address
  • Function - Represents a contract function (call or transaction)
  • Event - Represents a contract event for filtering and decoding
  • ContractHandler - Unified interface for contract operations
  • Standard Services - Pre-built services for ERC20, ERC721, ERC1155, etc.

Typed vs Untyped: Which Should You Use?

Nethereum supports two fundamental approaches for contract interaction:

Untyped Approach (Dynamic)

When to use:

  • Quick scripts and one-off queries
  • Rapid prototyping
  • Simple contracts with few functions
  • When you don't need compile-time safety

Advantages:

  • Less code (no DTO classes needed)
  • Faster to write initially
  • Flexible for exploration

Disadvantages:

  • No compile-time type checking
  • No IntelliSense
  • Parameter order errors only caught at runtime
  • Harder to maintain
  • No refactoring support

Example:

var abi = @"[...]";
var contract = web3.Eth.GetContract(abi, contractAddress);
var balanceFunction = contract.GetFunction("balanceOf");
var balance = await balanceFunction.CallAsync<BigInteger>("0x742d35Cc...");
// If you pass wrong type or wrong order, you only find out at runtime!
Typed Approach (DTOs)

When to use:

  • Production applications
  • Team development
  • Complex contracts
  • When compile-time safety matters
  • Long-term maintained code

Advantages:

  • Compile-time type checking
  • IntelliSense support
  • Clear parameter names
  • Refactoring support
  • Self-documenting code
  • Catches errors before deployment

Disadvantages:

  • More initial code (DTO classes)
  • Requires understanding of attributes
  • Slightly more verbose

Example:

[Function("balanceOf", "uint256")]
public class BalanceOfFunction : FunctionMessage
{
    [Parameter("address", "owner", 1)]
    public string Owner { get; set; }
}

var queryHandler = web3.Eth.GetContractQueryHandler<BalanceOfFunction>();
var balance = await queryHandler.QueryAsync<BigInteger>(
    contractAddress,
    new BalanceOfFunction { Owner = "0x742d35Cc..." }
);
// Wrong type = compile error, not runtime error!

Comparison Table:

Feature Untyped Typed Code Generation
Compile-time safety
IntelliSense
Lines of code Fewest More Auto-generated
Learning curve Easy Medium Easy (after setup)
Maintainability Poor Good Excellent
Best for Scripts Production Production

Our Recommendation:

  • Prototyping: Start with untyped
  • Production: Use typed DTOs or code generation
  • Large projects: Always use code generation

From: Nethereum Playground Example 1007 (typed), Example 1045 (untyped)

Quick Start

Interacting with ERC20 Token

From: Nethereum Playground Example 1005

using Nethereum.Web3;
using Nethereum.Contracts.Standards.ERC20;

var web3 = new Web3("https://mainnet.infura.io/v3/YOUR-PROJECT-ID");

// ERC20 service for any token
var tokenAddress = "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2"; // MKR token
var erc20Service = web3.Eth.ERC20.GetContractService(tokenAddress);

// Get token details
var name = await erc20Service.NameQueryAsync();
var symbol = await erc20Service.SymbolQueryAsync();
var decimals = await erc20Service.DecimalsQueryAsync();
var totalSupply = await erc20Service.TotalSupplyQueryAsync();

Console.WriteLine($"Token: {name} ({symbol})");
Console.WriteLine($"Decimals: {decimals}");
Console.WriteLine($"Total Supply: {totalSupply}");

// Get balance
var holderAddress = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e";
var balance = await erc20Service.BalanceOfQueryAsync(holderAddress);
Console.WriteLine($"Balance: {balance}");

Deploying and Interacting with Custom Contract

From: Nethereum Playground Example 1006

The typed approach uses ContractDeploymentMessage for compile-time safety:

using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.Contracts;
using Nethereum.ABI.FunctionEncoding.Attributes;
using Nethereum.Contracts.CQS;

// Define deployment message with constructor parameters
public class StandardTokenDeployment : ContractDeploymentMessage
{
    public static string BYTECODE = "0x60606040526040516020806106f5833981016040528080519060200190919050505b80600160005060003373ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060005081905550806000600050819055505b506106868061006f6000396000f360606040523615610074576000357c010000000000000000000000000000000000000000000000000000000090048063095ea7b31461008157806318160ddd146100b657806323b872dd146100d957806370a0823114610117578063a9059cbb14610143578063dd62ed3e1461017857610074565b61007f5b610002565b565b005b6100a060048080359060200190919080359060200190919050506101ad565b6040518082815260200191505060405180910390f35b6100c36004805050610674565b6040518082815260200191505060405180910390f35b6101016004808035906020019091908035906020019091908035906020019091905050610281565b6040518082815260200191505060405180910390f35b61012d600480803590602001909190505061048d565b6040518082815260200191505060405180910390f35b61016260048080359060200190919080359060200190919050506104cb565b6040518082815260200191505060405180910390f35b610197600480803590602001909190803590602001909190505061060b565b6040518082815260200191505060405180910390f35b600081600260005060003373ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060005060008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905061027b565b92915050565b600081600160005060008673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050541015801561031b575081600260005060008673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060005060003373ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000505410155b80156103275750600082115b1561047c5781600160005060008573ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828282505401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a381600160005060008673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282825054039250508190555081600260005060008673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060005060003373ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828282505403925050819055506001905061048656610485565b60009050610486565b5b9392505050565b6000600160005060008373ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000505490506104c6565b919050565b600081600160005060003373ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050541015801561050c5750600082115b156105fb5781600160005060003373ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282825054039250508190555081600160005060008573ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828282505401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905061060556610604565b60009050610605565b5b92915050565b6000600260005060008473ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060005060008373ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060005054905061066e565b92915050565b60006000600050549050610683565b9056";

    public StandardTokenDeployment() : base(BYTECODE)
    {
    }

    [Parameter("uint256", "totalSupply")]
    public BigInteger TotalSupply { get; set; }
}

var privateKey = "0x7580e7fb49df1c861f0050fae31c2224c6aba908e116b8da44ee8cd927b990b0";
var chainId = 444444444500;
var account = new Account(privateKey, chainId);
var web3 = new Web3(account, "http://testchain.nethereum.com:8545");

// Create deployment message with constructor parameters
var deploymentMessage = new StandardTokenDeployment
{
    TotalSupply = 100000
};

// Deploy using typed handler
var deploymentHandler = web3.Eth.GetContractDeploymentHandler<StandardTokenDeployment>();
var transactionReceipt = await deploymentHandler.SendRequestAndWaitForReceiptAsync(deploymentMessage);
var contractAddress = transactionReceipt.ContractAddress;

Console.WriteLine($"Contract deployed at: {contractAddress}");

Why use typed deployment:

  • Constructor parameters are strongly-typed properties
  • Compile-time validation of parameter types and order
  • IntelliSense support for deployment parameters
  • Automatic gas estimation, nonce management
  • Self-documenting deployment code
Untyped Deployment (Alternative)

For quick scripts or when you don't want to define deployment classes:

var abi = @"[{'inputs':[{'name':'totalSupply','type':'uint256'}],'stateMutability':'nonpayable','type':'constructor'}...]";
var bytecode = "0x608060405234801561001057600080fd5b50...";

// Deploy with constructor parameters as params
var receipt = await web3.Eth.DeployContract.SendRequestAndWaitForReceiptAsync(
    abi,
    bytecode,
    account.Address,
    new Nethereum.Hex.HexTypes.HexBigInteger(3000000), // gas
    null, // gas price
    null, // value
    100000 // constructor parameter: totalSupply
);

var contractAddress = receipt.ContractAddress;

Trade-offs:

  • Faster to write for simple deployments
  • No compile-time checking of constructor parameters
  • Easy to pass wrong parameter type or order
  • Less maintainable for complex constructors

## Contract Definition Patterns

### Pattern 1: Using Contract ABI Directly

Simple approach for quick interactions:

```csharp
var abi = @"[{'inputs':[],'name':'totalSupply','outputs':[{'type':'uint256'}],'type':'function'}]";
var contract = web3.Eth.GetContract(abi, contractAddress);

var function = contract.GetFunction("totalSupply");
var totalSupply = await function.CallAsync<BigInteger>();

Define C# classes for type safety and IntelliSense:

using Nethereum.Contracts;
using Nethereum.ABI.FunctionEncoding.Attributes;

// Function message
[Function("balanceOf", "uint256")]
public class BalanceOfFunction : FunctionMessage
{
    [Parameter("address", "_owner", 1)]
    public string Owner { get; set; }
}

// Usage
var balanceOfFunction = contract.GetFunction<BalanceOfFunction>();
var balance = await balanceOfFunction.CallAsync<BigInteger>(new BalanceOfFunction
{
    Owner = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
});

Extension Methods for FunctionMessage

Nethereum provides powerful extension methods to simplify common operations with FunctionMessages:

using Nethereum.Contracts.Extensions;

// 1. Create CallInput for eth_call (read-only)
var balanceOfFunction = new BalanceOfFunction { Owner = senderAddress };
var callInput = balanceOfFunction.CreateCallInput(contractAddress);
// Use for EVM simulation, Nethereum.EVM, etc.

// 2. Create TransactionInput for transactions
var transferFunction = new TransferFunction { To = receiver, TokenAmount = 1000 };
var transactionInput = transferFunction.CreateTransactionInput(contractAddress);
// Complete TransactionInput ready to sign

// 3. Get encoded function call data
var callData = transferFunction.GetCallData();
Console.WriteLine($"Encoded data: {callData.ToHex()}");
// Perfect for analyzing transactions, debugging ABI encoding

// 4. Decode transaction input to FunctionMessage
var txn = await web3.Eth.Transactions.GetTransactionByHash.SendRequestAsync("0x...");
var decodedTransfer = new TransferFunction().DecodeTransaction(txn);
Console.WriteLine($"Decoded to: {decodedTransfer.To}, amount: {decodedTransfer.TokenAmount}");

// 5. Check if transaction matches function signature
if (txn.IsTransactionForFunctionMessage<TransferFunction>())
{
    var transfer = new TransferFunction().DecodeTransaction(txn);
    Console.WriteLine($"Transfer to {transfer.To}");
}

Common Use Cases:

  • EVM Simulation: Use CreateCallInput() to simulate contract calls locally
  • Transaction Analysis: Use DecodeTransaction() to parse historical transactions
  • Signature Verification: Use IsTransactionForFunctionMessage<T>() to filter transactions
  • Data Extraction: Use GetCallData() to get raw encoded function data

From: Nethereum Playground Example 1063, Example 1075, Example 1079

For production applications, use the Nethereum Code Generator to automatically create strongly-typed C# classes from your contract ABI. This eliminates manual DTO creation, reduces errors, and provides the most type-safe contract interaction pattern.

Installation:

dotnet tool install -g Nethereum.Generator.Console

Generate from ABI:

Nethereum.Generator.Console generate from-abi \
    -abi MyContract.abi.json \
    -o Generated \
    -n MyApp.Contracts

Generate from Project (Truffle/Hardhat):

Nethereum.Generator.Console generate from-project \
    -p ./contracts \
    -o ./src/Generated \
    -n MyApp.Contracts
What Gets Generated

The code generator creates a complete ContractService class that provides:

  1. Deployment Messages - Typed constructor parameters
  2. Function Messages - All function DTOs with parameters
  3. Event DTOs - All event definitions
  4. ContractService - High-level wrapper with typed methods

Key Generated Methods:

public class StandardTokenService
{
    // Static deployment method - deploys and returns service instance
    public static Task<StandardTokenService> DeployContractAndGetServiceAsync(
        Web3 web3,
        StandardTokenDeployment deployment
    );

    // Typed query methods - for view/pure functions
    public Task<BigInteger> BalanceOfQueryAsync(string owner);
    public Task<BigInteger> TotalSupplyQueryAsync();
    public Task<BigInteger> AllowanceQueryAsync(string owner, string spender);

    // Typed transaction methods - for state-changing functions
    public Task<TransactionReceipt> TransferRequestAndWaitForReceiptAsync(TransferFunction function);
    public Task<TransactionReceipt> ApproveRequestAndWaitForReceiptAsync(ApproveFunction function);

    // Event access
    public Event<TransferEventDTO> GetTransferEvent();
}
Complete Example: Manual vs Generated

Before (Manual Pattern):

// Define deployment message manually
public class StandardTokenDeployment : ContractDeploymentMessage
{
    public static string BYTECODE = "0x608060...";
    public StandardTokenDeployment() : base(BYTECODE) { }

    [Parameter("uint256", "totalSupply")]
    public BigInteger TotalSupply { get; set; }
}

// Define function messages manually
[Function("balanceOf", "uint256")]
public class BalanceOfFunction : FunctionMessage
{
    [Parameter("address", "owner", 1)]
    public string Owner { get; set; }
}

[Function("transfer", "bool")]
public class TransferFunction : FunctionMessage
{
    [Parameter("address", "to", 1)]
    public string To { get; set; }

    [Parameter("uint256", "value", 2)]
    public BigInteger Value { get; set; }
}

// Deploy manually
var deploymentHandler = web3.Eth.GetContractDeploymentHandler<StandardTokenDeployment>();
var receipt = await deploymentHandler.SendRequestAndWaitForReceiptAsync(new StandardTokenDeployment
{
    TotalSupply = 100000
});
var contractAddress = receipt.ContractAddress;

// Query manually
var balanceOfHandler = web3.Eth.GetContractQueryHandler<BalanceOfFunction>();
var balance = await balanceOfHandler.QueryAsync<BigInteger>(
    contractAddress,
    new BalanceOfFunction { Owner = address }
);

// Transaction manually
var transferHandler = web3.Eth.GetContractTransactionHandler<TransferFunction>();
var transferReceipt = await transferHandler.SendRequestAndWaitForReceiptAsync(
    contractAddress,
    new TransferFunction { To = receiver, Value = 1000 }
);

After (Generated Pattern):

using MyApp.Contracts.StandardToken.Service;
using MyApp.Contracts.StandardToken.ContractDefinition;

// Deploy and get service in ONE call
var tokenService = await StandardTokenService.DeployContractAndGetServiceAsync(
    web3,
    new StandardTokenDeployment { TotalSupply = 100000 }
);

// Query with simple typed method
var balance = await tokenService.BalanceOfQueryAsync(address);

// Transaction with simple typed method
var transferReceipt = await tokenService.TransferRequestAndWaitForReceiptAsync(
    new TransferFunction { To = receiver, Value = 1000 }
);

// Access events through service
var transferEvent = tokenService.GetTransferEvent();
var filter = transferEvent.CreateFilterInput(fromAddress: address);
var logs = await transferEvent.GetAllChangesAsync(filter);

Comparison:

Aspect Manual Generated
Lines of code ~80 lines ~15 lines
Deployment 5 steps 1 step
Query 4 steps 1 step
Transaction 4 steps 1 step
Type safety Manual DTOs Auto-generated DTOs
Refactoring Manual updates Regenerate from ABI
IntelliSense Full Full
Maintainability High effort Low effort

Why Use Code Generation:

  1. Eliminates boilerplate - No manual DTO creation
  2. Single source of truth - ABI is the only source
  3. Compile-time safety - All parameters typed
  4. Easy refactoring - Regenerate when contract changes
  5. IntelliSense support - All methods discoverable
  6. Less error-prone - No manual attribute decoration
  7. Faster development - Write business logic, not infrastructure

For comprehensive code generation documentation, see: Nethereum Code Generation Guide

Usage Examples

Example 1: ERC20 Token Transfer

using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.Contracts;
using Nethereum.ABI.FunctionEncoding.Attributes;

var account = new Account("0xPRIVATE_KEY", chainId: 1);
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");

// Transfer function DTO
[Function("transfer", "bool")]
public class TransferFunction : FunctionMessage
{
    [Parameter("address", "to", 1)]
    public string To { get; set; }

    [Parameter("uint256", "amount", 2)]
    public BigInteger Amount { get; set; }
}

// Get ERC20 contract
var tokenAddress = "0x...";
var contract = web3.Eth.GetContract("[...abi...]", tokenAddress);

// Send transfer transaction
var transferHandler = web3.Eth.GetContractTransactionHandler<TransferFunction>();
var receipt = await transferHandler.SendRequestAndWaitForReceiptAsync(
    tokenAddress,
    new TransferFunction
    {
        To = "0xRECIPIENT",
        Amount = 1000000000000000000, // 1 token (18 decimals)
        Gas = 100000
    }
);

Console.WriteLine($"Transfer transaction: {receipt.TransactionHash}");
Console.WriteLine($"Status: {(receipt.Status.Value == 1 ? "Success" : "Failed")}");

Example 1a: Complex Return Types (Multiple Values)

From: Nethereum Playground Example 1007

For functions that return multiple values or complex objects, use QueryDeserializingToObjectAsync with a FunctionOutputDTO:

using Nethereum.Web3;
using Nethereum.Contracts;
using Nethereum.ABI.FunctionEncoding.Attributes;

// Function that returns a single value
[Function("balanceOf", "uint256")]
public class BalanceOfFunction : FunctionMessage
{
    [Parameter("address", "owner", 1)]
    public string Owner { get; set; }
}

// Output DTO for deserializing the return value
[FunctionOutput]
public class BalanceOfOutputDTO : IFunctionOutputDTO
{
    [Parameter("uint256", "balance", 1)]
    public BigInteger Balance { get; set; }
}

var web3 = new Web3("https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
var contractAddress = "0x...";

var balanceHandler = web3.Eth.GetContractQueryHandler<BalanceOfFunction>();
var balanceMessage = new BalanceOfFunction { Owner = "0xOWNER" };

// Query and deserialize to output DTO
var balanceOutput = await balanceHandler.QueryDeserializingToObjectAsync<BalanceOfOutputDTO>(
    balanceMessage,
    contractAddress
);

Console.WriteLine($"Balance: {balanceOutput.Balance}");

// Query at specific block number (historical state)
var balanceAtBlock = await balanceHandler.QueryDeserializingToObjectAsync<BalanceOfOutputDTO>(
    balanceMessage,
    contractAddress,
    new Nethereum.RPC.Eth.DTOs.BlockParameter(15000000)
);

For functions with multiple return values:

[FunctionOutput]
public class TokenInfoOutputDTO : IFunctionOutputDTO
{
    [Parameter("string", "name", 1)]
    public string Name { get; set; }

    [Parameter("string", "symbol", 2)]
    public string Symbol { get; set; }

    [Parameter("uint8", "decimals", 3)]
    public byte Decimals { get; set; }
}

// Use same QueryDeserializingToObjectAsync pattern
var tokenInfo = await handler.QueryDeserializingToObjectAsync<TokenInfoOutputDTO>(
    functionMessage,
    contractAddress
);

Example 1b: Transaction Customization

From: Nethereum Playground Example 1007

All FunctionMessage and ContractDeploymentMessage classes support transaction customization through properties:

using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.Contracts;
using Nethereum.Util;

var account = new Account("0xPRIVATE_KEY", chainId: 1);
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");

var transferFunction = new TransferFunction
{
    To = "0xRECIPIENT",
    Amount = 1000,

    // Customize gas limit
    Gas = 100000,

    // Customize gas price (in Wei, convert from Gwei)
    GasPrice = Web3.Convert.ToWei(25, UnitConversion.EthUnit.Gwei),

    // Customize nonce (usually auto-calculated)
    Nonce = 5,

    // Send Ether along with function call
    AmountToSend = Web3.Convert.ToWei(0.1, UnitConversion.EthUnit.Ether)
};

var transferHandler = web3.Eth.GetContractTransactionHandler<TransferFunction>();
var receipt = await transferHandler.SendRequestAndWaitForReceiptAsync(
    contractAddress,
    transferFunction
);

Estimating Gas:

var transferFunction = new TransferFunction { To = receiver, Amount = 1000 };
var transferHandler = web3.Eth.GetContractTransactionHandler<TransferFunction>();

// Estimate gas for this specific transaction
var estimatedGas = await transferHandler.EstimateGasAsync(contractAddress, transferFunction);
transferFunction.Gas = estimatedGas.Value;

// Now send with accurate gas estimate
var receipt = await transferHandler.SendRequestAndWaitForReceiptAsync(
    contractAddress,
    transferFunction
);

When to customize:

  • Gas: Estimate first, then set manually if needed for complex transactions
  • GasPrice: Set higher for faster confirmation, lower to save costs
  • Nonce: Usually auto-managed, set manually only for offline signing or parallel transactions
  • AmountToSend: When function is payable and requires Ether

Example 1c: Offline Transaction Signing

From: Nethereum Playground Example 1007

For offline signing, set all transaction parameters and use SignTransactionAsync:

using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.Contracts;
using Nethereum.Util;

var account = new Account("0xPRIVATE_KEY", chainId: 1);
var web3 = new Web3(account);  // No RPC endpoint needed for signing

var transferFunction = new TransferFunction
{
    To = "0xRECIPIENT",
    Amount = 1000,

    // MUST set all values for offline signing
    Nonce = 2,
    Gas = 21000,
    GasPrice = Web3.Convert.ToWei(25, UnitConversion.EthUnit.Gwei)
};

var transferHandler = web3.Eth.GetContractTransactionHandler<TransferFunction>();

// Sign transaction offline
var signedTransaction = await transferHandler.SignTransactionAsync(
    contractAddress,
    transferFunction
);

Console.WriteLine($"Signed transaction: {signedTransaction}");

// Later, broadcast signed transaction to network
web3 = new Web3("https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
var txHash = await web3.Eth.Transactions.SendRawTransaction.SendRequestAsync(signedTransaction);
Console.WriteLine($"Transaction hash: {txHash}");

Important for offline signing:

  • Nonce must be known in advance (query online first, then sign offline)
  • Gas and GasPrice must be estimated or set manually
  • ChainId must be correct for the target network
  • Signed transaction is a hex string that can be stored or transmitted

Example 2: Querying Contract Events

From: Nethereum Playground Example 1008

using Nethereum.Web3;
using Nethereum.Contracts;
using Nethereum.ABI.FunctionEncoding.Attributes;

// Transfer event DTO
[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; }
}

var web3 = new Web3("https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
var tokenAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; // DAI

// Get contract
var contract = web3.Eth.GetContract("[...abi...]", tokenAddress);

// Get transfer event
var transferEvent = contract.GetEvent<TransferEventDTO>();

// Create filter for specific address
var filterInput = transferEvent.CreateFilterInput(
    fromAddress: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
    fromBlock: new Nethereum.RPC.Eth.DTOs.BlockParameter(18000000),
    toBlock: new Nethereum.RPC.Eth.DTOs.BlockParameter(18000100)
);

// Get all transfer events
var logs = await transferEvent.GetAllChangesAsync(filterInput);

foreach (var log in logs)
{
    Console.WriteLine($"Transfer from {log.Event.From} to {log.Event.To}");
    Console.WriteLine($"Amount: {Web3.Convert.FromWei(log.Event.Value)}");
    Console.WriteLine($"Block: {log.Log.BlockNumber}");
    Console.WriteLine($"Tx: {log.Log.TransactionHash}");
    Console.WriteLine();
}

Example 3: Decoding Events from Transaction Receipt

using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.Contracts;

var account = new Account("0xPRIVATE_KEY");
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");

// Send transaction
var tokenAddress = "0x...";
var contract = web3.Eth.GetContract("[...abi...]", tokenAddress);
var transferFunction = contract.GetFunction("transfer");

var receipt = await transferFunction.SendTransactionAndWaitForReceiptAsync(
    account.Address,
    new Nethereum.Hex.HexTypes.HexBigInteger(100000),
    null,
    "0xRECIPIENT",
    1000000
);

// Decode all events from receipt
var transferEvents = receipt.DecodeAllEvents<TransferEventDTO>();

foreach (var transferEvent in transferEvents)
{
    Console.WriteLine($"Decoded Transfer Event:");
    Console.WriteLine($"  From: {transferEvent.Event.From}");
    Console.WriteLine($"  To: {transferEvent.Event.To}");
    Console.WriteLine($"  Value: {transferEvent.Event.Value}");
}

Example 3a: Advanced Event Filtering

From: Nethereum Playground Example 1008

Nethereum provides powerful event filtering capabilities using indexed parameters:

using Nethereum.Web3;
using Nethereum.Contracts;
using Nethereum.ABI.FunctionEncoding.Attributes;

[Event("Transfer")]
public class TransferEventDTO : IEventDTO
{
    [Parameter("address", "_from", 1, true)]  // indexed
    public string From { get; set; }

    [Parameter("address", "_to", 2, true)]    // indexed
    public string To { get; set; }

    [Parameter("uint256", "_value", 3, false)] // not indexed
    public BigInteger Value { get; set; }
}

var web3 = new Web3("https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
var tokenAddress = "0x...";
var transferEvent = web3.Eth.GetEvent<TransferEventDTO>(tokenAddress);

// 1. Filter by specific sender AND receiver
var filterBoth = transferEvent.CreateFilterInput(
    fromAddress: "0xSENDER",
    toAddress: "0xRECEIVER"
);
var resultsBoth = await transferEvent.GetAllChangesAsync(filterBoth);

// 2. Filter by receiver only (skip sender with null)
// Note: Must use array format to pass null for first parameter
var filterReceiverOnly = transferEvent.CreateFilterInput(
    null,  // Any sender
    new[] { "0xRECEIVER" }
);
var resultsReceiverOnly = await transferEvent.GetAllChangesAsync(filterReceiverOnly);

// 3. Filter with OR logic (multiple receivers)
// Use array to match ANY of the specified addresses
var filterMultipleReceivers = transferEvent.CreateFilterInput(
    null,  // Any sender
    new[] { "0xRECEIVER1", "0xRECEIVER2", "0xRECEIVER3" }
);
var resultsMultiple = await transferEvent.GetAllChangesAsync(filterMultipleReceivers);

// 4. Incremental updates with GetFilterChangesAsync
// Create a filter that tracks changes since last check
var filterId = await transferEvent.CreateFilterAsync(filterReceiverOnly);

// First call returns no results (no changes since filter creation)
var changes1 = await transferEvent.GetFilterChangesAsync(filterId);
Console.WriteLine($"Changes: {changes1.Count}"); // 0

// After new transactions occur...
await Task.Delay(10000); // Wait for new blocks

// Second call returns only NEW events since last check
var changes2 = await transferEvent.GetFilterChangesAsync(filterId);
Console.WriteLine($"New events: {changes2.Count}");

// Third call returns only events since second check
var changes3 = await transferEvent.GetFilterChangesAsync(filterId);

Cross-Contract Event Filtering:

To monitor events across ALL contracts with the same signature (e.g., all ERC20 transfers):

// Create event handler WITHOUT contract address
var transferEventAnyContract = web3.Eth.GetEvent<TransferEventDTO>();

// Filter for transfers to specific address across ALL token contracts
var filterAllContracts = transferEventAnyContract.CreateFilterInput(
    null,  // Any sender
    new[] { "0xRECEIVER" }  // Specific receiver
);

var allTransfers = await transferEventAnyContract.GetAllChangesAsync(filterAllContracts);

foreach (var transfer in allTransfers)
{
    Console.WriteLine($"Token Contract: {transfer.Log.Address}");
    Console.WriteLine($"From: {transfer.Event.From}");
    Console.WriteLine($"To: {transfer.Event.To}");
    Console.WriteLine($"Value: {transfer.Event.Value}");
}

Key Concepts:

  • Indexed Parameters: Only indexed parameters (marked true in [Parameter]) can be used in filters
  • Null for Skip: Pass null to ignore a parameter position in the filter
  • Array for OR Logic: Pass array of values to match ANY of them (OR operation)
  • Filter Order: Filter parameter order must match event parameter order
  • GetFilterChangesAsync: Returns only NEW events since last check (efficient for polling)
  • GetAllChangesAsync: Returns ALL matching events in block range (can be expensive)
  • Cross-Contract: Omit contract address to filter events across all contracts

Example 4: Handling Custom Errors

using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.Contracts;
using Nethereum.Contracts.Exceptions;

// Custom error DTO
[Error("InsufficientBalance")]
public class InsufficientBalanceError : IErrorDTO
{
    [Parameter("uint256", "available", 1)]
    public BigInteger Available { get; set; }

    [Parameter("uint256", "required", 2)]
    public BigInteger Required { get; set; }
}

var account = new Account("0xPRIVATE_KEY");
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");

try
{
    // Attempt transfer with insufficient balance
    var contract = web3.Eth.GetContract("[...abi...]", "0x...");
    var transferFunction = contract.GetFunction("transfer");

    var receipt = await transferFunction.SendTransactionAndWaitForReceiptAsync(
        account.Address,
        new Nethereum.Hex.HexTypes.HexBigInteger(100000),
        null,
        "0xRECIPIENT",
        1000000000000000000000 // More than balance
    );
}
catch (SmartContractCustomErrorRevertException ex)
{
    // Decode custom error
    var error = ex.DecodeError<InsufficientBalanceError>();
    if (error != null)
    {
        Console.WriteLine($"Insufficient Balance!");
        Console.WriteLine($"Available: {error.Available}");
        Console.WriteLine($"Required: {error.Required}");
    }
}

Example 5: Contract Deployment with Verification

using Nethereum.Web3;
using Nethereum.Web3.Accounts;

var account = new Account("0xPRIVATE_KEY");
var web3 = new Web3(account, "http://localhost:8545");

// Contract source
var abi = @"[...]";
var bytecode = "0x...";

// Deploy
Console.WriteLine("Deploying contract...");
var receipt = await web3.Eth.DeployContract.SendRequestAndWaitForReceiptAsync(
    abi,
    bytecode,
    account.Address,
    new Nethereum.Hex.HexTypes.HexBigInteger(3000000),
    null,
    null,
    1000000 // constructor param
);

if (receipt.Status.Value != 1)
{
    throw new Exception("Contract deployment failed");
}

var contractAddress = receipt.ContractAddress;
Console.WriteLine($"Contract deployed: {contractAddress}");
Console.WriteLine($"Gas used: {receipt.GasUsed.Value}");

// Verify deployment by calling a function
var contract = web3.Eth.GetContract(abi, contractAddress);
var totalSupplyFunction = contract.GetFunction("totalSupply");
var totalSupply = await totalSupplyFunction.CallAsync<BigInteger>();

Console.WriteLine($"Verified total supply: {totalSupply}");

Example 6: ENS Name Resolution

From: Nethereum Playground Example 1055

using Nethereum.Web3;
using Nethereum.Contracts.Standards.ENS;

var web3 = new Web3("https://mainnet.infura.io/v3/YOUR-PROJECT-ID");

// Get ENS service
var ensService = web3.Eth.GetEnsService();

// Resolve ENS name to address
var address = await ensService.ResolveAddressAsync("vitalik.eth");
Console.WriteLine($"vitalik.eth resolves to: {address}");

// Reverse lookup
var ensName = await ensService.ReverseResolveAsync(address);
Console.WriteLine($"{address} reverse resolves to: {ensName}");

// Resolve text records
var url = await ensService.ResolveTextAsync("vitalik.eth", ENSTextRecordKey.Url);
var avatar = await ensService.ResolveTextAsync("vitalik.eth", ENSTextRecordKey.Avatar);

Example 7: Multicall - Batch Contract Calls

From: Nethereum Playground Example 1066

using Nethereum.Web3;
using Nethereum.Contracts.QueryHandlers.MultiCall;

var web3 = new Web3("https://mainnet.infura.io/v3/YOUR-PROJECT-ID");

// Multiple tokens to query
var tokens = new[]
{
    "0x6B175474E89094C44Da98b954EedeAC495271d0F", // DAI
    "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
    "0xdAC17F958D2ee523a2206206994597C13D831ec7"  // USDT
};

var holder = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e";

// Get multicall handler
var multiQueryHandler = web3.Eth.GetMultiQueryHandler();

// Create calls for each token
var calls = new List<IMulticallInputOutput>();
foreach (var tokenAddress in tokens)
{
    var erc20 = web3.Eth.ERC20.GetContractService(tokenAddress);
    var balanceQuery = new BalanceOfFunction { Owner = holder };

    calls.Add(new MulticallInputOutput<BalanceOfFunction, BigInteger>(
        balanceQuery,
        tokenAddress
    ));
}

// Execute all calls in single RPC request
var results = await multiQueryHandler.MultiCallAsync(calls.ToArray());

for (int i = 0; i < tokens.Length; i++)
{
    var balance = ((MulticallInputOutput<BalanceOfFunction, BigInteger>)results[i]).Output;
    Console.WriteLine($"Token {tokens[i]}: {Web3.Convert.FromWei(balance, 18)}");
}

Example 8: Estimating Gas for Contract Call

using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.RPC.Eth.DTOs;

var account = new Account("0xPRIVATE_KEY");
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");

var contract = web3.Eth.GetContract("[...abi...]", "0x...");
var transferFunction = contract.GetFunction("transfer");

// Estimate gas
var gasEstimate = await transferFunction.EstimateGasAsync(
    account.Address,
    null,
    null,
    "0xRECIPIENT",
    1000000
);

Console.WriteLine($"Estimated gas: {gasEstimate.Value}");

// Add 10% buffer
var gasLimit = gasEstimate.Value * 110 / 100;

// Send with custom gas limit
var receipt = await transferFunction.SendTransactionAndWaitForReceiptAsync(
    account.Address,
    new Nethereum.Hex.HexTypes.HexBigInteger(gasLimit),
    null,
    null,
    "0xRECIPIENT",
    1000000
);

Example 9: Query Handler Pattern (Simplified)

using Nethereum.Web3;
using Nethereum.Contracts;
using Nethereum.ABI.FunctionEncoding.Attributes;

// Query DTO
[Function("balanceOf", "uint256")]
public class BalanceOfFunction : FunctionMessage
{
    [Parameter("address", "owner", 1)]
    public string Owner { get; set; }
}

var web3 = new Web3("https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
var tokenAddress = "0x...";

// Query handler simplifies read operations
var queryHandler = web3.Eth.GetContractQueryHandler<BalanceOfFunction>();
var balance = await queryHandler.QueryAsync<BigInteger>(
    tokenAddress,
    new BalanceOfFunction { Owner = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" }
);

Console.WriteLine($"Balance: {balance}");

Example 10: AOT-Compatible Contract Usage

using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.JsonRpc.SystemTextJsonRpcClient;
using Nethereum.Contracts;

// CRITICAL: Enable System.Text.Json for AOT compatibility
Nethereum.ABI.ABIDeserialisation.AbiDeserializationSettings.UseSystemTextJson = true;

var account = new Account("0xPRIVATE_KEY", chainId: 1);
var client = new SimpleRpcClient("https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
var web3 = new Web3(account, client);

// Deploy contract (AOT-compatible)
var abi = "[...]";
var bytecode = "0x...";

var receipt = await web3.Eth.DeployContract.SendRequestAndWaitForReceiptAsync(
    abi,
    bytecode,
    account.Address,
    new Nethereum.Hex.HexTypes.HexBigInteger(3000000)
);

var contractAddress = receipt.ContractAddress;

// Get contract and call function (AOT-compatible)
var contract = web3.Eth.GetContract(abi, contractAddress);
var function = contract.GetFunction("totalSupply");
var supply = await function.CallAsync<BigInteger>();

Console.WriteLine($"Total supply: {supply}");

// Decode events (AOT-compatible)
var events = receipt.DecodeAllEvents<TransferEventDTO>();
foreach (var evt in events)
{
    Console.WriteLine($"Transfer: {evt.Event.From} → {evt.Event.To}");
}

Contract Standards

ERC20 Token Standard

From: Nethereum Playground Example 1005

var erc20 = web3.Eth.ERC20.GetContractService(tokenAddress);

// Read operations
var name = await erc20.NameQueryAsync();
var symbol = await erc20.SymbolQueryAsync();
var decimals = await erc20.DecimalsQueryAsync();
var totalSupply = await erc20.TotalSupplyQueryAsync();
var balance = await erc20.BalanceOfQueryAsync(holderAddress);
var allowance = await erc20.AllowanceQueryAsync(owner, spender);

// Write operations (requires account)
var approveReceipt = await erc20.ApproveRequestAndWaitForReceiptAsync(spender, amount);
var transferReceipt = await erc20.TransferRequestAndWaitForReceiptAsync(to, amount);
var transferFromReceipt = await erc20.TransferFromRequestAndWaitForReceiptAsync(from, to, amount);

ERC721 NFT Standard

From: Nethereum Playground Example 1067

var erc721 = web3.Eth.ERC721.GetContractService(nftAddress);

// Query operations
var name = await erc721.NameQueryAsync();
var symbol = await erc721.SymbolQueryAsync();
var owner = await erc721.OwnerOfQueryAsync(tokenId);
var balance = await erc721.BalanceOfQueryAsync(address);
var tokenURI = await erc721.TokenURIQueryAsync(tokenId);
var approved = await erc721.GetApprovedQueryAsync(tokenId);

// Transfer operations
var transferReceipt = await erc721.TransferFromRequestAndWaitForReceiptAsync(from, to, tokenId);
var approveReceipt = await erc721.ApproveRequestAndWaitForReceiptAsync(to, tokenId);
var setApprovalForAllReceipt = await erc721.SetApprovalForAllRequestAndWaitForReceiptAsync(operator, approved);

ERC1155 Multi-Token Standard

var erc1155 = web3.Eth.ERC1155.GetContractService(contractAddress);

// Query single balance
var balance = await erc1155.BalanceOfQueryAsync(owner, tokenId);

// Query multiple balances
var balances = await erc1155.BalanceOfBatchQueryAsync(
    new[] { owner1, owner2 },
    new[] { tokenId1, tokenId2 }
);

// Transfer single token
var transferReceipt = await erc1155.SafeTransferFromRequestAndWaitForReceiptAsync(
    from,
    to,
    tokenId,
    amount,
    data
);

// Transfer multiple tokens
var batchTransferReceipt = await erc1155.SafeBatchTransferFromRequestAndWaitForReceiptAsync(
    from,
    to,
    new[] { tokenId1, tokenId2 },
    new[] { amount1, amount2 },
    data
);

EIP-3009: Transfer With Authorization (USDC)

using Nethereum.Contracts.Standards.EIP3009;

var eip3009Service = web3.Eth.EIP3009.GetContractService(usdcAddress);

// Create authorization for gasless transfer
var authorization = await eip3009Service.CreateTransferWithAuthorizationAsync(
    from: fromAddress,
    to: toAddress,
    value: amount,
    validAfter: 0,
    validBefore: uint.MaxValue,
    nonce: nonceBytes
);

// Sign the authorization
var signature = await account.SignTypedDataV4Async(authorization);

// Execute the transfer (can be done by anyone, not just the sender)
var receipt = await eip3009Service.TransferWithAuthorizationRequestAndWaitForReceiptAsync(
    from: authorization.From,
    to: authorization.To,
    value: authorization.Value,
    validAfter: authorization.ValidAfter,
    validBefore: authorization.ValidBefore,
    nonce: authorization.Nonce,
    v: signature.V,
    r: signature.R,
    s: signature.S
);

Best Practices

  1. Use Strongly-Typed DTOs: Better IntelliSense, compile-time safety

    // Good
    var function = contract.GetFunction<TransferFunction>();
    
    // Less maintainable
    var function = contract.GetFunction("transfer");
    
  2. Use Code Generation for Production: Automate DTO creation

    Nethereum.Generator.Console generate from-abi -abi MyContract.abi.json
    
  3. Estimate Gas Before Sending:

    var gasEstimate = await function.EstimateGasAsync(...);
    var gasLimit = gasEstimate.Value * 110 / 100; // 10% buffer
    
  4. Always Check Transaction Status:

    if (receipt.Status.Value != 1)
    {
        throw new Exception("Transaction failed");
    }
    
  5. Decode Events from Receipts:

    var events = receipt.DecodeAllEvents<TransferEventDTO>();
    
  6. Use ERC Standard Services: Don't reimplement standards

    var erc20 = web3.Eth.ERC20.GetContractService(tokenAddress);
    
  7. Handle Custom Errors Properly:

    catch (SmartContractCustomErrorRevertException ex)
    {
        var error = ex.DecodeError<YourErrorDTO>();
    }
    
  8. Use Multicall for Batch Queries: Reduce RPC calls

    var multiQueryHandler = web3.Eth.GetMultiQueryHandler();
    
  9. Enable AOT Compatibility When Needed:

    // CRITICAL: Enable System.Text.Json for AOT
    AbiDeserializationSettings.UseSystemTextJson = true;
    
  10. Index Event Parameters Correctly:

    [Parameter("address", "from", 1, true)] // indexed=true
    [Parameter("uint256", "value", 3, false)] // indexed=false
    

Error Handling

using Nethereum.Contracts.Exceptions;
using Nethereum.JsonRpc.Client;

try
{
    var receipt = await function.SendTransactionAndWaitForReceiptAsync(...);

    if (receipt.Status.Value != 1)
    {
        Console.WriteLine("Transaction reverted");
    }
}
catch (SmartContractCustomErrorRevertException ex)
{
    Console.WriteLine($"Custom error: {ex.Message}");
    var decoded = ex.DecodeError<YourErrorDTO>();
    // Handle specific custom error
}
catch (SmartContractRevertException ex)
{
    Console.WriteLine($"Revert reason: {ex.RevertMessage}");
}
catch (RpcResponseException ex)
{
    Console.WriteLine($"RPC error: {ex.Message}");
}

API Reference

Core Classes

  • Contract - Represents a deployed contract

    • GetFunction(name) / GetFunction<T>() - Get function by name or type
    • GetEvent(name) / GetEvent<T>() - Get event by name or type
    • GetError(name) / FindError(encodedData) - Get custom error
  • Function / Function<T> - Represents a contract function

    • CallAsync<TReturn>(params) - Call function (read-only)
    • SendTransactionAsync(from, gas, value, params) - Send transaction
    • SendTransactionAndWaitForReceiptAsync(...) - Send and wait for receipt
    • EstimateGasAsync(from, gas, value, params) - Estimate gas cost
  • Event / Event<T> - Represents a contract event

    • CreateFilterInput(...) - Create event filter
    • GetAllChangesAsync(filterInput) - Get historical events
    • CreateFilterAsync() - Create persistent filter on node
    • GetFilterChangesAsync(filterId) - Get new events since last check
  • ContractHandler - Unified interface for contract operations

    • QueryAsync<TFunction, TReturn>(function) - Query contract state
    • SendRequestAndWaitForReceiptAsync<TFunction>(function) - Send transaction
    • EstimateGasAsync<TFunction>(function) - Estimate gas

Handler Classes

  • IContractQueryHandler<TFunction> - Simplified query operations
  • IContractTransactionHandler<TFunction> - Simplified transaction operations
  • IContractDeploymentHandler<TDeployment> - Simplified deployment operations

Standard Services

  • ERC20Service - Complete ERC20 token interaction
  • ERC721Service - Complete ERC721 NFT interaction
  • ERC1155Service - Complete ERC1155 multi-token interaction
  • ERC1271Service - Contract signature validation
  • ERC165SupportsInterfaceService - Interface detection
  • ERC2535DiamondService - Diamond proxy standard
  • ERC6492Service - Pre-deployed contract signature validation
  • EIP3009Service - Transfer with authorization (USDC/stablecoins)
  • ENSService - Ethereum Name Service resolution
  • ProofOfHumanityService - Proof of Humanity registry

Playground Examples

Live, runnable examples:

Smart Contracts:

Events:

ERC721 NFTs:

ERC20 Multicall:

  • Example 1066 - Query multiple ERC20 balances using multicall

ENS:

Advanced:

  • Nethereum.ABI - ABI encoding/decoding (used by this package)
  • Nethereum.Web3 - High-level API that includes contracts
  • Nethereum.Accounts - Account management for signing transactions
  • Nethereum.RPC - Low-level RPC methods
  • Nethereum.Generator.Console - Code generation tool

Comprehensive Guides

For in-depth documentation:

Important Notes

Event Parameter Indexing

Indexed parameters (up to 3) go into event topics for filtering:

[Parameter("address", "from", 1, true)] // indexed - can filter
[Parameter("uint256", "value", 3, false)] // not indexed - cannot filter

Gas Estimation Buffer

Always add a buffer to gas estimates (network conditions vary):

var gasLimit = gasEstimate.Value * 110 / 100; // 10% buffer recommended

Transaction Receipt Status

Status field indicates success/failure:

  • Status = 1 or 0x1 - Transaction succeeded
  • Status = 0 or 0x0 - Transaction failed (reverted)

ABI Compatibility

Nethereum supports:

  • Solidity ABI JSON format
  • Human-readable ABI format
  • Minimal ABI (function signatures only)

Code Generation Benefits

For production applications, code generation is strongly recommended:

  • Eliminates manual DTO creation
  • Provides compile-time type safety
  • Reduces errors from manual ABI transcription
  • Simplifies contract updates
  • Generates complete service wrappers
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net451 is compatible.  net452 was computed.  net46 was computed.  net461 is compatible.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (40)

Showing the top 5 NuGet packages that depend on Nethereum.Contracts:

Package Downloads
Nethereum.Web3

Nethereum.Web3 Ethereum Web3 Class Library to interact via RPC with an Ethereum client, for example geth. Including contract interaction, deployment, transaction, encoding / decoding and event filters

Nethereum.BlockchainProcessing

Nethereum.BlockchainProcessing Ethereum blockchain processing allowing to crawl Blocks, Transactions, TransactionReceipts and Logs (Event) for storage and / or using custom handlers like queuing , search, etc

Nethereum

Package Description

Nethereum.Web3Lite

Nethereum.Web3Lite Ethereum Web3 Class Library (light browser version, with no reference to signing crypto libraries) to interact via RPC with an Ethereum client, for example geth. Including contract interaction, deployment, transaction, encoding / decoding and event filters

Aoite.Neth

The aoite ethereum module.

GitHub repositories (5)

Showing the top 5 popular GitHub repositories that depend on Nethereum.Contracts:

Repository Stars
ChainSafe/web3.unity
🕹 Unity SDK for building games that interact with blockchains.
Texnomic/SecureDNS
Secure, Modern, Fully-Featured, All-In-One Cross-Architecture & Cross-Platform DNS Server Using .NET 10
yc-l/yc.boilerplate
YC. Boilerplate is a set of loose coupling, flexible combination, complete functions, convenient development, and reduces the workload of development.
unoplatform/Uno.Samples
A collection of code samples for the Uno Platform
biheBlockChain/MyLinkToken
开源链克口袋,玩客币钱包
Version Downloads Last Updated
5.8.0 673 1/6/2026
5.0.0 296,149 5/28/2025
4.29.0 220,165 2/10/2025
4.28.0 72,393 1/7/2025
4.27.1 12,504 12/24/2024
4.27.0 1,685 12/24/2024
4.26.0 101,924 10/1/2024
4.25.0 22,816 9/19/2024
4.21.4 96,794 8/9/2024
4.21.3 11,596 7/22/2024
4.21.2 68,338 6/26/2024
4.21.1 2,698 6/26/2024
4.21.0 10,527 6/18/2024
4.20.0 314,847 3/28/2024
4.19.0 83,569 2/16/2024
4.18.0 281,557 11/21/2023
4.17.1 78,116 9/28/2023
4.17.0 16,998 9/27/2023
4.16.0 120,713 8/14/2023
4.15.2 128,441 7/11/2023
4.15.1 3,973 7/11/2023
4.15.0 4,444 7/11/2023
4.14.0 183,551 3/19/2023
4.13.0 131,084 2/18/2023
4.12.0 268,863 12/9/2022
4.11.0 173,731 10/27/2022
4.9.0 113,138 9/27/2022
4.8.0 179,322 8/24/2022
4.7.0 169,319 7/20/2022
4.6.1 129,410 6/18/2022
4.6.0 9,171 6/16/2022
4.5.0 389,217 5/13/2022
4.4.1 104,832 4/27/2022
4.4.0 12,849 4/27/2022
4.3.0 69,925 4/12/2022
4.2.0 171,123 2/18/2022
4.1.1 486,517 11/4/2021
4.1.0 29,111 10/15/2021
4.0.5 138,546 8/12/2021
4.0.4 7,414 8/10/2021
4.0.3 7,461 8/8/2021
4.0.2 6,461 8/5/2021
4.0.1 13,057 7/28/2021
4.0.0 17,547 7/26/2021
3.8.0 393,666 7/3/2020
3.7.1 115,685 2/13/2020
3.7.0 9,173 2/13/2020
3.6.0 32,437 1/27/2020
3.5.0 19,968 12/31/2019
3.4.0 148,894 7/29/2019
3.3.0 73,754 4/23/2019
3.2.0 43,483 4/8/2019
3.1.2 22,345 3/13/2019
3.1.1 4,987 3/12/2019
3.1.0 25,857 3/12/2019
3.0.0 36,658 11/28/2018
3.0.0-rc3 5,337 10/25/2018
3.0.0-rc2 3,050 10/24/2018
3.0.0-rc1 8,702 7/25/2018
2.5.1 104,615 6/5/2018
2.5.0 5,571 6/4/2018
2.4.0 52,052 3/11/2018
2.3.1 7,272 3/7/2018
2.3.0 5,640 3/6/2018
2.2.3 18,363 12/16/2017
2.2.2 5,604 12/16/2017
2.2.0 5,943 12/8/2017
2.1.0 11,077 10/23/2017
2.0.1 5,640 10/4/2017
2.0.0 6,373 9/26/2017
2.0.0-rc7 3,826 8/17/2017
2.0.0-rc6-2 3,375 7/29/2017
2.0.0-rc6.1 1,019 7/26/2017
2.0.0-rc5 2,364 6/19/2017
2.0.0-rc4 3,629 6/6/2017
2.0.0-rc3 3,061 4/11/2017
2.0.0-rc2-fix 2,616 4/6/2017
2.0.0-rc2 1,246 4/5/2017
2.0.0-rc1 3,120 2/8/2017