Skip to main content

Connecting Browser Wallets

Nethereum provides three ways to connect browser wallets from Blazor. EIP-6963 is the recommended approach -- it discovers all installed wallet extensions automatically. MetaMask direct integration and WalletConnect/Reown are available for specific use cases.

EIP-6963 replaces the legacy window.ethereum injection model. All installed wallet extensions (MetaMask, Coinbase Wallet, Rabby, etc.) announce themselves, and your app presents them for the user to choose.

Setup​

dotnet add package Nethereum.Blazor

Include the JS interop script in index.html or App.razor:

<script src="_content/Nethereum.Blazor/NethereumEIP6963.js"></script>

Register services in Program.cs:

using Nethereum.Blazor;
using Nethereum.Blazor.EIP6963WalletInterop;
using Nethereum.EIP6963WalletInterop;
using Nethereum.UI;
using Microsoft.AspNetCore.Components.Authorization;

builder.Services.AddSingleton<IEIP6963WalletInterop, EIP6963WalletBlazorInterop>();
builder.Services.AddSingleton<EIP6963WalletHostProvider>();
builder.Services.AddSingleton<IEthereumHostProvider>(sp =>
sp.GetRequiredService<EIP6963WalletHostProvider>());
builder.Services.AddSingleton<SelectedEthereumHostProviderService>();
builder.Services.AddScoped<AuthenticationStateProvider, EthereumAuthenticationStateProvider>();
builder.Services.AddAuthorizationCore();

Discover and Connect​

@inject IEIP6963WalletInterop WalletInterop
@inject EIP6963WalletHostProvider WalletProvider
@inject AuthenticationStateProvider AuthStateProvider

@if (wallets != null)
{
@foreach (var wallet in wallets)
{
<button @onclick="() => Connect(wallet)">
@if (!string.IsNullOrEmpty(wallet.Icon))
{
<img src="@wallet.Icon" alt="@wallet.Name" width="24" />
}
@wallet.Name
</button>
}
}

@code {
private EIP6963WalletInfo[] wallets;

protected override async Task OnInitializedAsync()
{
await Task.Delay(500);
wallets = await WalletInterop.GetAvailableWalletsAsync();
}

private async Task Connect(EIP6963WalletInfo wallet)
{
await WalletInterop.SelectWalletAsync(wallet.Uuid);
var address = await WalletProvider.EnableProviderAsync();

if (!string.IsNullOrEmpty(address) &&
AuthStateProvider is EthereumAuthenticationStateProvider ethAuth)
{
await ethAuth.NotifyAuthenticationStateAsEthereumConnected();
}
}
}

EIP-6963 wallet discovery detecting MetaMask, Brave, and Coinbase wallets in Blazor Server

Get Web3 After Connection​

Once connected through any provider, get a Web3 instance for RPC calls:

var web3 = await WalletProvider.GetWeb3Async();
var balance = await web3.Eth.GetBalance.SendRequestAsync(WalletProvider.SelectedAccount);

MetaMask Direct Integration​

For apps that only need MetaMask support:

dotnet add package Nethereum.Metamask.Blazor

Add the JS interop script:

<script src="_content/Nethereum.Metamask.Blazor/NethereumMetamask.js"></script>

Register services:

using Nethereum.Metamask;
using Nethereum.Metamask.Blazor;

builder.Services.AddSingleton<IMetamaskInterop, MetamaskBlazorInterop>();
builder.Services.AddSingleton<MetamaskHostProvider>();
builder.Services.AddSingleton<IEthereumHostProvider>(sp =>
sp.GetRequiredService<MetamaskHostProvider>());
builder.Services.AddSingleton<SelectedEthereumHostProviderService>();

Connect and use:

@inject MetamaskHostProvider MetamaskProvider

var address = await MetamaskProvider.EnableProviderAsync();
var web3 = await MetamaskProvider.GetWeb3Async();

WalletConnect / Reown AppKit​

For mobile wallet support via QR codes and the full Reown modal UI:

dotnet add package Nethereum.Reown.AppKit.Blazor

Register services with your Reown Project ID:

using Nethereum.Reown.AppKit.Blazor;

builder.Services.AddAppKit(new AppKitConfiguration
{
Networks = NetworkConstants.Networks.All,
ProjectId = "YOUR-REOWN-PROJECT-ID",
Name = "My Dapp",
Description = "My decentralized application",
Url = builder.HostEnvironment.BaseAddress,
Icons = ["https://my-dapp.com/icon.png"],
ThemeMode = ThemeModeOptions.dark,
Swaps = true,
Onramp = true
});

Use the built-in web components in your Razor page:

@using Nethereum.Reown.AppKit.Blazor
@inject IEthereumHostProvider ethereumHostProvider

<appkit-button />
<appkit-network-button />

@code {
protected override async Task OnInitializedAsync()
{
ethereumHostProvider.SelectedAccountChanged += async (address) =>
{
await InvokeAsync(StateHasChanged);
};
}
}

Listening for Account and Network Changes​

All providers implement IEthereumHostProvider with the same event model:

@inject IEthereumHostProvider Provider
@implements IDisposable

<p>Account: @Provider.SelectedAccount</p>
<p>Chain: @Provider.SelectedNetworkChainId</p>

@code {
protected override void OnInitialized()
{
Provider.SelectedAccountChanged += OnAccountChanged;
Provider.NetworkChanged += OnNetworkChanged;
}

private async Task OnAccountChanged(string address)
{
await InvokeAsync(StateHasChanged);
}

private async Task OnNetworkChanged(long chainId)
{
await InvokeAsync(StateHasChanged);
}

public void Dispose()
{
Provider.SelectedAccountChanged -= OnAccountChanged;
Provider.NetworkChanged -= OnNetworkChanged;
}
}

Next Steps​