Tutorial: MetaMask Snaps – What Are They and How to Use Them
A step-by-step tutorial on how to integrate the Hedera Wallet Snap by MetaMask into a dApp.
Introduction
MetaMask is a widely used Ethereum wallet and browser extension – MetaMask Snaps is an open-source solution designed to enhance the capabilities of this wallet. Snaps are created by developers using JavaScript and enable users to interact with various blockchains, protocols, and decentralized applications (dApps) that MetaMask does not natively support. To learn more about Snaps, visit the MetaMask Snap Guide.
The Hedera Wallet Snap, developed by Tuum Tech and managed by Hashgraph, enables users to interact directly with the Hedera network. It offers functionalities like sending HBAR to different accounts and retrieving account information.
What You Will Learn
This tutorial will demonstrate how dApp builders and developers can seamlessly integrate and utilize the Hedera Wallet Snap in their applications. You will learn how to:
Pair dApp with MetaMask
Install the Hedera Wallet Snap
Get the Snap Ethereum Virtual Machine (EVM) address
Create the Snap account and check its balance
Call other methods in the Hedera Wallet Snap, like:
transferCrypto
Tools You Will Use
React JS (Documentation)
MetaMask (Documentation)
Hedera Wallet Snap for MetaMask (Documentation)
Hedera JSON-RPC Relay (Hashio)
Ethers JS (Documentation)
Mirror Node REST API (Learn More)
Mirror Node Explorer (HashScan)
Prerequisites
NodeJS >= 18.13 (Download)
TypeScript >= 4.7 (Download)
Git Command Line (Download)
Hedera Testnet Account (Create)
MetaMask Wallet Extension (Download)
Get Familiar with the dApp Structure and UI
Explore the project files and functions and get a feel for how the sample dApp looks and functions. This will make it easier to follow along as you dive into the technical aspects of the dApp.
For convenience, two options are provided to run the code used in this example:
Project Structure
The project folder structure should look something like the following.
Overall dApp Structure
The example application has four buttons, which complete different tasks when pressed.
The first button connects the application to MetaMask.
The second button installs the Hedera Wallet Snap.
The third button obtains information about the Snap account.
The fourth button (and respective input boxes) uses the Hedera Wallet Snap to transfer HBAR.
Now let’s look at the App.jsx file (inside the src folder) behind this UI. You can think of the code as three main sections (in addition to the imports):
The state management is the part that uses the useState() React Hook.
The functions that are executed with each button press; we’ll look at these in the next few sections.
The return statement groups the elements we see on the page.
The useState() hook helps store information about the state of the application. In this case, we store information like the snapId, wallet data, the account that is connected, the network, and the receiver address and HBAR amount, along with text and links presented in the UI. Remember that the first output of useState() is the variable of interest (e.g., walletData), and the second output is a function to set a new value for that variable (e.g., setWalletData).
Understanding the React Components
The buttons, text, and input boxes in the UI are a combination of React components in the return statement of App.jsx. Working with components, we take advantage of React's composability for better organization and readability. The properties for each component instance – like the function that each button executes, the label of the button, the text above the button, and the link for that text – are customized using React props.
Step 1: Pair MetaMask Wallet (select network and account)
In App.jsx, we use the connectWallet() function (code tab 1), which in turn calls the walletConnectFcn() function (code tab 2) that is imported from the file src/components/hedera/walletConnect.js.
When the Connect Wallet button is pressed in the dApp, the connectWallet() function is executed. This function checks if an account is already connected. If it is, a message displaying the connected account is shown. If no account is connected, the walletConnectFcn() is called to establish a connection.
The walletConnectFcn() function performs the following steps:
Creates an ethers provider: It initializes an ethers provider using the Web3Provider from the ethers library, which connects to MetaMask. An Ethers provider serves as a bridge between your application and the Hedera network. It allows you to perform actions like sending transactions and querying data.
Switches to Hedera Testnet: It determines the chainId based on the chosen Hedera network (testnet, previewnet, or mainnet) and sends a wallet_addEthereumChain request to MetaMask to add the corresponding Hedera network. A chain ID is a unique identifier that represents a blockchain network. This is an important step that includes setting the native currency (HBAR) and providing the JSON-RPC and network explorer URLs. For the JSON-RPC provider, this example uses Hashio, a community-hosted JSON-RPC relay provided by Hashgraph (note that anyone can host their own relay and/or use other commercial providers, like Arkhia). For network explorer, HashScan is used. (Keep in mind that HashScan supports EIP-3091, which makes it easy to explore historical data like blocks, transactions, accounts, contracts, and tokens from wallets like MetaMask).
Connects and Pairs Account: The function sends an eth_requestAccounts request to MetaMask to access the user's Hedera account. Upon successful connection, the selected account is returned.
Finally, the connectWallet() function in App.jsx updates the React state with the connected testnet account and provider information, allowing the dApp to display the connected testnet account information.
This is what you see when clicking the Connect Wallet button for the first time.
Note: The Hedera account selected in MetaMask for this example has ECDSA keys and is created using the Hedera Portal.
Once the network switches and the account is paired, you should see something like the following in the dApp UI and in HashScan (if you click on the hyperlinked text showing the account address).
Step 2: Install the Hedera Wallet Snap
The snapInstall() function (code tab 1) in App.jsx calls the snapInstallFcn() function (code tab 2). The latter is imported from the file src/components/hedera/snapInstall.js.
The snapInstallFcn() function performs the following steps:
Gets Installed Snaps: The function requests a list of installed Snaps in the user's MetaMask wallet using the wallet_getSnaps method. It logs the currently installed Snaps.
Installs Snaps: The function checks if the Hedera Wallet Snap (identified by snapId) is already installed. If it's not installed, the function attempts to install it by calling wallet_requestSnaps and passing the snapId. It then logs the result of this installation attempt. After attempting installation, it checks again for installed Snaps.
Handles Installation Outcome: If the Snap is successfully installed (i.e., snapId is found in the list of installed snaps), it logs and alerts the user that the Snap installation was successful. If the installation fails (i.e., snapId is not found in the list), it logs and alerts the user that the connection could not be established successfully.
Returns Result: Lastly, the function returns a text message indicating whether the Snap was installed successfully.
The text output from snapInstallFcn() is used by snapInstall() in the front end of the application to update the message seen by the user.
This is what you see when clicking the Install Snap button for the first time.
Step 3: Get the Snap EVM Address
Before we can start using the functionality of the Hedera Wallet Snap, this step obtains the Snap EVM address. In the next step, we send HBAR to that Snap EVM address to create the corresponding Snap account.
These steps are necessary because Snaps provide a way to create a secure and unique wallet associated with a user's MetaMask account without directly accessing the private key of the account. This separate Snap account is unique to the user's MetaMask account and remains accessible across different browsers or devices as long as the secret recovery phrase remains the same. For more information, read this section of the Hedera Wallet Snap documentation.
The snapGetAccountInfo() function (code tab 1) in App.jsx calls the snapGetAccountInfoFcn() function (code tab 2). The latter is imported from the file src/components/hedera/ snapGetAccountInfo.js.
The snapGetAccountInfoFcn() function performs the following steps:
Requests Account Information: The function makes a request to MetaMask using the wallet_invokeSnap method. It sends the snapId and a request for the getAccountInfo method, along with the network details and the URL of the Hedera mirror node. For more details on this method, read this section of the Hedera Wallet Snap documentation.
Processes the Response: If the request is successful, the function extracts the EVM address of the Snap account and its balance in HBAR from the response. It then constructs a message stating the account's EVM address and balance.
Handles Errors: If there's an error (like the account not being created yet), the function extracts the EVM address from the error message. It then creates a message instructing the user to transfer HBAR to this address to create the Snap account.
Returns Results: The function logs the outcome message (either the account details or the activation instruction). It then returns the EVM address of the Snap account and the outcome message.
The outputs from snapGetAccountInfoFcn() are used by snapGetAccountInfo() in the front end of the application to update the message and link seen by the user.
This is what you see when clicking the Get Snap Account Info button for the first time.
Note: When invoking a Snap method for the first time, MetaMask checks for confirmation before connecting the Hedera Wallet Snap to the account.
Once the Snap is connected to the MetaMask account, you should see something like the following in the dApp UI and in HashScan (if you click on the hyperlinked text showing the Snap account address).
Step 4: Create Snap Account and Check Its Balance
We now know the EVM address for the Snap account. However, this Snap account has not yet been created on the Hedera network (testnet in this case). It’s time to send HBAR to that address to create the actual Hedera account for the snap.
Perform a transfer of 10 HBAR to the Snap address. In this case, the Snap address is: 0xa7deaf8acd6be555740f9672dff34f510480f0c9
Note: The Hedera account selected in MetaMask for this example has ECDSA keys and is created using the Hedera Developer Portal.
Once the transfer is complete, check the balance of the Snap account by clicking on the Get Snap Account Info button again. You should see something like the following in the dApp UI and in HashScan (if you click on the hyperlinked text showing the snap account address and its balance).
Step 5: Call Other Methods in the Hedera Wallet Snap
The last step in this exercise is to try other methods available in the Hedera Wallet Snap. As of version 0.1.3
of the Snap, the transferCrypto method enables transferring HBAR to other Hedera accounts from MetaMask. In this case, we send 0.2 HBAR from the Snap account to the MetaMask account. Let’s see how this is done in the code.
The snapTransferHbar() function (code tab 1) in App.jsx calls the snapTransferHbarFcn() function (code tab 2). The latter is imported from the file src/components/hedera/snapTransferHbar.js.
The snapTransferHbarFcn() function performs the following steps:
Sets Transfer Details: It extracts the recipient's address and the amount of HBAR to be transferred from the arguments (args) provided. Note that these inputs are specified in the dApp UI – remember that the receiver address is that of the MetaMask account and the transfer amount is 0.2 HBAR. The function sets a maximum fee for the transaction, which is predefined as 0.05 HBAR in the code. It also prepares the transfer details, including the asset type (HBAR), the receiver's address, and the amount.
Executes Transfer Request: The function sends a wallet_invokeSnap request. This request includes the snapId, the transferCrypto method, and parameters such as the network, the transfer details, a memo (may be needed for transferring to exchange accounts), and the maximum fee.
Handles Transfer Outcome: If the transfer is successful, the function sets a message indicating success and suggests re-checking the Snap account info for an updated balance. In case of an error or failure, it sets a different message indicating that the transaction failed and prompts trying again.
Returns Output: Finally, the function returns the outcome message, informing whether the transfer was successful or not.
The text output from snapTransferHbarFcn() is used by snapTransferHbar() in the front end of the application to update the message seen by the user.
This is what you see when clicking the Transfer HBAR w/ Snap button after entering a valid receiver address and HBAR amount in the corresponding input fields.
Once the transfer of HBAR from the Snap account to the MetaMask account is complete, you can check the Snap account balance by clicking on the Get Snap Account Info button again. You will see something like this:
Summary
This tutorial provides a comprehensive guide on how to use MetaMask Snaps, focusing on the Hedera Wallet Snap.
It starts by covering the dApp's structure and user interface, highlighting the functions of various buttons like connecting to MetaMask, installing the Hedera Wallet Snap, obtaining Snap account information, and transferring HBAR using the Snap.
The tutorial also dives into the technical aspects, detailing the App.jsx file, which includes state management, functions for button actions, and the layout of the UI components. It explains the use of React components like MyGroup, MyButton, and MyInputBox to create an interactive and user-friendly interface.
It then elaborates on MetaMask Snaps, its purpose, and how to use them, specifically focusing on the Hedera Wallet Snap for tasks like pairing the dApp with MetaMask, installing the Snap, managing the Snap account, and executing crypto transfers.
🎉 Congratulations! You have learned about the new Hedera Wallet Snap by MetaMask and how to integrate it into a dApp. Feel free to reach out in Discord if you have any questions!
Additional Resources
➡ Hedera JSON-RPC Relay Repository
➡ Ask Questions on StackOverflow
Last updated