Nethereum.Accounts
5.8.0
Prefix Reserved
dotnet add package Nethereum.Accounts --version 5.8.0
NuGet\Install-Package Nethereum.Accounts -Version 5.8.0
<PackageReference Include="Nethereum.Accounts" Version="5.8.0" />
<PackageVersion Include="Nethereum.Accounts" Version="5.8.0" />
<PackageReference Include="Nethereum.Accounts" />
paket add Nethereum.Accounts --version 5.8.0
#r "nuget: Nethereum.Accounts, 5.8.0"
#:package Nethereum.Accounts@5.8.0
#addin nuget:?package=Nethereum.Accounts&version=5.8.0
#tool nuget:?package=Nethereum.Accounts&version=5.8.0
Nethereum.Accounts
Nethereum.Accounts provides account management and transaction signing capabilities for Ethereum. It enables offline transaction signing, key management, and integration with external signers (hardware wallets, browser wallets, etc.).
Features
- Account - Full control with private key (offline signing)
- ManagedAccount - Node-managed accounts (personal API)
- ExternalAccount - Hardware wallets, browser extensions, custom signers
- ViewOnlyAccount - Read-only account access (no signing)
- Transaction Managers - Automatic nonce management and transaction lifecycle
- Message Signing - EIP-191 personal_sign and EIP-712 typed data
- KeyStore Support - Load accounts from encrypted JSON keystore files
- Chain ID Support - EIP-155 replay protection
- Nonce Management - In-memory and external nonce services
Installation
dotnet add package Nethereum.Accounts
Dependencies
- Nethereum.RPC - RPC functionality and transaction managers
- Nethereum.Signer - Transaction and message signing (ECDSA)
- Nethereum.Signer.EIP712 - Typed data signing
- Nethereum.KeyStore - Keystore encryption/decryption (Scrypt/PBKDF2)
- Nethereum.Util - Utilities and unit conversion
Quick Start
Creating an Account
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
// From private key (hex string)
var account = new Account("0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7");
// From private key with chain ID (EIP-155)
var account = new Account("0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7", chainId: 1);
// Use with Web3
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
// Now all transactions will be signed with this account
var balance = await web3.Eth.GetBalance.SendRequestAsync(account.Address);
Console.WriteLine($"Balance: {Web3.Convert.FromWei(balance)} ETH");
Account Types
Account (Offline Signing)
Full control account with private key. Signs transactions locally without exposing the private key to the node.
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
var account = new Account("0xYOUR_PRIVATE_KEY", chainId: 1); // Mainnet
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
// Send Ether
var receipt = await web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync("0xRECIPIENT_ADDRESS", 0.1m); // 0.1 ETH
Console.WriteLine($"Transaction hash: {receipt.TransactionHash}");
Console.WriteLine($"Status: {(receipt.Status.Value == 1 ? "Success" : "Failed")}");
Use Case: Production applications, automated services, scripts
ManagedAccount (Node-Managed)
Account managed by the Ethereum node. Requires node support for personal API methods.
using Nethereum.Web3;
using Nethereum.Web3.Accounts.Managed;
var account = new ManagedAccount("0xYOUR_ADDRESS", "password");
var web3 = new Web3(account, "http://localhost:8545");
// Node unlocks account and signs transaction
var receipt = await web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync("0xRECIPIENT_ADDRESS", 0.1m);
Use Case: Local development nodes (Ganache, Hardhat) that manage accounts
ExternalAccount (Hardware Wallets, Browser Wallets)
Integrates with external signers like Ledger, Trezor, MetaMask, WalletConnect.
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.Signer;
// Custom external signer implementation
public class MyHardwareWalletSigner : IEthExternalSigner
{
public Task<string> GetAddressAsync() => Task.FromResult("0x...");
public Task<string> SignAsync(byte[] message) =>
Task.FromResult(/* Call hardware wallet API */);
public Task<string> SignAsync(byte[] rawHash, EthECKey.SigningVersion signingVersion) =>
Task.FromResult(/* Call hardware wallet API */);
}
var externalSigner = new MyHardwareWalletSigner();
var account = new ExternalAccount(externalSigner, chainId: 1);
await account.InitialiseAsync();
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
Use Case: Integrating with hardware wallets, browser wallets, HSMs
ViewOnlyAccount (Read-Only)
No signing capabilities, useful for monitoring addresses.
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
var account = new ViewOnlyAccount("0xADDRESS_TO_MONITOR");
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
// Can read data but cannot send transactions
var balance = await web3.Eth.GetBalance.SendRequestAsync(account.Address);
Use Case: Monitoring wallets, read-only dashboards
Usage Examples
Example 1: Complete Transfer with Account
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.Hex.HexTypes;
var privateKey = "0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7";
var account = new Account(privateKey, chainId: 1); // Mainnet
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
Console.WriteLine($"From address: {account.Address}");
// Check balance before transfer
var balanceBefore = await web3.Eth.GetBalance.SendRequestAsync(account.Address);
Console.WriteLine($"Balance before: {Web3.Convert.FromWei(balanceBefore)} ETH");
// Send 0.1 ETH
var toAddress = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e";
var amountInEther = 0.1m;
var receipt = await web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync(toAddress, amountInEther);
Console.WriteLine($"Transaction hash: {receipt.TransactionHash}");
Console.WriteLine($"Block number: {receipt.BlockNumber.Value}");
Console.WriteLine($"Gas used: {receipt.GasUsed.Value}");
Console.WriteLine($"Status: {(receipt.Status.Value == 1 ? "Success" : "Failed")}");
// Check balance after transfer
var balanceAfter = await web3.Eth.GetBalance.SendRequestAsync(account.Address);
Console.WriteLine($"Balance after: {Web3.Convert.FromWei(balanceAfter)} ETH");
Based on Nethereum integration tests
Example 2: Creating Account from KeyStore File
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
// Load from keystore file (UTC JSON format)
var account = Account.LoadFromKeyStoreFile(
"/path/to/UTC--2016-11-23T09-58-36Z--12890d2cce102216644c59dae5baed380d84830c",
"password",
chainId: 1
);
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
Console.WriteLine($"Loaded account: {account.Address}");
// Or from JSON string
string keystoreJson = File.ReadAllText("keystore.json");
var account2 = Account.LoadFromKeyStore(keystoreJson, "password", chainId: 1);
The Account class provides static methods:
LoadFromKeyStoreFile(filePath, password, chainId)- Load from file pathLoadFromKeyStore(json, password, chainId)- Load from JSON string
Based on Nethereum.Accounts source code
Example 3: Signing Offline Transactions
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.Hex.HexTypes;
using Nethereum.Signer;
var account = new Account("0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7", chainId: 1);
// Create transaction input
var transactionInput = new TransactionInput
{
From = account.Address,
To = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
Value = new HexBigInteger(Web3.Convert.ToWei(0.1m)),
Nonce = new HexBigInteger(10), // Must be correct nonce
GasPrice = new HexBigInteger(Web3.Convert.ToWei(50, UnitConversion.EthUnit.Gwei)),
Gas = new HexBigInteger(21000)
};
// Sign transaction offline
var offlineSigner = new AccountOfflineTransactionSigner();
var signedTransaction = await offlineSigner.SignTransactionAsync(transactionInput, account);
Console.WriteLine($"Signed transaction: {signedTransaction}");
// Broadcast via any node (node doesn't see private key)
var 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}");
Based on Nethereum integration tests
Example 4: Personal Sign (EIP-191)
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.Hex.HexConvertors.Extensions;
var account = new Account("0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7");
// Sign a message
string message = "Hello, Ethereum!";
var signature = await account.AccountSigningService.PersonalSign.SendRequestAsync(message.ToHexUTF8());
Console.WriteLine($"Message: {message}");
Console.WriteLine($"Signature: {signature}");
// Verify signature
var signer = new Nethereum.Signer.EthereumMessageSigner();
var recoveredAddress = signer.EncodeUTF8AndEcRecover(message, signature);
Console.WriteLine($"Recovered address: {recoveredAddress}");
Console.WriteLine($"Verified: {recoveredAddress.Equals(account.Address, StringComparison.OrdinalIgnoreCase)}");
Based on Nethereum integration tests
Example 5: EIP-712 Typed Data Signing
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.ABI.FunctionEncoding.Attributes;
using Nethereum.Signer.EIP712;
// Define EIP-712 typed data
[Struct("Person")]
public class Person
{
[Parameter("string", "name")]
public string Name { get; set; }
[Parameter("address", "wallet")]
public string Wallet { get; set; }
}
var account = new Account("0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7", chainId: 1);
var domain = new TypedData<Domain>
{
Domain = new Domain
{
Name = "My Dapp",
Version = "1",
ChainId = 1,
VerifyingContract = "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
PrimaryType = "Person",
Types = MemberDescriptionFactory.GetTypesMemberDescription(typeof(Domain), typeof(Person)),
Message = new Person
{
Name = "Alice",
Wallet = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
}
};
// Sign typed data
var signature = await account.AccountSigningService.SignTypedDataV4.SendRequestAsync(domain);
Console.WriteLine($"EIP-712 Signature: {signature}");
Based on Nethereum integration tests
Example 6: Multiple Accounts with Web3
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
var rpcUrl = "https://mainnet.infura.io/v3/YOUR-PROJECT-ID";
// Account 1
var account1 = new Account("0x" + new string('1', 64), chainId: 1);
var web3Account1 = new Web3(account1, rpcUrl);
// Account 2
var account2 = new Account("0x" + new string('2', 64), chainId: 1);
var web3Account2 = new Web3(account2, rpcUrl);
// Send from account1 to account2
var receipt = await web3Account1.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync(account2.Address, 0.5m);
Console.WriteLine($"Transfer from {account1.Address} to {account2.Address}");
Console.WriteLine($"Transaction: {receipt.TransactionHash}");
Based on Nethereum integration tests
Example 7: Custom Nonce Management
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.RPC.NonceServices;
using Nethereum.RPC.Eth.DTOs;
var privateKey = "0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7";
var account = new Account(privateKey, chainId: 1);
// Custom nonce service
var client = new Nethereum.JsonRpc.Client.RpcClient(new Uri("https://mainnet.infura.io/v3/YOUR-PROJECT-ID"));
var nonceService = new InMemoryNonceService(account.Address, client);
// Assign to account
account.NonceService = nonceService;
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
// Send multiple transactions in parallel (nonce service handles sequencing)
var task1 = web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync("0xAddress1", 0.1m);
var task2 = web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync("0xAddress2", 0.1m);
var task3 = web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync("0xAddress3", 0.1m);
await Task.WhenAll(task1, task2, task3);
Console.WriteLine("All transfers complete!");
Based on Nethereum integration tests
Example 8: Chain-Specific Accounts
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.Signer;
var privateKey = "0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7";
// Mainnet account (Chain ID = 1)
var mainnetAccount = new Account(privateKey, Chain.MainNet);
var web3Mainnet = new Web3(mainnetAccount, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
// Sepolia testnet account (Chain ID = 11155111)
var sepoliaAccount = new Account(privateKey, Chain.Sepolia);
var web3Sepolia = new Web3(sepoliaAccount, "https://sepolia.infura.io/v3/YOUR-PROJECT-ID");
// Polygon account (Chain ID = 137)
var polygonAccount = new Account(privateKey, Chain.Polygon);
var web3Polygon = new Web3(polygonAccount, "https://polygon-rpc.com");
Console.WriteLine($"Mainnet: {mainnetAccount.Address} (Chain {mainnetAccount.ChainId})");
Console.WriteLine($"Sepolia: {sepoliaAccount.Address} (Chain {sepoliaAccount.ChainId})");
Console.WriteLine($"Polygon: {polygonAccount.Address} (Chain {polygonAccount.ChainId})");
The Account constructor supports:
Account(privateKey, chainId)- Using numeric chain IDAccount(privateKey, Chain.MainNet)- UsingChainenum
Based on Nethereum.Accounts source code
Example 9: Production Transaction Workflow
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.Hex.HexTypes;
var account = new Account(Environment.GetEnvironmentVariable("PRIVATE_KEY"), chainId: 1);
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
try
{
// 1. Check balance
var balance = await web3.Eth.GetBalance.SendRequestAsync(account.Address);
if (balance.Value < Web3.Convert.ToWei(0.2m))
{
throw new Exception($"Insufficient balance: {Web3.Convert.FromWei(balance)} ETH");
}
// 2. Estimate gas
var toAddress = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e";
var value = Web3.Convert.ToWei(0.1m);
var gasEstimate = await web3.Eth.TransactionManager.EstimateGasAsync(
new TransactionInput
{
From = account.Address,
To = toAddress,
Value = new HexBigInteger(value)
}
);
Console.WriteLine($"Estimated gas: {gasEstimate.Value}");
// 3. Get current gas price
var gasPrice = await web3.Eth.GasPrice.SendRequestAsync();
Console.WriteLine($"Gas price: {Web3.Convert.FromWei(gasPrice, UnitConversion.EthUnit.Gwei)} gwei");
// 4. Send transaction
var receipt = await web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync(toAddress, 0.1m);
// 5. Verify success
if (receipt.Status.Value != 1)
{
throw new Exception($"Transaction failed: {receipt.TransactionHash}");
}
Console.WriteLine($"Transfer successful!");
Console.WriteLine($" Transaction: {receipt.TransactionHash}");
Console.WriteLine($" Block: {receipt.BlockNumber.Value}");
Console.WriteLine($" Gas used: {receipt.GasUsed.Value}");
// Calculate cost
var gasCost = receipt.GasUsed.Value * gasPrice.Value;
Console.WriteLine($" Gas cost: {Web3.Convert.FromWei(gasCost)} ETH");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
Based on Nethereum integration tests
Example 10: AOT-Compatible Account Usage
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.JsonRpc.SystemTextJsonRpcClient;
// Enable System.Text.Json for AOT compatibility
Nethereum.ABI.ABIDeserialisation.AbiDeserializationSettings.UseSystemTextJson = true;
// Create account
var privateKey = "0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7";
var account = new Account(privateKey, chainId: 1);
// Use SimpleRpcClient for AOT
var client = new SimpleRpcClient("https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
var web3 = new Web3(account, client);
// All operations are AOT-compatible
var balance = await web3.Eth.GetBalance.SendRequestAsync(account.Address);
Console.WriteLine($"Balance: {Web3.Convert.FromWei(balance)} ETH");
// Send transaction
var receipt = await web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync("0xRECIPIENT", 0.01m);
Console.WriteLine($"Transaction: {receipt.TransactionHash}");
Based on Nethereum AOT integration tests
Transaction Managers
Each account type has an associated transaction manager that handles nonce management and transaction lifecycle:
AccountSignerTransactionManager
Used by Account for offline transaction signing:
var account = new Account(privateKey, chainId: 1);
// Transaction manager is automatically created
var txManager = account.TransactionManager;
// Manual transaction with custom parameters
var receipt = await txManager.SendTransactionAndWaitForReceiptAsync(
new TransactionInput
{
From = account.Address,
To = "0xTO_ADDRESS",
Value = new HexBigInteger(Web3.Convert.ToWei(0.1m)),
Gas = new HexBigInteger(21000),
GasPrice = new HexBigInteger(Web3.Convert.ToWei(50, UnitConversion.EthUnit.Gwei))
}
);
Key Methods:
SendTransactionAsync(TransactionInput)- Sign and send transactionSignTransactionAsync(TransactionInput)- Sign transaction without sendingGetNonceAsync(TransactionInput)- Get next nonce for accountSignAuthorisationAsync(Authorisation)- Sign EIP-7702 authorization
ManagedAccountTransactionManager
Used by ManagedAccount for node-managed signing. The node must support personal_ methods.
ExternalAccountSignerTransactionManager
Used by ExternalAccount for external signer integration (hardware wallets, browser wallets).
ViewOnlyAccountTransactionManager
Used by ViewOnlyAccount. Throws exception if signing is attempted.
Nonce Management
Nonce management is critical for sending multiple transactions from the same account. Each Ethereum transaction must have a unique, sequential nonce to ensure proper ordering and prevent replay attacks.
The Nonce Problem
When sending multiple transactions concurrently from the same account, nonce racing occurs:
// ❌ PROBLEM: All three transactions will get the same nonce!
var task1 = web3.Eth.SendTransactionAsync(...); // Gets nonce: 5
var task2 = web3.Eth.SendTransactionAsync(...); // Gets nonce: 5 (race condition!)
var task3 = web3.Eth.SendTransactionAsync(...); // Gets nonce: 5 (race condition!)
await Task.WhenAll(task1, task2, task3);
// Result: Only one transaction succeeds, two fail with "nonce too low"
Why This Happens:
- All three transactions query
eth_getTransactionCountat nearly the same time - The blockchain hasn't mined the first transaction yet
- All queries return the same nonce value (e.g., 5)
- All three transactions try to use nonce 5
- Only the first one to reach the mempool succeeds
Solution: InMemoryNonceService
Nethereum provides InMemoryNonceService to solve nonce racing and ensure consecutive nonces:
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
using Nethereum.RPC.NonceServices;
var privateKey = "0xPRIVATE_KEY";
var account = new Account(privateKey, chainId: 1);
// Create nonce service
var client = new Nethereum.JsonRpc.Client.RpcClient(new Uri("https://mainnet.infura.io/v3/YOUR-PROJECT-ID"));
account.NonceService = new InMemoryNonceService(account.Address, client);
var web3 = new Web3(account, "https://mainnet.infura.io/v3/YOUR-PROJECT-ID");
// ✅ SOLUTION: Nonce service ensures consecutive nonces (5, 6, 7)
var task1 = web3.Eth.GetEtherTransferService().TransferEtherAndWaitForReceiptAsync("0xAddress1", 0.1m);
var task2 = web3.Eth.GetEtherTransferService().TransferEtherAndWaitForReceiptAsync("0xAddress2", 0.1m);
var task3 = web3.Eth.GetEtherTransferService().TransferEtherAndWaitForReceiptAsync("0xAddress3", 0.1m);
await Task.WhenAll(task1, task2, task3);
// Result: All three transactions succeed with nonces 5, 6, 7
How InMemoryNonceService Works
The InMemoryNonceService uses a semaphore and local nonce tracking to guarantee consecutive nonces:
public class InMemoryNonceService : INonceService
{
public BigInteger CurrentNonce { get; set; } = -1;
private SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
public bool UseLatestTransactionsOnly { get; set; } = false;
public async Task<HexBigInteger> GetNextNonceAsync()
{
await _semaphoreSlim.WaitAsync(); // 🔒 Lock to prevent concurrent access
try
{
var blockParameter = UseLatestTransactionsOnly
? BlockParameter.CreateLatest() // Only confirmed transactions
: BlockParameter.CreatePending(); // Including pending transactions
var nonce = await ethGetTransactionCount.SendRequestAsync(_account, blockParameter);
// 🎯 Key Logic: Ensure consecutive nonces
if (nonce.Value <= CurrentNonce)
{
// Blockchain hasn't caught up yet, increment locally
CurrentNonce = CurrentNonce + 1;
nonce = new HexBigInteger(CurrentNonce);
}
else
{
// Blockchain has progressed, use its nonce
CurrentNonce = nonce.Value;
}
return nonce;
}
finally
{
_semaphoreSlim.Release(); // 🔓 Unlock
}
}
}
Key Features:
- Semaphore Protection - Prevents race conditions with thread-safe locking
- Consecutive Nonce Guarantee - Automatically increments if blockchain returns same/lower nonce
- In-Memory Tracking - Tracks
CurrentNonceto ensure sequential nonces - Configurable Block Parameter - Choose between
pending(default) orlatesttransactions
Pending vs Latest Block Parameter
var nonceService = new InMemoryNonceService(account.Address, client);
// Default: Use pending transactions (includes unconfirmed transactions)
nonceService.UseLatestTransactionsOnly = false;
// Query: eth_getTransactionCount(address, "pending")
// Use this when: Sending many transactions rapidly
// Alternative: Use latest confirmed transactions only
nonceService.UseLatestTransactionsOnly = true;
// Query: eth_getTransactionCount(address, "latest")
// Use this when: You want to ignore pending transactions (rare)
When to use pending (default):
- ✅ Sending multiple transactions in quick succession
- ✅ High-throughput scenarios
- ✅ Most production applications
When to use latest:
- ⚠️ You want to replace a pending transaction (manually managing nonces)
- ⚠️ Advanced nonce manipulation scenarios
Why Nonce Management is Pluggable
The INonceService interface allows custom nonce management strategies:
public interface INonceService
{
IClient Client { get; set; }
bool UseLatestTransactionsOnly { get; set; }
Task<HexBigInteger> GetNextNonceAsync();
Task ResetNonceAsync();
}
Why Pluggable?
Different Storage Strategies
InMemoryNonceService- Local in-memory tracking (default)- Custom database-backed service - Shared nonce across multiple app instances
- Redis-backed service - Distributed nonce management
Custom Business Logic
- Nonce reservations for batch processing
- Priority transaction handling
- Gap detection and recovery
Testing and Mocking
- Mock nonce service for unit tests
- Deterministic nonce sequences for integration tests
Custom Nonce Service Example
using Nethereum.RPC.NonceServices;
using Nethereum.Hex.HexTypes;
// Custom database-backed nonce service
public class DatabaseNonceService : INonceService
{
private readonly string _address;
private readonly IMyDatabase _database;
public IClient Client { get; set; }
public bool UseLatestTransactionsOnly { get; set; }
public DatabaseNonceService(string address, IClient client, IMyDatabase database)
{
_address = address;
Client = client;
_database = database;
}
public async Task<HexBigInteger> GetNextNonceAsync()
{
// 🔒 Database-level locking for distributed systems
await _database.AcquireLockAsync($"nonce_{_address}");
try
{
// Get nonce from database (shared across app instances)
var currentNonce = await _database.GetNonceAsync(_address);
// Get blockchain nonce
var ethGetTransactionCount = new EthGetTransactionCount(Client);
var blockchainNonce = await ethGetTransactionCount.SendRequestAsync(
_address,
BlockParameter.CreatePending()
);
// Use higher of the two
var nextNonce = BigInteger.Max(currentNonce + 1, blockchainNonce.Value);
// Update database
await _database.SetNonceAsync(_address, nextNonce);
return new HexBigInteger(nextNonce);
}
finally
{
await _database.ReleaseLockAsync($"nonce_{_address}");
}
}
public async Task ResetNonceAsync()
{
await _database.DeleteNonceAsync(_address);
}
}
// Usage with custom service
var dbNonceService = new DatabaseNonceService(account.Address, client, myDatabase);
account.NonceService = dbNonceService;
Resetting Nonces
If transactions fail or you need to resynchronize with the blockchain:
// Reset nonce service to query blockchain again
await account.NonceService.ResetNonceAsync();
// Next transaction will query eth_getTransactionCount fresh
var receipt = await web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync(toAddress, amount);
Production Patterns
Pattern 1: High-Throughput Transaction Sender
var account = new Account(privateKey, chainId: 1);
account.NonceService = new InMemoryNonceService(account.Address, client);
var web3 = new Web3(account, rpcUrl);
// Send 100 transactions concurrently
var tasks = Enumerable.Range(0, 100).Select(async i =>
{
try
{
var receipt = await web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync($"0xRecipient{i}", 0.001m);
Console.WriteLine($"Transaction {i}: {receipt.TransactionHash}");
}
catch (Exception ex)
{
Console.WriteLine($"Transaction {i} failed: {ex.Message}");
}
});
await Task.WhenAll(tasks);
Pattern 2: Retry with Fresh Nonce
async Task<TransactionReceipt> SendWithRetryAsync()
{
int maxRetries = 3;
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
if (attempt > 1)
{
// Reset nonce on retry
await account.NonceService.ResetNonceAsync();
}
var receipt = await web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync(toAddress, amount);
return receipt;
}
catch (Exception ex) when (ex.Message.Contains("nonce"))
{
if (attempt == maxRetries) throw;
Console.WriteLine($"Nonce error on attempt {attempt}, retrying...");
await Task.Delay(1000 * attempt); // Exponential backoff
}
}
throw new Exception("Failed after retries");
}
Pattern 3: Manual Nonce Control (Advanced)
// For advanced scenarios, you can set nonce manually
var txInput = new TransactionInput
{
From = account.Address,
To = toAddress,
Value = new HexBigInteger(Web3.Convert.ToWei(0.1m)),
Nonce = new HexBigInteger(42) // ⚠️ Manually specified nonce
};
var signedTx = await web3.Eth.TransactionManager.SignTransactionAsync(txInput);
var txHash = await web3.Eth.Transactions.SendRawTransaction.SendRequestAsync(signedTx);
Common Nonce Issues and Solutions
| Issue | Symptom | Solution |
|---|---|---|
| Nonce too low | replacement transaction underpriced |
Reset nonce: await account.NonceService.ResetNonceAsync() |
| Nonce too high | Transaction stuck pending | Reset nonce or wait for previous transactions to confirm |
| Nonce gap | Multiple transactions stuck | Send missing nonce transaction or reset nonce |
| Race condition | Some transactions fail with same nonce | Use InMemoryNonceService |
| Distributed apps | Multiple instances send conflicting nonces | Implement custom database-backed nonce service |
When You Don't Need Nonce Management
Nonce management is automatic if you're sending transactions sequentially:
// ✅ No nonce service needed - sequential execution
var receipt1 = await web3.Eth.GetEtherTransferService().TransferEtherAndWaitForReceiptAsync(addr1, 0.1m);
var receipt2 = await web3.Eth.GetEtherTransferService().TransferEtherAndWaitForReceiptAsync(addr2, 0.1m);
var receipt3 = await web3.Eth.GetEtherTransferService().TransferEtherAndWaitForReceiptAsync(addr3, 0.1m);
// Each transaction waits for previous to confirm before querying nonce
You need nonce management when:
- ✅ Sending transactions concurrently (
Task.WhenAll) - ✅ High-throughput applications (many transactions per second)
- ✅ Distributed systems (multiple app instances using same account)
- ✅ Batch transaction sending
Message Signing
Personal Sign (EIP-191)
var account = new Account(privateKey);
// Sign UTF-8 message
string message = "Sign this message";
var signature = await account.AccountSigningService.PersonalSign.SendRequestAsync(
System.Text.Encoding.UTF8.GetBytes(message).ToHex(true)
);
// Verify
var signer = new Nethereum.Signer.EthereumMessageSigner();
var recoveredAddress = signer.EncodeUTF8AndEcRecover(message, signature);
EIP-712 Typed Data
// Sign structured typed data
var signature = await account.AccountSigningService.SignTypedDataV4.SendRequestAsync(typedData);
Best Practices
Never Hardcode Private Keys: Use environment variables or secure vaults
// Good var privateKey = Environment.GetEnvironmentVariable("PRIVATE_KEY"); var account = new Account(privateKey, chainId: 1); // Bad var account = new Account("0x123456...", chainId: 1); // Hardcoded!Always Specify Chain ID: Prevents replay attacks (EIP-155)
var account = new Account(privateKey, chainId: 1); // MainnetUse Appropriate Account Type:
- Production/Automation:
Account(offline signing) - Local Development:
ManagedAccount - Hardware Wallets:
ExternalAccount - Read-Only:
ViewOnlyAccount
- Production/Automation:
Handle Nonce for Concurrent Transactions:
account.NonceService = new InMemoryNonceService(account.Address, client);Verify Transactions:
if (receipt.Status.Value != 1) { throw new Exception("Transaction failed"); }Estimate Gas Before Sending:
var gasEstimate = await web3.Eth.TransactionManager.EstimateGasAsync(txInput);Monitor Balance:
var balance = await web3.Eth.GetBalance.SendRequestAsync(account.Address); if (balance.Value < requiredAmount) { throw new InsufficientFundsException(); }Secure KeyStore Files: Encrypt private keys when storing
var keyStoreService = new Nethereum.KeyStore.KeyStoreService(); var json = keyStoreService.EncryptAndGenerateDefaultKeyStoreAsJson( password, privateKeyBytes, address ); File.WriteAllText("keystore.json", json);
Security Considerations
Private Key Storage:
- Never commit private keys to version control
- Use environment variables, Azure Key Vault, AWS KMS, or HSMs
- Encrypt keystore files with strong passwords
Chain ID:
- Always specify chain ID to prevent replay attacks
- Verify chain ID matches the network you're using
Transaction Signing:
- Prefer
Account(offline signing) overManagedAccountin production - Never send private keys over the network
- Prefer
External Signers:
- Validate addresses returned by external signers
- Handle signer errors gracefully
Error Handling
using Nethereum.JsonRpc.Client;
try
{
var receipt = await web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync(toAddress, amount);
if (receipt.Status.Value != 1)
{
Console.WriteLine($"Transaction reverted: {receipt.TransactionHash}");
}
}
catch (RpcResponseException ex)
{
Console.WriteLine($"RPC Error: {ex.RpcError.Message}");
Console.WriteLine($"Code: {ex.RpcError.Code}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
API Reference
Account Class
Constructors:
Account(string privateKey, BigInteger? chainId = null)- Create from hex private keyAccount(byte[] privateKey, BigInteger? chainId = null)- Create from byte arrayAccount(EthECKey key, BigInteger? chainId = null)- Create from EthECKeyAccount(string privateKey, Chain chain)- Create with Chain enumAccount(byte[] privateKey, Chain chain)- Create with Chain enumAccount(EthECKey key, Chain chain)- Create with Chain enum
Static Methods:
LoadFromKeyStoreFile(string filePath, string password, BigInteger? chainId = null)- Load from UTC JSON fileLoadFromKeyStore(string json, string password, BigInteger? chainId = null)- Load from JSON string
Properties:
string Address- Ethereum addressstring PrivateKey- Private key (hex)string PublicKey- Public key (hex)BigInteger? ChainId- Chain ID for EIP-155ITransactionManager TransactionManager- Transaction manager instanceINonceService NonceService- Nonce service (lazy-initialized as InMemoryNonceService)IAccountSigningService AccountSigningService- Signing service
ManagedAccount Class
Constructor:
ManagedAccount(string accountAddress, string password)- Create managed account
Properties:
string Address- Ethereum addressstring Password- Password for nodeITransactionManager TransactionManager- ManagedAccountTransactionManagerINonceService NonceService- Optional nonce serviceIAccountSigningService AccountSigningService- Signing service
ExternalAccount Class
Constructors:
ExternalAccount(IEthExternalSigner externalSigner, BigInteger? chainId = null)- Create external accountExternalAccount(string address, IEthExternalSigner externalSigner, BigInteger? chainId = null)- With pre-known address
Methods:
Task InitialiseAsync()- Initialize account (fetches address from signer)void InitialiseDefaultTransactionManager(IClient client)- Initialize transaction manager
Properties:
IEthExternalSigner ExternalSigner- External signer implementationstring Address- Ethereum addressBigInteger? ChainId- Chain IDITransactionManager TransactionManager- ExternalAccountSignerTransactionManagerINonceService NonceService- Optional nonce serviceIAccountSigningService AccountSigningService- Signing service
ViewOnlyAccount Class
Constructor:
ViewOnlyAccount(string accountAddress)- Create view-only account
Properties:
string Address- Ethereum address to monitorITransactionManager TransactionManager- ViewOnlyAccountTransactionManager (throws on sign)INonceService NonceService- Optional nonce serviceIAccountSigningService AccountSigningService- No signing service
AccountSignerTransactionManager Class
Constructor:
AccountSignerTransactionManager(IClient rpcClient, Account account, BigInteger? overridingAccountChainId = null)- Create transaction manager
Key Methods:
Task<string> SendTransactionAsync(TransactionInput transactionInput)- Sign and sendTask<string> SignTransactionAsync(TransactionInput transaction)- Sign only (doesn't send)string SignTransaction(TransactionInput transaction)- Synchronous signingTask<HexBigInteger> GetNonceAsync(TransactionInput transaction)- Get next nonceTask<Authorisation> SignAuthorisationAsync(Authorisation authorisation)- Sign EIP-7702 authorization
Related Packages
Used By (Consumers)
- Nethereum.Web3 - Uses accounts for transaction signing and management
- Nethereum.HdWallet - Creates Account instances from HD wallet derivation
Dependencies
- Nethereum.RPC - Provides
IAccountinterface and nonce services - Nethereum.Signer - Provides
EthECKeyand transaction signing - Nethereum.Signer.EIP712 - Provides typed data signing
- Nethereum.KeyStore - Provides keystore encryption/decryption
See Also
- Nethereum.Web3 - High-level Web3 API
- Nethereum.HdWallet - HD Wallet implementation
- Nethereum.Signer - Transaction and message signing
Playground Examples
Live examples you can run in the browser:
Chain IDs and Replay Protection:
- Chain ID Usage - How to use Chain IDs to prevent replay attacks (EIP-155)
HD Wallets:
- HD Wallet Introduction - Creating HD wallets with BIP32 standard
- Deriving Accounts - Derive multiple accounts from seed words
- Generating Mnemonics - Generate 12-word seed phrases
KeyStore:
- Create Scrypt KeyStore - Create keystore with custom Scrypt params
Supported Chains
Common chain IDs available in the Chain enum:
| Network | Chain ID | Enum |
|---|---|---|
| Ethereum Mainnet | 1 | Chain.MainNet |
| Sepolia | 11155111 | Chain.Sepolia |
| Polygon | 137 | Chain.Polygon |
| Binance Smart Chain | 56 | Chain.BSC |
| Arbitrum One | 42161 | Chain.Arbitrum |
| Optimism | 10 | Chain.Optimism |
Additional Resources
- EIP-155: Simple replay attack protection
- EIP-191: Signed Data Standard
- EIP-712: Typed structured data hashing and signing
- EIP-7702: Set EOA account code
- Nethereum Documentation
- BIP-32: Hierarchical Deterministic Wallets
Important Notes
- Chain ID is crucial: Always specify chain ID in production to prevent replay attacks across different networks
- Nonce racing: Use
InMemoryNonceServicewhen sending concurrent transactions - Private key security: Never hardcode or commit private keys. Use environment variables or secure key management systems
- KeyStore passwords: Use strong passwords for keystore encryption (high Scrypt N parameter increases security but also computation time)
- Gas estimation: Always estimate gas before sending transactions to avoid out-of-gas errors
- Transaction verification: Check
receipt.Status.Value == 1to ensure transaction success
| Product | Versions 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. |
-
.NETFramework 4.5.1
- Nethereum.KeyStore (>= 5.8.0)
- Nethereum.RPC (>= 5.8.0)
- Nethereum.Signer (>= 5.8.0)
- Nethereum.Signer.EIP712 (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
.NETFramework 4.6.1
- Nethereum.KeyStore (>= 5.8.0)
- Nethereum.RPC (>= 5.8.0)
- Nethereum.Signer (>= 5.8.0)
- Nethereum.Signer.EIP712 (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
.NETStandard 2.0
- Nethereum.KeyStore (>= 5.8.0)
- Nethereum.RPC (>= 5.8.0)
- Nethereum.Signer (>= 5.8.0)
- Nethereum.Signer.EIP712 (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- NETStandard.Library (>= 2.0.3)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
net6.0
- Nethereum.KeyStore (>= 5.8.0)
- Nethereum.RPC (>= 5.8.0)
- Nethereum.Signer (>= 5.8.0)
- Nethereum.Signer.EIP712 (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
net8.0
- Nethereum.KeyStore (>= 5.8.0)
- Nethereum.RPC (>= 5.8.0)
- Nethereum.Signer (>= 5.8.0)
- Nethereum.Signer.EIP712 (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
net9.0
- Nethereum.KeyStore (>= 5.8.0)
- Nethereum.RPC (>= 5.8.0)
- Nethereum.Signer (>= 5.8.0)
- Nethereum.Signer.EIP712 (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
NuGet packages (23)
Showing the top 5 NuGet packages that depend on Nethereum.Accounts:
| 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 |
|
|
FenixAlliance.ACL.Dependencies
Application Component for the Alliance Business Suite. |
|
|
Nethereum
Package Description |
|
|
Nethereum.Signer.Ledger
Netherum.Signer.Ledger provides the External Signing capability for Ethereum transactions and Messages using Ledger Hardware wallets |
|
|
Nethereum.Signer.Trezor
Netherum.Signer.Trezor provides the External Signing capability for Ethereum transactions and Messages using Trezor Hardware wallets |
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on Nethereum.Accounts:
| Repository | Stars |
|---|---|
|
Texnomic/SecureDNS
Secure, Modern, Fully-Featured, All-In-One Cross-Architecture & Cross-Platform DNS Server Using .NET 10
|
| Version | Downloads | Last Updated |
|---|---|---|
| 5.8.0 | 154 | 1/6/2026 |
| 5.0.0 | 291,807 | 5/28/2025 |
| 4.29.0 | 200,027 | 2/10/2025 |
| 4.28.0 | 71,299 | 1/7/2025 |
| 4.27.1 | 12,435 | 12/24/2024 |
| 4.27.0 | 1,531 | 12/24/2024 |
| 4.26.0 | 98,486 | 10/1/2024 |
| 4.25.0 | 22,482 | 9/19/2024 |
| 4.21.4 | 96,405 | 8/9/2024 |
| 4.21.3 | 11,583 | 7/22/2024 |
| 4.21.2 | 68,425 | 6/26/2024 |
| 4.21.1 | 2,575 | 6/26/2024 |
| 4.21.0 | 10,393 | 6/18/2024 |
| 4.20.0 | 310,950 | 3/28/2024 |
| 4.19.0 | 72,202 | 2/16/2024 |
| 4.18.0 | 269,701 | 11/21/2023 |
| 4.17.1 | 76,655 | 9/28/2023 |
| 4.17.0 | 16,066 | 9/27/2023 |
| 4.16.0 | 117,281 | 8/14/2023 |
| 4.15.2 | 130,342 | 7/11/2023 |
| 4.15.1 | 2,912 | 7/11/2023 |
| 4.15.0 | 8,657 | 7/11/2023 |
| 4.14.0 | 188,586 | 3/19/2023 |
| 4.13.0 | 131,310 | 2/18/2023 |
| 4.12.0 | 268,381 | 12/9/2022 |
| 4.11.0 | 172,432 | 10/27/2022 |
| 4.9.0 | 111,780 | 9/27/2022 |
| 4.8.0 | 179,785 | 8/24/2022 |
| 4.7.0 | 159,087 | 7/20/2022 |
| 4.6.1 | 142,873 | 6/18/2022 |
| 4.6.0 | 8,113 | 6/16/2022 |
| 4.5.0 | 465,566 | 5/13/2022 |
| 4.4.1 | 103,659 | 4/27/2022 |
| 4.4.0 | 11,755 | 4/27/2022 |
| 4.3.0 | 62,513 | 4/12/2022 |
| 4.2.0 | 171,107 | 2/18/2022 |
| 4.1.1 | 485,476 | 11/4/2021 |
| 4.1.0 | 28,132 | 10/15/2021 |
| 4.0.5 | 137,805 | 8/12/2021 |
| 4.0.4 | 6,490 | 8/10/2021 |
| 4.0.3 | 6,389 | 8/8/2021 |
| 4.0.2 | 5,548 | 8/5/2021 |
| 4.0.1 | 11,241 | 7/28/2021 |
| 4.0.0 | 16,652 | 7/26/2021 |
| 3.8.0 | 383,704 | 7/3/2020 |
| 3.7.1 | 115,638 | 2/13/2020 |
| 3.7.0 | 8,438 | 2/13/2020 |
| 3.6.0 | 31,653 | 1/27/2020 |
| 3.5.0 | 19,331 | 12/31/2019 |
| 3.4.0 | 147,426 | 7/29/2019 |
| 3.3.0 | 73,110 | 4/23/2019 |
| 3.2.0 | 44,055 | 4/8/2019 |
| 3.1.2 | 22,243 | 3/13/2019 |
| 3.1.1 | 5,364 | 3/12/2019 |
| 3.1.0 | 26,217 | 3/12/2019 |
| 3.0.0 | 36,666 | 11/28/2018 |
| 3.0.0-rc3 | 5,477 | 10/25/2018 |
| 3.0.0-rc2 | 3,283 | 10/24/2018 |
| 3.0.0-rc1 | 8,680 | 7/25/2018 |
| 2.5.1 | 104,596 | 6/5/2018 |
| 2.5.0 | 5,672 | 6/4/2018 |
| 2.4.0 | 51,639 | 3/11/2018 |
| 2.3.1 | 7,118 | 3/7/2018 |
| 2.3.0 | 5,580 | 3/6/2018 |