Skip to main content

WebGL Wallet Connection

After setting up Nethereum in Unity, you'll want to connect browser wallets in WebGL builds. This guide covers discovering all installed wallets via EIP-6963 (the modern approach), falling back to MetaMask directly, handling account and network change events, and getting a Web3 instance from the connected wallet.

All wallet code in this guide only runs in WebGL builds — use #if UNITY_WEBGL conditional compilation to separate WebGL and desktop code paths.

EIP-6963 is the modern standard for discovering browser wallets. Instead of relying on a single window.ethereum object (which only works if one wallet is installed), EIP-6963 lets all installed wallets announce themselves. Users choose which wallet to connect.

Discover and Connect

The core workflow is: create the provider singleton, discover wallets, let the user select one, then enable it:

using UnityEngine;
using Nethereum.Unity.EIP6963;
using Nethereum.EIP6963WalletInterop;

public class WalletConnect : MonoBehaviour
{
private EIP6963WalletHostProvider walletProvider;

async void Start()
{
// Create singleton — initializes browser-side EIP-6963 event listeners
walletProvider = EIP6963WebglHostProvider.CreateOrGetCurrentInstance();

// Discover all installed wallets
var wallets = await walletProvider.GetAvailableWalletsAsync();
foreach (var wallet in wallets)
Debug.Log($"Found: {wallet.Name} ({wallet.Rdns})");

// Select first wallet and connect
if (wallets.Length > 0)
{
await walletProvider.SelectWalletAsync(wallets[0].Uuid);
var account = await walletProvider.EnableProviderAsync();
Debug.Log($"Connected: {account}");
}
}
}

CreateOrGetCurrentInstance() is a singleton — calling it multiple times returns the same instance. The constructor calls EIP6963_InitEIP6963() in JavaScript, which sets up browser event listeners for eip6963:announceProvider events.

Build a Wallet Selection UI

In a real game, you'd show buttons for each discovered wallet so the user can choose. Each wallet provides a name, icon (as a base64 data URI), UUID, and reverse DNS identifier:

public async void ShowWalletButtons()
{
var wallets = await walletProvider.GetAvailableWalletsAsync();

for (int i = 0; i < wallets.Length; i++)
{
// Create a button for each wallet
var button = Instantiate(walletButtonPrefab, walletListPanel);
button.GetComponentInChildren<Text>().text = wallets[i].Name;

// Wallet icons come as data URIs (data:image/png;base64,...)
// Convert to Texture2D for display
Image[] images = button.GetComponentsInChildren<Image>();
foreach (var image in images)
{
if (image.gameObject.name == "Icon")
ImageHelper.ApplyBase64Image(image, wallets[i].Icon);
}

// Capture UUID for the click handler
string uuid = wallets[i].Uuid;
button.GetComponent<Button>().onClick.AddListener(
() => SelectWallet(uuid));
}
}

async void SelectWallet(string uuid)
{
await walletProvider.SelectWalletAsync(uuid);
var account = await walletProvider.EnableProviderAsync();
Debug.Log($"Connected: {account}");
}

EIP6963WalletInfo has four properties: Name (e.g., "MetaMask"), Uuid (unique identifier), Icon (base64 data URI), and Rdns (reverse DNS like "io.metamask"). SVG icons are not directly supported by Unity's Image component — you'll need a conversion helper for those.

Get Web3 and Use Full API

Once connected, get a Web3 instance from the wallet provider. This gives you the full Nethereum API — the wallet handles signing:

var web3 = await walletProvider.GetWeb3Async();

// All standard web3.Eth.* calls work — wallet signs transactions
var balance = await web3.Eth.GetBalance.SendRequestAsync(
walletProvider.SelectedAccount);
Debug.Log($"Balance: {Web3.Convert.FromWei(balance.Value)} ETH");

// Transfer ETH
var receipt = await web3.Eth.GetEtherTransferService()
.TransferEtherAndWaitForReceiptAsync("0xRecipient", 0.01m);

// ERC-20 queries
var erc20 = web3.Eth.ERC20.GetContractService(tokenAddress);
var tokenBalance = await erc20.BalanceOfQueryAsync(walletProvider.SelectedAccount);

The Web3 instance routes all signing RPC methods (eth_sendTransaction, personal_sign, eth_signTypedData_v4) through the selected browser wallet. Read-only calls like eth_call and eth_getBalance go through the wallet's provider.

Listen to Account and Network Changes

Users can switch accounts or networks in their wallet at any time. The events use Func<T, Task> delegates — handlers must return Task:

void OnEnable()
{
walletProvider.SelectedAccountChanged += OnAccountChanged;
walletProvider.NetworkChanged += OnNetworkChanged;
}

void OnDisable()
{
walletProvider.SelectedAccountChanged -= OnAccountChanged;
walletProvider.NetworkChanged -= OnNetworkChanged;
}

private Task OnAccountChanged(string newAccount)
{
Debug.Log($"Account switched: {newAccount}");
return Task.CompletedTask;
}

private Task OnNetworkChanged(long newChainId)
{
Debug.Log($"Network switched: {newChainId}");
return Task.CompletedTask;
}

There are also AvailabilityChanged and EnabledChanged events if you need to react to the wallet becoming unavailable.

MetaMask Direct (Fallback)

For MetaMask-only support, or as a fallback when EIP-6963 wallets aren't detected, use MetamaskWebglHostProvider:

using UnityEngine;
using Nethereum.Unity.Metamask;

public class MetamaskConnect : MonoBehaviour
{
async void Start()
{
var metamask = MetamaskWebglHostProvider.CreateOrGetCurrentInstance();
await metamask.EnableProviderAsync();
var web3 = await metamask.GetWeb3Async();

var balance = await web3.Eth.GetBalance.SendRequestAsync(
metamask.SelectedAccount);
Debug.Log($"Balance: {balance.Value}");
}
}

Both MetamaskWebglHostProvider and EIP6963WebglHostProvider implement IEthereumHostProvider, so your game logic can work with either through the interface.

When to Use Which

ApproachUse When
EIP-6963Modern multi-wallet support — discovers MetaMask, Rainbow, Coinbase, and any EIP-6963 wallet
MetaMask DirectLegacy support or when you specifically need MetaMask's window.ethereum API
BothDiscover via EIP-6963 first, fall back to MetaMask if no EIP-6963 wallets found

Message Signing

Both providers support personal message signing through the connected wallet:

var web3 = await walletProvider.GetWeb3Async();
var signature = await web3.Eth.AccountSigning.PersonalSign
.SendRequestAsync(new HexUTF8String("Hello from Unity!"));
Debug.Log($"Signature: {signature}");

The wallet will show a signing prompt to the user — no private key leaves the browser.

Next Steps