Smart Contract Interaction
var service = new StandardTokenService(web3, contractAddress);
var balance = await service.BalanceOfQueryAsync(ownerAddress);
var receipt = await service.TransferRequestAndWaitForReceiptAsync(
new TransferFunction { To = recipient, Value = amount });
Define typed DTOs, then query and transact — gas, nonce, and fees are all automatic.
Nethereum uses typed C# classes to represent every part of a smart contract: deployment bytecode, function calls, events, and return values. This page covers the full interaction lifecycle using a standard ERC20 token as the example.
dotnet add package Nethereum.Web3
Define Contract DTOs
Every smart contract interaction starts with defining C# classes that map to the Solidity contract's constructor, functions, events, and return types. These can be hand-written or code-generated.
Deployment Message
A class inheriting from ContractDeploymentMessage represents the contract constructor. Include the compiled bytecode and any constructor parameters:
using Nethereum.Contracts;
using Nethereum.ABI.FunctionEncoding.Attributes;
using System.Numerics;
public class StandardTokenDeployment : ContractDeploymentMessage
{
public static string BYTECODE = "0x60606040..."; // compiled bytecode
public StandardTokenDeployment() : base(BYTECODE) { }
[Parameter("uint256", "totalSupply")]
public BigInteger TotalSupply { get; set; }
}
Function Messages (Query and Transaction)
Each function is a class inheriting from FunctionMessage, decorated with [Function]:
[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 TokenAmount { get; set; }
}
Event DTOs
Events implement IEventDTO and are decorated with [Event]. Indexed parameters allow filtered queries:
[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; }
}
Function Output DTOs
For functions that return complex values, define an output class implementing IFunctionOutputDTO:
[FunctionOutput]
public class BalanceOfOutputDTO : IFunctionOutputDTO
{
[Parameter("uint256", "balance", 1)]
public BigInteger Balance { get; set; }
}
Deploy a Smart Contract
Create a deployment handler and send the deployment transaction:
var web3 = new Web3(account, "http://localhost:8545");
var deploymentMessage = new StandardTokenDeployment
{
TotalSupply = 100000
};
var deploymentHandler = web3.Eth.GetContractDeploymentHandler<StandardTokenDeployment>();
var receipt = await deploymentHandler.SendRequestAndWaitForReceiptAsync(deploymentMessage);
var contractAddress = receipt.ContractAddress;
Gas, gas price, and nonce are estimated automatically. To control them, set properties on the deployment message before sending.
Query a Smart Contract
Use GetContractQueryHandler<T> to call view/pure functions without sending a transaction:
var balanceOfMessage = new BalanceOfFunction { Owner = account.Address };
var balanceHandler = web3.Eth.GetContractQueryHandler<BalanceOfFunction>();
// Single return value
var balance = await balanceHandler.QueryAsync<BigInteger>(contractAddress, balanceOfMessage);
// Deserialize to output DTO (for multiple return values)
var output = await balanceHandler.QueryDeserializingToObjectAsync<BalanceOfOutputDTO>(
balanceOfMessage, contractAddress);
Send a Transaction
Use GetContractTransactionHandler<T> to call state-changing functions:
var transferHandler = web3.Eth.GetContractTransactionHandler<TransferFunction>();
var transfer = new TransferFunction
{
To = receiverAddress,
TokenAmount = 100
};
var receipt = await transferHandler.SendRequestAndWaitForReceiptAsync(contractAddress, transfer);
Decode Events from a Receipt
After a transaction, decode events directly from the receipt:
var transferEvents = receipt.DecodeAllEvents<TransferEventDTO>();
var firstTransfer = transferEvents[0];
var from = firstTransfer.Event.From;
var to = firstTransfer.Event.To;
var value = firstTransfer.Event.Value;
For advanced event filtering and querying across block ranges, see Events & Logs.
Query Historical State
All query methods accept an optional BlockParameter to read contract state at a specific block:
var historicalBalance = await balanceHandler.QueryDeserializingToObjectAsync<BalanceOfOutputDTO>(
balanceOfMessage, contractAddress,
new Nethereum.RPC.Eth.DTOs.BlockParameter(deployReceipt.BlockNumber));
Gas Estimation and Gas Price
Nethereum auto-estimates gas and retrieves the current gas price. For manual control:
var transferHandler = web3.Eth.GetContractTransactionHandler<TransferFunction>();
var transfer = new TransferFunction { To = receiverAddress, TokenAmount = 100 };
// Estimate gas
var estimate = await transferHandler.EstimateGasAsync(contractAddress, transfer);
transfer.Gas = estimate.Value;
// Set gas price (in Wei)
transfer.GasPrice = Web3.Convert.ToWei(25, UnitConversion.EthUnit.Gwei);
var receipt = await transferHandler.SendRequestAndWaitForReceiptAsync(contractAddress, transfer);
Send Ether with a Function Call
If a function is payable, set AmountToSend:
transfer.AmountToSend = Web3.Convert.ToWei(1); // 1 ETH
Nonce Control
Nethereum manages nonces automatically (including an in-memory counter for rapid submissions). To set manually:
transfer.Nonce = 42;
Offline Signing
Sign a transaction without broadcasting it, useful for cold wallets or deferred submission:
var transfer = new TransferFunction
{
To = receiverAddress,
TokenAmount = 100,
Nonce = 2,
Gas = 60000,
GasPrice = Web3.Convert.ToWei(25, UnitConversion.EthUnit.Gwei)
};
var signedTransaction = await transferHandler.SignTransactionAsync(contractAddress, transfer);
Set Nonce, Gas, and GasPrice before signing for fully offline operation (no node required).
Non-Type-Safe Interaction (ABI JSON)
When you have a raw ABI string and don't want typed classes, use GetContract and GetFunction:
var abi = @"[{""constant"":true,""inputs"":[{""name"":""_owner"",""type"":""address""}],
""name"":""balanceOf"",""outputs"":[{""name"":""balance"",""type"":""uint256""}],
""type"":""function""}, ...]";
var receipt = await web3.Eth.DeployContract.SendRequestAndWaitForReceiptAsync(
abi, bytecode, senderAddress, new HexBigInteger(900000), null, totalSupply);
var contract = web3.Eth.GetContract(abi, receipt.ContractAddress);
var balanceFunction = contract.GetFunction("balanceOf");
var transferFunction = contract.GetFunction("transfer");
// Query
var balance = await balanceFunction.CallAsync<BigInteger>(ownerAddress);
// Estimate gas and send transaction
var gas = await transferFunction.EstimateGasAsync(senderAddress, null, null, toAddress, amount);
var txReceipt = await transferFunction.SendTransactionAndWaitForReceiptAsync(
senderAddress, gas, null, null, toAddress, amount);
The typed approach is recommended — it catches parameter mismatches at compile time and enables code generation.
Next Steps
- Deploy a Contract -- deploy contracts using the typed deployment handlers shown above
- ERC-20 Tokens -- interact with any ERC-20 token using built-in typed services
- Code Generation -- generate typed DTOs and service classes from ABI
- Events & Logs -- filter, subscribe, and decode contract events