Nethereum.Merkle
5.8.0
Prefix Reserved
See the version list below for details.
dotnet add package Nethereum.Merkle --version 5.8.0
NuGet\Install-Package Nethereum.Merkle -Version 5.8.0
<PackageReference Include="Nethereum.Merkle" Version="5.8.0" />
<PackageVersion Include="Nethereum.Merkle" Version="5.8.0" />
<PackageReference Include="Nethereum.Merkle" />
paket add Nethereum.Merkle --version 5.8.0
#r "nuget: Nethereum.Merkle, 5.8.0"
#:package Nethereum.Merkle@5.8.0
#addin nuget:?package=Nethereum.Merkle&version=5.8.0
#tool nuget:?package=Nethereum.Merkle&version=5.8.0
Nethereum.Merkle
Comprehensive Merkle tree implementations for Ethereum smart contract verification, airdrops, whitelisting, and large-scale state management.
Overview
Nethereum.Merkle provides production-ready Merkle tree data structures optimized for blockchain use cases:
- Standard Merkle Trees: For airdrops, whitelisting, and contract verification
- OpenZeppelin Compatible: Interoperable with OpenZeppelin's JavaScript library and Solidity contracts
- Incremental Trees: Efficient updates without rebuilding the entire tree
- Sparse Merkle Trees: Handle millions of records with database-backed storage
- Proof Generation & Verification: Create and verify cryptographic proofs on-chain and off-chain
Use Merkle trees to efficiently verify membership in large datasets, enable token airdrops, implement whitelists, or manage scalable state commitments.
Installation
dotnet add package Nethereum.Merkle
Dependencies
Nethereum Dependencies:
- Nethereum.ABI - ABI encoding for struct-based Merkle leaves
- Nethereum.Util - Keccak-256 hashing and byte array utilities
Key Concepts
What is a Merkle Tree?
A Merkle tree is a cryptographic data structure that allows efficient verification of large datasets:
- Leaves: Data elements (hashed)
- Branches: Hashes of pairs of child nodes
- Root: Single hash representing the entire dataset
Key Property: You can prove an element exists in the dataset by providing a small "proof" (log₂(n) hashes) instead of the entire dataset.
Pairing Strategies
When combining hash pairs, Nethereum.Merkle supports:
- Sorted Pairing (
PairingConcatType.Sorted): Hashes are ordered before concatenation (OpenZeppelin standard) - Normal Pairing (
PairingConcatType.Normal): Hashes concatenated in given order
Use Cases
- Token Airdrops: Distribute tokens to thousands of addresses efficiently
- Whitelisting: Verify user eligibility on-chain with minimal gas
- State Commitments: Compress large state into a single hash
- Fraud Proofs: Prove invalid state transitions in layer 2 solutions
- NFT Metadata: Prove authenticity of off-chain metadata
Quick Start
using Nethereum.Merkle;
using Nethereum.Util.HashProviders;
using Nethereum.Util.ByteArrayConvertors;
using System.Collections.Generic;
// Create a simple merkle tree with string addresses
var addresses = new List<string>
{
"0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5",
"0xA61b1fB89Dd42fcDDD2D3fA19c2B715c426692c7",
"0xfa6179E49EE57a06391F218965b35B632F930472"
};
var merkleTree = new MerkleTree<string>(
new Sha3KeccackHashProvider(),
new HexStringByteArrayConvertor()
);
merkleTree.BuildTree(addresses);
// Get the root hash for your smart contract
var rootHash = merkleTree.Root.Hash.ToHex(true);
// Generate proof for an address
var proof = merkleTree.GetProof(addresses[0]);
// Verify proof
var isValid = merkleTree.VerifyProof(proof, addresses[0]);
Usage Examples
Example 1: Simple Merkle Tree with Character Data
using Nethereum.Merkle;
using Nethereum.Util.HashProviders;
using Nethereum.Util.ByteArrayConvertors;
using Nethereum.Hex.HexConvertors.Extensions;
using System.Linq;
// Create a list of characters
var elements = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
.ToCharArray()
.ToList();
// Build the merkle tree
var merkleTree = new MerkleTree<char>(
new Sha3KeccackHashProvider(),
new ChartByteArrayConvertor()
);
merkleTree.BuildTree(elements);
// Get the root hash
var hexRoot = merkleTree.Root.Hash.ToHex(true);
// Result: "0xec0dffcb601ee38fa372bbf1d89ed16761db0a0b215480032b783f8c33230783"
// Generate proof for 'A'
var proofs = merkleTree.GetProof('A');
// Verify the proof
var isValid = merkleTree.VerifyProof(proofs, 'A'); // Returns true
Source: tests/Nethereum.Contracts.IntegrationTests/MerkleDrop/MerkleUnitTests.cs
Example 2: OpenZeppelin-Compatible Merkle Tree for Airdrops
using Nethereum.Merkle;
using Nethereum.ABI.FunctionEncoding.Attributes;
using System.Collections.Generic;
using System.Numerics;
// Define your airdrop recipient struct (must match Solidity struct)
[Struct("AirdropRecipient")]
public class AirdropRecipient
{
[Parameter("address", 1)]
public string User { get; set; }
[Parameter("uint256", "amount", 2)]
public BigInteger Amount { get; set; }
}
// Create recipients list
var recipients = new List<AirdropRecipient>
{
new AirdropRecipient
{
User = "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5",
Amount = BigInteger.Parse("1000000000000000000") // 1 token
},
new AirdropRecipient
{
User = "0xA61b1fB89Dd42fcDDD2D3fA19c2B715c426692c7",
Amount = BigInteger.Parse("2000000000000000000") // 2 tokens
},
new AirdropRecipient
{
User = "0xfa6179E49EE57a06391F218965b35B632F930472",
Amount = BigInteger.Parse("500000000000000000") // 0.5 tokens
}
};
// Build OpenZeppelin-compatible merkle tree
var merkleTree = new OpenZeppelinStandardMerkleTree<AirdropRecipient>();
merkleTree.BuildTree(recipients);
// Deploy your smart contract with this root
var rootHash = merkleTree.Root.Hash.ToHex(true);
// User wants to claim - generate their proof
var userToClaim = recipients[0];
var proof = merkleTree.GetProof(userToClaim);
// User submits proof + their data to claim() function on-chain
// Contract verifies using OpenZeppelin's MerkleProof.sol
Source: tests/Nethereum.Contracts.IntegrationTests/MerkleDrop/OpenZeppelinMerkleUnitTests.cs
Example 3: Whitelist with Single Parameter
using Nethereum.Merkle;
using Nethereum.ABI.FunctionEncoding.Attributes;
using System.Collections.Generic;
[Struct("WhitelistEntry")]
public class WhitelistEntry
{
[Parameter("address", 1)]
public string User { get; set; }
}
// Create whitelist
var whitelist = new List<WhitelistEntry>
{
new WhitelistEntry { User = "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5" },
new WhitelistEntry { User = "0xA61b1fB89Dd42fcDDD2D3fA19c2B715c426692c7" },
new WhitelistEntry { User = "0xfa6179E49EE57a06391F218965b35B632F930472" },
new WhitelistEntry { User = "0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326" }
};
var merkleTree = new OpenZeppelinStandardMerkleTree<WhitelistEntry>();
merkleTree.BuildTree(whitelist);
// Store root hash in your smart contract constructor
var rootHash = merkleTree.Root.Hash.ToHex(true);
// User proves they're whitelisted
var userEntry = whitelist[0];
var proof = merkleTree.GetProof(userEntry);
var isWhitelisted = merkleTree.VerifyProof(proof, userEntry); // true
Source: tests/Nethereum.Contracts.IntegrationTests/MerkleDrop/OpenZeppelinMerkleUnitTests.cs
Example 4: Lean Incremental Merkle Tree (Efficient Updates)
using Nethereum.Merkle;
using Nethereum.Util.HashProviders;
using Nethereum.Util.ByteArrayConvertors;
using System.Numerics;
// Create an incremental tree (efficient for frequent updates)
var tree = new LeanIncrementalMerkleTree<BigInteger>(
new Sha3KeccackHashProvider(),
new BigIntegerByteArrayConvertor(),
PairingConcatType.Normal
);
// Insert leaves one at a time (tree updates incrementally)
tree.InsertLeaf(BigInteger.Parse("100"));
tree.InsertLeaf(BigInteger.Parse("200"));
tree.InsertLeaf(BigInteger.Parse("300"));
// Get current root
var root = tree.Root.ToHex(true);
// Insert many leaves at once
var newValues = new[] {
BigInteger.Parse("400"),
BigInteger.Parse("500")
};
tree.InsertMany(newValues);
// Update existing leaf
tree.Update(0, BigInteger.Parse("150")); // Change first leaf from 100 to 150
// Check if value exists
var hasValue = tree.Has(BigInteger.Parse("200")); // true
var index = tree.IndexOf(BigInteger.Parse("200")); // 1
// Generate proof
var proof = tree.GenerateProof(1); // Proof for index 1
// Verify proof
var isValid = tree.VerifyProof(proof, BigInteger.Parse("200"), tree.Root);
// Export tree (for storage or sharing)
var exported = tree.Export(); // JSON string
// Import tree later
var imported = LeanIncrementalMerkleTree<BigInteger>.Import(
new Sha3KeccackHashProvider(),
new BigIntegerByteArrayConvertor(),
exported,
s => BigInteger.Parse(s) // Leaf mapper
);
Source: src/Nethereum.Merkle/LeanIncrementalMerkleTree.cs
Example 5: Sparse Merkle Tree for Large Datasets
using Nethereum.Merkle.Sparse;
using Nethereum.Util.HashProviders;
using Nethereum.Util.ByteArrayConvertors;
using System.Collections.Generic;
using System.Threading.Tasks;
// Create sparse merkle tree with in-memory storage
// (use DatabaseSparseMerkleTreeStorage for millions of records)
var storage = new InMemorySparseMerkleTreeStorage<string>();
var sparseTree = new SparseMerkleTree<string>(
depth: 256, // Tree depth (bits for key space)
hashProvider: new Sha3KeccackHashProvider(),
byteArrayConvertor: new HexStringByteArrayConvertor(),
storage: storage
);
// Set leaves by key (async for database support)
await sparseTree.SetLeafAsync("key1", "value1");
await sparseTree.SetLeafAsync("key2", "value2");
await sparseTree.SetLeafAsync("key3", "value3");
// Batch update for performance (critical for processing blocks)
var updates = new Dictionary<string, string>
{
["key4"] = "value4",
["key5"] = "value5",
["key6"] = "value6"
};
await sparseTree.SetLeavesAsync(updates);
// Get root hash (cached for performance)
var root = await sparseTree.GetRootHashAsync();
// Get individual leaf
var value = await sparseTree.GetLeafAsync("key1");
// Get leaf count
var count = await sparseTree.GetLeafCountAsync(); // 6
// Clear all data
await sparseTree.ClearAsync();
Source: src/Nethereum.Merkle/Sparse/SparseMerkleTree.cs
Example 6: Using MerkleDropMerkleTree for Token Airdrops
using Nethereum.Merkle;
using System.Collections.Generic;
using System.Numerics;
// MerkleDropItem is pre-defined for airdrop scenarios
var airdropItems = new List<MerkleDropItem>
{
new MerkleDropItem
{
Index = 0,
Account = "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5",
Amount = BigInteger.Parse("1000000000000000000")
},
new MerkleDropItem
{
Index = 1,
Account = "0xA61b1fB89Dd42fcDDD2D3fA19c2B715c426692c7",
Amount = BigInteger.Parse("2500000000000000000")
},
new MerkleDropItem
{
Index = 2,
Account = "0xfa6179E49EE57a06391F218965b35B632F930472",
Amount = BigInteger.Parse("750000000000000000")
}
};
// Build airdrop merkle tree
var merkleDropTree = new MerkleDropMerkleTree();
merkleDropTree.BuildTree(airdropItems);
// Get root for smart contract
var root = merkleDropTree.Root.Hash.ToHex(true);
// Generate proof for a specific recipient
var proof = merkleDropTree.GetProof(airdropItems[0]);
// Recipient calls claim(proof, index, account, amount) on contract
Source: src/Nethereum.Merkle/MerkleDropMerkleTree.cs
Example 7: Custom Merkle Tree with Custom Data Types
using Nethereum.Merkle;
using Nethereum.Util.HashProviders;
using Nethereum.Util.ByteArrayConvertors;
using System.Collections.Generic;
using System.Text;
// Create custom byte array convertor for your data type
public class MyDataConvertor : IByteArrayConvertor<MyCustomData>
{
public byte[] ConvertToByteArray(MyCustomData value)
{
// Serialize your data type to bytes
var json = JsonConvert.SerializeObject(value);
return Encoding.UTF8.GetBytes(json);
}
}
public class MyCustomData
{
public string Name { get; set; }
public int Value { get; set; }
}
// Create merkle tree with custom type
var items = new List<MyCustomData>
{
new MyCustomData { Name = "Alice", Value = 100 },
new MyCustomData { Name = "Bob", Value = 200 },
new MyCustomData { Name = "Charlie", Value = 150 }
};
var merkleTree = new MerkleTree<MyCustomData>(
new Sha3KeccackHashProvider(),
new MyDataConvertor(),
PairingConcatType.Sorted // OpenZeppelin compatible
);
merkleTree.BuildTree(items);
// Get root
var root = merkleTree.Root.Hash.ToHex(true);
// Generate and verify proof
var proof = merkleTree.GetProof(items[0]);
var isValid = merkleTree.VerifyProof(proof, items[0]);
Source: src/Nethereum.Merkle/MerkleTree.cs
Example 8: Dynamically Adding Leaves to Existing Tree
using Nethereum.Merkle;
using Nethereum.Util.HashProviders;
using Nethereum.Util.ByteArrayConvertors;
// Start with initial set
var addresses = new List<string>
{
"0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5",
"0xA61b1fB89Dd42fcDDD2D3fA19c2B715c426692c7"
};
var merkleTree = new MerkleTree<string>(
new Sha3KeccackHashProvider(),
new HexStringByteArrayConvertor()
);
merkleTree.BuildTree(addresses);
var initialRoot = merkleTree.Root.Hash.ToHex(true);
// Add single leaf (tree rebuilds)
merkleTree.InsertLeaf("0xfa6179E49EE57a06391F218965b35B632F930472");
var newRoot = merkleTree.Root.Hash.ToHex(true);
// Root has changed!
// Add multiple leaves at once
var newAddresses = new[]
{
"0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326",
"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
};
merkleTree.InsertLeaves(newAddresses);
// Tree now has 5 leaves total
var finalRoot = merkleTree.Root.Hash.ToHex(true);
Source: src/Nethereum.Merkle/MerkleTree.cs
Example 9: Static Proof Verification (Without Building Tree)
using Nethereum.Merkle;
using Nethereum.Util.HashProviders;
using Nethereum.Hex.HexConvertors.Extensions;
// You have a proof from somewhere (API, database, etc.)
var proof = new List<byte[]>
{
"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111".HexToByteArray(),
"0xe62e1dfc08d58fd144947903447473a090c958fe34e2425d578237fcdf1ab5a4".HexToByteArray(),
"0x1907ce7877ec74782a26c166b562bfbdd4c8d8833f98ad82ae9dc8e98db20093".HexToByteArray()
};
var rootHash = "0xec0dffcb601ee38fa372bbf1d89ed16761db0a0b215480032b783f8c33230783".HexToByteArray();
var itemHash = "0x3f4a1640bcca71e45d053d67ab9891fe44608f4db37cc45e5523588c76c79539".HexToByteArray();
// Verify without building the tree (static method)
var hashProvider = new Sha3KeccackHashProvider();
var isValid = MerkleTree<object>.VerifyProof(
proof,
rootHash,
itemHash,
hashProvider,
PairingConcatType.Sorted
);
// Returns true if proof is valid
Source: src/Nethereum.Merkle/MerkleTree.cs
API Reference
MerkleTree<T>
Generic Merkle tree implementation with pluggable hashing and serialization.
Constructor:
MerkleTree(
IHashProvider hashProvider,
IByteArrayConvertor<T> byteArrayConvertor,
PairingConcatType pairingConcatType = PairingConcatType.Sorted
)
Key Properties:
MerkleTreeNode Root: The root node of the treeList<MerkleTreeNode> Leaves: All leaf nodesList<List<MerkleTreeNode>> Layers: All layers of the tree (for proof generation)
Key Methods:
void BuildTree(List<T> items): Build tree from itemsvoid InsertLeaf(T item): Add single item and rebuildvoid InsertLeaves(IEnumerable<T> items): Add multiple items and rebuildList<byte[]> GetProof(T item): Generate proof for an itemList<byte[]> GetProof(int index): Generate proof by leaf indexList<byte[]> GetProof(byte[] hashLeaf): Generate proof by leaf hashbool VerifyProof(IEnumerable<byte[]> proof, T item): Verify proof for itembool VerifyProof(IEnumerable<byte[]> proof, byte[] itemHash): Verify proof for hashstatic bool VerifyProof(proof, rootHash, itemHash, hashProvider, pairingType): Static verification
OpenZeppelinStandardMerkleTree<T>
Merkle tree compatible with OpenZeppelin's JavaScript library and Solidity contracts.
var tree = new OpenZeppelinStandardMerkleTree<TAbiStruct>();
- Inherits from
AbiStructSha3KeccackMerkleTree<T> - Uses sorted pairing (OpenZeppelin standard)
- Works with ABI-annotated structs (
[Struct]and[Parameter]attributes)
Requirements:
- Type
Tmust have[Struct]attribute - Properties must have
[Parameter]attributes with correct types and order
AbiStructMerkleTree<T>
Merkle tree for ABI-encoded structs.
var tree = new AbiStructMerkleTree<MyStruct>();
- Uses
AbiStructEncoderPackedByteConvertor<T>for encoding - Uses Keccak-256 (Sha3) hashing
- Sorted pairing by default
MerkleDropMerkleTree
Specialized tree for token airdrops using MerkleDropItem.
var tree = new MerkleDropMerkleTree();
MerkleDropItem Properties:
BigInteger Index: Sequential indexstring Account: Recipient addressBigInteger Amount: Token amount
LeanIncrementalMerkleTree<T>
Efficient incremental tree optimized for frequent updates.
Constructor:
LeanIncrementalMerkleTree(
IHashProvider hashProvider,
IByteArrayConvertor<T> byteArrayConvertor,
PairingConcatType pairingType = PairingConcatType.Normal
)
Key Properties:
byte[] Root: Current root hash (auto-updated)List<T> Leaves: Current leavesint Size: Number of leavesint Depth: Tree depth
Key Methods:
void InsertLeaf(T leaf): Add single leaf (incremental update)void InsertMany(IEnumerable<T> leaves): Batch insertvoid Update(int index, T newLeaf): Update existing leafvoid UpdateMany(int[] indices, T[] newLeaves): Batch updatebool Has(T leaf): Check if leaf existsint IndexOf(T leaf): Find leaf indexMerkleProof GenerateProof(int leafIndex): Generate proofbool VerifyProof(MerkleProof proof, T leaf, byte[] root): Verify proofstring Export(Func<byte[], string> formatter = null): Export to JSONstatic Import(hashProvider, convertor, json, leafMapper, ...): Import from JSON
SparseMerkleTree<T>
High-performance sparse tree for millions of records with pluggable storage.
Constructor:
SparseMerkleTree(
int depth, // 1-256
IHashProvider hashProvider,
IByteArrayConvertor<T> byteArrayConvertor,
ISparseMerkleTreeStorage<T> storage
)
Key Properties:
int Depth: Tree depth (key space size)string EmptyLeafHash: Hash of empty leaf
Key Methods:
async Task SetLeafAsync(string key, T value): Set leaf valueasync Task<T> GetLeafAsync(string key): Get leaf valueasync Task<string> GetRootHashAsync(): Get cached root hashasync Task SetLeavesAsync(Dictionary<string, T> updates): Batch update (optimized)async Task<long> GetLeafCountAsync(): Count non-empty leavesasync Task ClearAsync(): Clear all data
Synchronous Overloads:
void SetLeaf(string key, T value)T GetLeaf(string key)string GetRootHash()
Storage Implementations:
InMemorySparseMerkleTreeStorage<T>: For testing/small datasetsDatabaseSparseMerkleTreeStorage<T>: For production/large datasets (requiresISparseMerkleRepository)
MerkleProof
Container for merkle proof data.
Properties:
List<byte[]> ProofNodes: Hashes needed to verify membership
MerkleTreeNode
Node in the merkle tree.
Properties:
byte[] Hash: Node hash value
Methods:
bool Matches(byte[] hash): Check if hash matchesMerkleTreeNode Clone(): Create copy
Pairing Strategies
PairingConcatType Enum
Sorted: Sort hashes before concatenating (OpenZeppelin standard)Normal: Concatenate in given order
IPairConcatStrategy
Interface for custom pairing strategies.
Implementations:
SortedPairConcatStrategy: Lexicographic sortingPairConcatStrategy: Direct concatenation
Factory:
PairingConcatFactory.GetPairConcatStrategy(PairingConcatType): Get strategy instance
Related Packages
Used By (Consumers)
- Nethereum.Contracts - Uses merkle trees for airdrop contract deployments
- Smart Contract Verification - On-chain proof verification
- Token Distribution - ERC-20/ERC-721 airdrops
- Whitelisting Systems - NFT mints, presales
Dependencies
- Nethereum.ABI - ABI encoding for struct-based leaves
- Nethereum.Util - Keccak-256 hashing, byte conversions
Important Notes
Gas Optimization
On-Chain Verification:
- Proof verification costs approximately
keccak256(32 bytes) * depthgas - For tree depth 20 (1M leaves): ~20 keccak operations
- Much cheaper than storing/checking entire list on-chain
Best Practices:
- Use sorted pairing for OpenZeppelin compatibility
- Keep tree balanced (number of leaves close to power of 2)
- For very large trees, consider sparse merkle trees
OpenZeppelin Compatibility
To ensure compatibility with OpenZeppelin's MerkleProof.sol:
- Use
OpenZeppelinStandardMerkleTree<T> - Use sorted pairing (
PairingConcatType.Sorted) - Struct fields must match Solidity struct exactly (order and types)
- Use
keccak256(abi.encodePacked(...))encoding
Solidity Example:
function claim(bytes32[] calldata proof, address account, uint256 amount) external {
bytes32 leaf = keccak256(abi.encodePacked(account, amount));
require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid proof");
// ... claim logic
}
Performance Considerations
Standard MerkleTree:
- Building: O(n log n)
- Proof generation: O(log n)
- Proof verification: O(log n)
- Inserting leaves: O(n log n) (rebuilds tree)
LeanIncrementalMerkleTree:
- Insert single leaf: O(n) (linear scan to rebuild)
- Better for frequent reads, occasional writes
- Export/import for persistence
SparseMerkleTree:
- Set leaf: O(log n) with path invalidation optimization
- Batch updates: O(m log n) for m leaves
- Root computation: O(1) when cached, O(log n) when dirty
- Optimized for millions of records with database storage
Recommendation:
- < 10K items: Use
MerkleTree<T>orOpenZeppelinStandardMerkleTree<T> - 10K-100K items: Use
LeanIncrementalMerkleTree<T>for updates - > 100K items: Use
SparseMerkleTree<T>with database storage
Tree Depth Calculation
For n leaves:
- Depth = ⌈log₂(n)⌉
- Proof size = depth × 32 bytes
Examples:
- 100 leaves: depth 7, proof 224 bytes
- 1,000 leaves: depth 10, proof 320 bytes
- 1,000,000 leaves: depth 20, proof 640 bytes
Security Considerations
Second Preimage Attacks:
- Nethereum.Merkle mitigates by using different encoding for leaves vs branches
- Leaves are hashed once, branches hash the concatenation of children
Collision Resistance:
- Keccak-256 provides 128-bit collision resistance
- Sufficient for all practical Ethereum use cases
Proof Validation:
- Always verify proofs on-chain before taking action
- Store merkle root on-chain (in contract storage or as constant)
- Never trust client-provided roots
Common Pitfalls
- Forgetting to Rebuild: After
InsertLeaf(), the tree is rebuilt automatically - Wrong Pairing Type: Use
Sortedfor OpenZeppelin compatibility - ABI Encoding Mismatch: Ensure C# struct matches Solidity struct exactly
- Index vs Hash:
GetProof()has overloads for item, index, or hash - Sparse Tree Keys: Keys must fit within the tree depth (depth 256 = 256-bit keys)
Sparse Merkle Tree Storage
In-Memory (testing only):
var storage = new InMemorySparseMerkleTreeStorage<string>();
Database (production):
public class MyRepository : ISparseMerkleRepository
{
// Implement database access
}
var storage = new DatabaseSparseMerkleTreeStorage<string>(
new MyRepository(),
new HexStringByteArrayConvertor()
);
Database storage is critical for:
- Persisting tree across restarts
- Handling millions of records
- Enabling horizontal scaling
Additional Resources
Ethereum & Merkle Trees
- Merkle Trees on Ethereum.org
- OpenZeppelin Merkle Tree JavaScript Library
- OpenZeppelin MerkleProof.sol
Use Case Examples
- Uniswap Merkle Distributor - Token airdrop pattern
- ENS Airdrop - Real-world example
Research Papers
Nethereum Documentation
| 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.ABI (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
.NETFramework 4.6.1
- Nethereum.ABI (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
.NETStandard 2.0
- Nethereum.ABI (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- NETStandard.Library (>= 2.0.3)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
net6.0
- Nethereum.ABI (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
net8.0
- Nethereum.ABI (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
net9.0
- Nethereum.ABI (>= 5.8.0)
- Nethereum.Util (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Nethereum.Merkle:
| Package | Downloads |
|---|---|
|
Nethereum.PrivacyPools
Nethereum PrivacyPools - 0xbow Privacy Pools protocol SDK with Poseidon commitments, LeanIMT, and Groth16 proof verification |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 6.1.0 | 710 | 3/25/2026 |
| 6.0.4 | 161 | 3/18/2026 |
| 6.0.3 | 111 | 3/18/2026 |
| 6.0.1 | 125 | 3/17/2026 |
| 6.0.0 | 111 | 3/16/2026 |
| 5.8.0 | 293 | 1/6/2026 |
| 5.0.0 | 649 | 5/28/2025 |
| 4.29.0 | 3,602 | 2/10/2025 |
| 4.28.0 | 1,251 | 1/7/2025 |
| 4.27.1 | 281 | 12/24/2024 |
| 4.27.0 | 225 | 12/24/2024 |
| 4.26.0 | 907 | 10/1/2024 |
| 4.25.0 | 511 | 9/19/2024 |
| 4.21.4 | 330 | 8/9/2024 |
| 4.21.3 | 947 | 7/22/2024 |
| 4.21.2 | 404 | 6/26/2024 |
| 4.21.1 | 239 | 6/26/2024 |
| 4.21.0 | 240 | 6/18/2024 |
| 4.20.0 | 780 | 3/28/2024 |
| 4.19.0 | 235 | 2/16/2024 |