![]() |
VOOZH | about |
dotnet add package Nethereum.Metamask --version 6.1.0
NuGet\Install-Package Nethereum.Metamask -Version 6.1.0
<PackageReference Include="Nethereum.Metamask" Version="6.1.0" />
<PackageVersion Include="Nethereum.Metamask" Version="6.1.0" />Directory.Packages.props
<PackageReference Include="Nethereum.Metamask" />Project file
paket add Nethereum.Metamask --version 6.1.0
#r "nuget: Nethereum.Metamask, 6.1.0"
#:package Nethereum.Metamask@6.1.0
#addin nuget:?package=Nethereum.Metamask&version=6.1.0Install as a Cake Addin
#tool nuget:?package=Nethereum.Metamask&version=6.1.0Install as a Cake Tool
Core MetaMask integration abstractions for Nethereum. Provides platform-agnostic interfaces and request interceptors for integrating MetaMask wallet into .NET applications.
Nethereum.Metamask is the foundation package for MetaMask integration. It defines the core abstractions (IMetamaskInterop) and request interceptor logic needed to route Ethereum RPC calls through the MetaMask browser extension. Platform-specific implementations (Blazor, desktop, mobile) build on top of these abstractions.
Key Features:
IMetamaskInterop interface for platform-specific implementationsMetamaskHostProvider implementing IEthereumHostProviderMetamaskInterceptor for routing RPC requests through MetaMaskUse Cases:
dotnet add package Nethereum.Metamask
Note: This is the core abstraction package. For Blazor applications, use Nethereum.Metamask.Blazor which provides the JavaScript interop implementation.
┌─────────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Nethereum.Metamask │ │ Nethereum.Unity │ │ Desktop/Mobile │
│ .Blazor │ │ .Metamask │ │ Implementations │
│ │ │ │ │ │
│ MetamaskBlazer- │ │ Unity-specific │ │ Platform- │
│ Interop (JS) │ │ implementation │ │ specific impl │
└─────────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
└───────────────────────┴──────────────────────┘
│
implements IMetamaskInterop
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Nethereum.Metamask (This Package) │
│ Core Abstractions │
│ │
│ ┌──────────────────────┐ ┌────────────────────────┐ │
│ │ IMetamaskInterop │ │ MetamaskHostProvider │ │
│ │ (Platform Interface) │ │ (Wallet Provider) │ │
│ └──────────────────────┘ └────────────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌────────────────────────┐ │
│ │ MetamaskInterceptor │ │ MetamaskRpcRequest- │ │
│ │ (Request Router) │ │ Message │ │
│ └──────────────────────┘ └────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Nethereum.Web3 │
│ (Ethereum Interaction) │
└─────────────────────────────────────────────────────────────┘
Platform-specific interface that must be implemented by each platform (Blazor, Unity, desktop):
public interface IMetamaskInterop
{
Task<string> EnableEthereumAsync();
Task<bool> CheckMetamaskAvailability();
Task<string> GetSelectedAddress();
Task<RpcResponseMessage> SendAsync(RpcRequestMessage rpcRequestMessage);
Task<RpcResponseMessage> SendTransactionAsync(MetamaskRpcRequestMessage rpcRequestMessage);
Task<string> SignAsync(string utf8Hex);
}
Implements IEthereumHostProvider for MetaMask:
public class MetamaskHostProvider : IEthereumHostProvider
{
public string Name { get; } // "Metamask"
public bool Available { get; }
public string SelectedAccount { get; }
public long SelectedNetworkChainId { get; }
public bool Enabled { get; }
// Events
event Func<string, Task> SelectedAccountChanged;
event Func<long, Task> NetworkChanged;
event Func<bool, Task> AvailabilityChanged;
event Func<bool, Task> EnabledChanged;
// Methods
Task<bool> CheckProviderAvailabilityAsync();
Task<string> EnableProviderAsync();
Task<IWeb3> GetWeb3Async();
Task<string> SignMessageAsync(string message);
}
Intercepts Web3 requests and routes appropriate methods through MetaMask:
public class MetamaskInterceptor : RequestInterceptor
{
// Methods routed through MetaMask
public static List<string> SigningWalletTransactionsMethods { get; } = new List<string>
{
"eth_sendTransaction",
"eth_signTransaction",
"eth_sign",
"personal_sign",
"eth_signTypedData",
"eth_signTypedData_v3",
"eth_signTypedData_v4",
"wallet_watchAsset",
"wallet_addEthereumChain",
"wallet_switchEthereumChain"
};
}
using Nethereum.Metamask;
using Nethereum.UI;
// Platform-specific interop implementation
// In Blazor: MetamaskBlazorInterop
// In other platforms: implement IMetamaskInterop
IMetamaskInterop metamaskInterop = GetPlatformSpecificInterop();
// Create MetaMask provider
var metamaskProvider = new MetamaskHostProvider(metamaskInterop);
// Check if MetaMask is available
bool isAvailable = await metamaskProvider.CheckProviderAvailabilityAsync();
if (!isAvailable)
{
Console.WriteLine("MetaMask extension not detected");
return;
}
// Request connection
string account = await metamaskProvider.EnableProviderAsync();
Console.WriteLine($"Connected to MetaMask: {account}");
// Get Web3 instance
var web3 = await metamaskProvider.GetWeb3Async();
// All transactions will be signed by MetaMask
var balance = await web3.Eth.GetBalance.SendRequestAsync(account);
Console.WriteLine($"Balance: {Web3.Convert.FromWei(balance)} ETH");
using Nethereum.Metamask;
var metamaskProvider = new MetamaskHostProvider(metamaskInterop);
// Subscribe to account changes
metamaskProvider.SelectedAccountChanged += async (newAccount) =>
{
Console.WriteLine($"Account changed to: {newAccount}");
// Update UI, reload balances, etc.
var web3 = await metamaskProvider.GetWeb3Async();
var balance = await web3.Eth.GetBalance.SendRequestAsync(newAccount);
Console.WriteLine($"New balance: {Web3.Convert.FromWei(balance)} ETH");
};
// Subscribe to network changes
metamaskProvider.NetworkChanged += async (chainId) =>
{
Console.WriteLine($"Network changed to chain ID: {chainId}");
if (chainId == 1)
Console.WriteLine("Connected to Ethereum Mainnet");
else if (chainId == 137)
Console.WriteLine("Connected to Polygon");
else
Console.WriteLine($"Connected to unknown network: {chainId}");
};
// Enable provider
await metamaskProvider.EnableProviderAsync();
using Nethereum.Metamask;
using Nethereum.Web3;
using Nethereum.Hex.HexTypes;
var metamaskProvider = new MetamaskHostProvider(metamaskInterop);
await metamaskProvider.EnableProviderAsync();
var web3 = await metamaskProvider.GetWeb3Async();
// Send ETH (MetaMask will prompt for signature)
var toAddress = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb";
var amountInWei = Web3.Convert.ToWei(0.1);
var txHash = await web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync(toAddress, 0.1m);
Console.WriteLine($"Transaction sent: {txHash}");
using Nethereum.Metamask;
using Nethereum.Signer;
var metamaskProvider = new MetamaskHostProvider(metamaskInterop);
await metamaskProvider.EnableProviderAsync();
// Sign message (MetaMask popup will appear)
string message = "Sign this message to authenticate";
string signature = await metamaskProvider.SignMessageAsync(message);
Console.WriteLine($"Signature: {signature}");
// Verify signature
var signer = new EthereumMessageSigner();
var recoveredAddress = signer.EncodeUTF8AndEcRecover(message, signature);
Console.WriteLine($"Recovered address: {recoveredAddress}");
Console.WriteLine($"Matches selected account: {recoveredAddress.Equals(metamaskProvider.SelectedAccount, StringComparison.OrdinalIgnoreCase)}");
using Nethereum.Metamask;
using Nethereum.JsonRpc.Client;
using Nethereum.Web3;
// Custom RPC client for queries (faster, no MetaMask popups)
var customRpcClient = new RpcClient(new Uri("https://mainnet.infura.io/v3/YOUR-PROJECT-ID"));
// MetaMask provider with custom RPC client
// useOnlySigningWalletTransactionMethods = true means:
// - Queries go through custom RPC
// - Signatures go through MetaMask
var metamaskProvider = new MetamaskHostProvider(
metamaskInterop,
client: customRpcClient,
useOnlySigningWalletTransactionMethods: true);
await metamaskProvider.EnableProviderAsync();
var web3 = await metamaskProvider.GetWeb3Async();
// This query goes through Infura (fast, no MetaMask popup)
var blockNumber = await web3.Eth.Blocks.GetBlockNumber.SendRequestAsync();
Console.WriteLine($"Current block: {blockNumber.Value}");
// This transaction goes through MetaMask (user signature required)
var txHash = await web3.Eth.GetEtherTransferService()
.TransferEtherAsync("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", 0.01m);
Console.WriteLine($"Transaction sent via MetaMask: {txHash}");
using Nethereum.Metamask;
using Nethereum.ABI.FunctionEncoding.Attributes;
using Nethereum.Signer.EIP712;
var metamaskProvider = new MetamaskHostProvider(metamaskInterop);
await metamaskProvider.EnableProviderAsync();
var web3 = await metamaskProvider.GetWeb3Async();
// Define typed data
var typedData = new
{
types = new
{
EIP712Domain = new[]
{
new { name = "name", type = "string" },
new { name = "version", type = "string" },
new { name = "chainId", type = "uint256" }
},
Person = new[]
{
new { name = "name", type = "string" },
new { name = "wallet", type = "address" }
}
},
primaryType = "Person",
domain = new
{
name = "MyDApp",
version = "1",
chainId = 1
},
message = new
{
name = "Alice",
wallet = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
}
};
// Sign typed data (MetaMask will show structured data)
var signature = await web3.Eth.AccountSigning.SignTypedDataV4.SendRequestAsync(
System.Text.Json.JsonSerializer.Serialize(typedData));
Console.WriteLine($"EIP-712 Signature: {signature}");
using Nethereum.Metamask;
using Nethereum.Web3;
var metamaskProvider = new MetamaskHostProvider(metamaskInterop);
await metamaskProvider.EnableProviderAsync();
var web3 = await metamaskProvider.GetWeb3Async();
// Prepare token details
var tokenDetails = new
{
type = "ERC20",
options = new
{
address = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
symbol = "USDC",
decimals = 6,
image = "https://example.com/usdc.png"
}
};
// Request to add token to MetaMask
// Note: This requires using the interceptor
var result = await web3.Client.SendRequestAsync<bool>(
"wallet_watchAsset",
null,
tokenDetails);
if (result)
{
Console.WriteLine("Token added to MetaMask successfully");
}
using Nethereum.Metamask;
using Nethereum.Web3;
using Nethereum.Hex.HexTypes;
var metamaskProvider = new MetamaskHostProvider(metamaskInterop);
await metamaskProvider.EnableProviderAsync();
var web3 = await metamaskProvider.GetWeb3Async();
// Switch to Polygon network
var switchParams = new
{
chainId = "0x89" // Polygon chain ID in hex (137 in decimal)
};
try
{
await web3.Client.SendRequestAsync<object>(
"wallet_switchEthereumChain",
null,
new[] { switchParams });
Console.WriteLine("Switched to Polygon network");
}
catch (RpcResponseException ex) when (ex.RpcError.Code == 4902)
{
// Network not added to MetaMask, add it first
var addChainParams = new
{
chainId = "0x89",
chainName = "Polygon Mainnet",
nativeCurrency = new
{
name = "MATIC",
symbol = "MATIC",
decimals = 18
},
rpcUrls = new[] { "https://polygon-rpc.com/" },
blockExplorerUrls = new[] { "https://polygonscan.com/" }
};
await web3.Client.SendRequestAsync<object>(
"wallet_addEthereumChain",
null,
new[] { addChainParams });
Console.WriteLine("Added and switched to Polygon network");
}
public class MetamaskHostProvider : IEthereumHostProvider
{
// Constructor
public MetamaskHostProvider(
IMetamaskInterop metamaskInterop,
IClient client = null,
bool useOnlySigningWalletTransactionMethods = false);
// Properties
public static MetamaskHostProvider Current { get; }
public string Name { get; }
public bool Available { get; }
public string SelectedAccount { get; }
public long SelectedNetworkChainId { get; }
public bool Enabled { get; }
public IClient Client { get; }
// Events
public event Func<string, Task> SelectedAccountChanged;
public event Func<long, Task> NetworkChanged;
public event Func<bool, Task> AvailabilityChanged;
public event Func<bool, Task> EnabledChanged;
// Methods
public Task<bool> CheckProviderAvailabilityAsync();
public Task<string> EnableProviderAsync();
public Task<IWeb3> GetWeb3Async();
public Task<string> GetProviderSelectedAccountAsync();
public Task<string> SignMessageAsync(string message);
// State change methods (called by platform layer)
public Task ChangeSelectedAccountAsync(string selectedAccount);
public Task ChangeSelectedNetworkAsync(long chainId);
public Task ChangeMetamaskAvailableAsync(bool available);
public Task ChangeMetamaskEnabledAsync(bool enabled);
}
public interface IMetamaskInterop
{
Task<string> EnableEthereumAsync();
Task<bool> CheckMetamaskAvailability();
Task<string> GetSelectedAddress();
Task<RpcResponseMessage> SendAsync(RpcRequestMessage rpcRequestMessage);
Task<RpcResponseMessage> SendTransactionAsync(MetamaskRpcRequestMessage rpcRequestMessage);
Task<string> SignAsync(string utf8Hex);
}
public class MetamaskInterceptor : RequestInterceptor
{
public MetamaskInterceptor(
IMetamaskInterop metamaskInterop,
bool useOnlySigningWalletTransactionMethods = false);
public static List<string> SigningWalletTransactionsMethods { get; }
public string SelectedAccount { get; set; }
}
public class MetamaskRpcRequestMessage : RpcRequestMessage
{
public MetamaskRpcRequestMessage(
object id,
string method,
string from,
params object[] parameterList);
public string From { get; }
}
This package provides abstractions only. You must implement IMetamaskInterop for your platform:
IMetamaskInterop with embedded browser controlIMetamaskInterop with WebGL or mobile wallet integration// Mode 1: All requests through MetaMask (default)
var provider = new MetamaskHostProvider(interop);
// Queries and transactions both use MetaMask
// Slower but everything goes through wallet
// Mode 2: Signing only through MetaMask (hybrid)
var provider = new MetamaskHostProvider(
interop,
client: customRpcClient,
useOnlySigningWalletTransactionMethods: true);
// Queries use custom RPC (faster)
// Transactions use MetaMask (secure)
The MetamaskInterceptor automatically routes these methods through MetaMask:
eth_sendTransaction - Sends transactions (user approval required)eth_signTransaction - Signs transactions without sendingeth_sign - Personal message signingpersonal_sign - EIP-191 personal message signingeth_signTypedData - Typed data signing (EIP-712)eth_signTypedData_v3 - Typed data signing v3eth_signTypedData_v4 - Typed data signing v4 (recommended)wallet_watchAsset - Add token to MetaMaskwallet_addEthereumChain - Add custom networkwallet_switchEthereumChain - Switch networkAll other methods (queries like eth_call, eth_getBalance, etc.) use the configured RPC client.
The interceptor automatically injects the selected MetaMask account into transactions:
var web3 = await provider.GetWeb3Async();
// No need to specify 'from' - automatically uses MetaMask account
await web3.Eth.GetEtherTransferService()
.TransferEtherAsync(toAddress, amount);
For convenience, the last created MetamaskHostProvider is available via:
var provider = new MetamaskHostProvider(interop);
// Later, anywhere in code:
var currentProvider = MetamaskHostProvider.Current;
MetaMask interactions can fail for various reasons:
try
{
var txHash = await web3.Eth.GetEtherTransferService()
.TransferEtherAsync(toAddress, amount);
}
catch (RpcResponseException ex)
{
// User rejected transaction
if (ex.RpcError.Code == 4001)
{
Console.WriteLine("User rejected the transaction");
}
// Insufficient funds
else if (ex.RpcError.Message.Contains("insufficient funds"))
{
Console.WriteLine("Insufficient funds");
}
else
{
Console.WriteLine($"Transaction failed: {ex.Message}");
}
}
On .NET Core 3.1+, IMetamaskInterop uses ValueTask<T> for better performance. On earlier frameworks, it uses Task<T>.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 is compatible. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 was computed. |
| .NET Framework | net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 is compatible. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
Showing the top 3 NuGet packages that depend on Nethereum.Metamask:
| Package | Downloads |
|---|---|
|
Nethereum.Metamask.Blazor
Nethereum.Metamask.Blazor Nethereum Metamask integration with Blazor including Control and EthereumAuthenticationProvider |
|
|
Nethereum.Blazor
Nethereum.Blazor Nethereum integration with Blazor including EIP6963WalletInterop and EthereumAuthenticationProvider |
|
|
Conclave.Nethereum.Metamask.Blazor
Nethereum.Metamask.Blazor Nethereum Metamask integration with Blazor including Control and EthereumAuthenticationProvider |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 6.1.0 | 1,539 | 3/25/2026 |
| 6.0.4 | 265 | 3/18/2026 |
| 6.0.3 | 198 | 3/18/2026 |
| 6.0.1 | 247 | 3/17/2026 |
| 6.0.0 | 224 | 3/16/2026 |
| 5.8.0 | 670 | 1/6/2026 |
| 5.0.0 | 989 | 5/28/2025 |
| 4.29.0 | 482 | 2/10/2025 |
| 4.28.0 | 419 | 1/7/2025 |
| 4.27.1 | 289 | 12/24/2024 |
| 4.27.0 | 271 | 12/24/2024 |
| 4.26.0 | 500 | 10/1/2024 |
| 4.25.0 | 365 | 9/19/2024 |
| 4.21.4 | 369 | 8/9/2024 |
| 4.21.3 | 471 | 7/22/2024 |
| 4.21.2 | 485 | 6/26/2024 |
| 4.21.1 | 290 | 6/26/2024 |
| 4.21.0 | 650 | 6/18/2024 |
| 4.20.0 | 2,076 | 3/28/2024 |
| 4.19.0 | 640 | 2/16/2024 |