Nethereum.Metamask
5.8.0
Prefix Reserved
dotnet add package Nethereum.Metamask --version 5.8.0
NuGet\Install-Package Nethereum.Metamask -Version 5.8.0
<PackageReference Include="Nethereum.Metamask" Version="5.8.0" />
<PackageVersion Include="Nethereum.Metamask" Version="5.8.0" />
<PackageReference Include="Nethereum.Metamask" />
paket add Nethereum.Metamask --version 5.8.0
#r "nuget: Nethereum.Metamask, 5.8.0"
#:package Nethereum.Metamask@5.8.0
#addin nuget:?package=Nethereum.Metamask&version=5.8.0
#tool nuget:?package=Nethereum.Metamask&version=5.8.0
Nethereum.Metamask
Core MetaMask integration abstractions for Nethereum. Provides platform-agnostic interfaces and request interceptors for integrating MetaMask wallet into .NET applications.
Overview
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:
IMetamaskInteropinterface for platform-specific implementationsMetamaskHostProviderimplementingIEthereumHostProviderMetamaskInterceptorfor routing RPC requests through MetaMask- Support for all MetaMask-specific methods (personal_sign, eth_signTypedData_v4, wallet_*)
- Automatic account injection into transactions
- Two operating modes: all requests or signing-only
Use Cases:
- Building Blazor dApps with MetaMask
- Creating custom MetaMask integrations for other platforms
- Routing transaction signing through MetaMask while using custom RPC
- Implementing wallet connection flows
Installation
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.
Dependencies
- Nethereum.UI
- Nethereum.Web3
Architecture
Platform Implementations
┌─────────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 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) │
└─────────────────────────────────────────────────────────────┘
IMetamaskInterop
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);
}
MetamaskHostProvider
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);
}
MetamaskInterceptor
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"
};
}
Usage Examples
Example 1: Basic Setup (Requires Platform Implementation)
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");
Example 2: Listening to Account Changes
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();
Example 3: Sending Transactions Through MetaMask
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}");
Example 4: Signing Messages with MetaMask
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)}");
Example 5: Hybrid Mode - MetaMask for Signing, Custom RPC for Queries
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}");
Example 6: Typed Data Signing (EIP-712)
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}");
Example 7: Adding Token to MetaMask (wallet_watchAsset)
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");
}
Example 8: Switching Networks in MetaMask
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");
}
API Reference
MetamaskHostProvider
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);
}
IMetamaskInterop
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);
}
MetamaskInterceptor
public class MetamaskInterceptor : RequestInterceptor
{
public MetamaskInterceptor(
IMetamaskInterop metamaskInterop,
bool useOnlySigningWalletTransactionMethods = false);
public static List<string> SigningWalletTransactionsMethods { get; }
public string SelectedAccount { get; set; }
}
MetamaskRpcRequestMessage
public class MetamaskRpcRequestMessage : RpcRequestMessage
{
public MetamaskRpcRequestMessage(
object id,
string method,
string from,
params object[] parameterList);
public string From { get; }
}
Important Notes
Platform-Specific Implementation Required
This package provides abstractions only. You must implement IMetamaskInterop for your platform:
- Blazor: Use Nethereum.Metamask.Blazor (provides JavaScript interop)
- Desktop/Mobile: Implement
IMetamaskInteropwith embedded browser control - Unity: Implement
IMetamaskInteropwith WebGL or mobile wallet integration
Two Operating Modes
// 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)
Automatically Handled Methods
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 network
All other methods (queries like eth_call, eth_getBalance, etc.) use the configured RPC client.
Account Injection
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);
Static Current Property
For convenience, the last created MetamaskHostProvider is available via:
var provider = new MetamaskHostProvider(interop);
// Later, anywhere in code:
var currentProvider = MetamaskHostProvider.Current;
Error Handling
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}");
}
}
ValueTask Support
On .NET Core 3.1+, IMetamaskInterop uses ValueTask<T> for better performance. On earlier frameworks, it uses Task<T>.
Related Packages
Platform Implementations
- Nethereum.Metamask.Blazor - Blazor WebAssembly implementation with JavaScript interop
Dependencies
- Nethereum.UI - Ethereum host provider abstractions
- Nethereum.Web3 - Web3 client and RPC functionality
Related UI Packages
- Nethereum.Blazor - Blazor components and services
- Nethereum.EIP6963WalletInterop - Multi-wallet discovery
- Nethereum.WalletConnect - WalletConnect integration
- Nethereum.Reown.AppKit.Blazor - Reown AppKit integration
Additional Resources
| 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 | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. 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.7.2
- Nethereum.UI (>= 5.8.0)
- Nethereum.Web3 (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
.NETStandard 2.0
- Nethereum.UI (>= 5.8.0)
- Nethereum.Web3 (>= 5.8.0)
- NETStandard.Library (>= 2.0.3)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
net6.0
- Nethereum.UI (>= 5.8.0)
- Nethereum.Web3 (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
net8.0
- Nethereum.UI (>= 5.8.0)
- Nethereum.Web3 (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
-
net9.0
- Nethereum.UI (>= 5.8.0)
- Nethereum.Web3 (>= 5.8.0)
- Newtonsoft.Json (>= 11.0.2 && < 14.0.0)
NuGet packages (3)
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 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.8.0 | 305 | 1/6/2026 |
| 5.0.0 | 936 | 5/28/2025 |
| 4.29.0 | 445 | 2/10/2025 |
| 4.28.0 | 386 | 1/7/2025 |
| 4.27.1 | 255 | 12/24/2024 |
| 4.27.0 | 238 | 12/24/2024 |
| 4.26.0 | 463 | 10/1/2024 |
| 4.25.0 | 322 | 9/19/2024 |
| 4.21.4 | 336 | 8/9/2024 |
| 4.21.3 | 433 | 7/22/2024 |
| 4.21.2 | 452 | 6/26/2024 |
| 4.21.1 | 257 | 6/26/2024 |
| 4.21.0 | 618 | 6/18/2024 |
| 4.20.0 | 1,756 | 3/28/2024 |
| 4.19.0 | 609 | 2/16/2024 |
| 4.18.0 | 679 | 11/21/2023 |
| 4.17.1 | 417 | 9/28/2023 |
| 4.17.0 | 259 | 9/27/2023 |
| 4.16.0 | 380 | 8/14/2023 |
| 4.15.2 | 485 | 7/11/2023 |