Hedera
  • Welcome to Hedera — let’s build the future
  • Getting Started
    • Environment Setup
    • Web2 Developers
      • Transfer HBAR
      • Create a Token
      • Create a Topic
    • EVM Developers
      • Deploy a Contract
  • Tutorials
    • Smart Contracts
      • How to Mint & Burn an ERC-721 Token Using Hardhat and Ethers (Part 1)
      • How to Set Access Control, a Token URI, Pause, and Transfer an ERC-721 Token Using Hardhat (Part 2)
      • How to Upgrade an ERC-721 Token with OpenZeppelin UUPS Proxies and Hardhat (Part 3)
      • How to Verify a Smart Contract on HashScan
      • Deploy a Smart Contract Using Remix
      • Deploy a Smart Contract Using Hardhat and Hiero JSON-RPC Relay
      • Deploy Your First Smart Contract
      • Deploy a Contract Using the Hedera Token Service
      • Send and Receive HBAR Using Solidity Smart Contracts
      • Deploy By Leveraging Ethereum Developer Tools On Hedera
      • Deploy a Subgraph Using The Graph and Hedera JSON-RPC Relay
      • Deploy Smart Contracts on Hedera Using Truffle
      • The Power of Native Hedera Tokens as ERC-20 Tokens: A step-by-step guide
      • HTS x EVM - Part 1: How to Mint NFTs
      • HTS x EVM - Part 2: KYC & Update
      • HTS x EVM - Part 3: How to Pause, Freeze, Wipe, and Delete NFTs
      • Hedera Smart Contracts Workshop
        • Setup
        • Solidity
        • Hedera SDK JS
        • Hardhat and EthersJs
        • Outro
      • Foundry
        • How to Setup Foundry and Write a Basic Unit Test
        • How to Deploy and Verify a Hedera Smart Contract with Foundry
        • How to Test A Solidity Event
        • How to Fork Testnet on Latest Block
    • Consensus
      • Submit Your First Message
      • Submit Message to Private Topic
      • Query Messages with Mirror Node
    • Tokens
      • Create and Transfer Your First NFT
      • Create and Transfer Your First Fungible Token
      • Create and Transfer an NFT using a Solidity Contract
      • Structure Your Token Metadata Using JSON Schema V2
      • Hedera Token Service - Part 1: How to Mint NFTs
      • Hedera Token Service - Part 2: KYC, Update, and Scheduled Transactions
      • Hedera Token Service - Part 3: How to Pause, Freeze, Wipe, and Delete NFTs
      • Create Your First Frictionless Airdrop Campaign
    • Local Node
      • How to Run Hedera Local Node in a Cloud Development Environment (CDE)
        • Run a Local Node in Gitpod
        • Run a Local Node in Codespaces
      • How to Set Up a Hedera Local Node
      • Set Up a Hedera Local Node using the NPM CLI
    • More Tutorials
      • Create and Fund Your Hedera Testnet Account
      • How to Create a Personal Access Token (API Key) on the Hedera Portal
      • How to Auto-Create Hedera Accounts with HBAR and Token Transfers
      • How to Configure a Mirror Node and Query Data
      • How to Generate a Random Number on Hedera
      • Get Started with the Hedera Consensus Service Fabric Plugin
        • Virtual Environment Setup
      • Schedule Your First Transaction
      • How to Connect to Hedera Networks Over RPC
        • Configuring Hashio RPC endpoints
        • Configuring Hiero JSON-RPC Relay endpoints
        • Configuring Validation Cloud RPC endpoints
      • JavaScript Testing
      • Create a Hedera DApp Integrated with WalletConnect
      • How to Connect MetaMask to Hedera
    • Demo Applications
    • Starter Projects
    • Building on Hedera (course)
  • Networks
    • Mainnet
      • Mainnet Accounts
      • Mainnet Consensus Nodes
        • Node Requirements
          • FAQ
      • Fees
        • Transaction Records
    • Testnets
      • Testnet Accounts
      • Testnet Consensus Nodes
    • Localnet
      • Single Node Configuration
      • Multinode Configuration
    • Network Explorers and Tools
    • Release Notes
      • Consensus Node
      • Hedera Mirror Node
  • Core Concepts
    • Accounts
      • Account Creation
      • Auto Account Creation
      • Account Properties
    • Keys and Signatures
    • Schedule Transaction
    • Smart Contracts
      • Understanding Hedera's EVM Differences and Compatibility
        • For EVM Developers Migrating to Hedera
          • Accounts, Signature Verification & Keys (ECDSA vs. ED25519)
          • JSON-RPC Relay and EVM Tooling
          • Token Management with Hedera Token Service
          • Decimal Handling (8 vs. 18 Decimals)
          • Handling HBAR Transfers in Contracts
        • For Hedera-Native Developers Adding Smart Contract Functionality
          • Integrating ED25519 Accounts and Advanced Features Into Smart Contracts
          • JSON-RPC Relay and State Queries
          • Extending Token Management with Smart Contracts
      • Creating Smart Contracts
      • Compiling Smart Contracts
      • System Smart Contracts
        • Hedera Account Service
        • Hedera Schedule Service
      • Gas and Fees
      • JSON-RPC Relay
      • Deploying Smart Contracts
      • Smart Contract Addresses
      • Verifying Smart Contracts
      • Smart Contract Traceability
      • Tokens Managed by Smart Contracts
        • ERC-20 (Fungible Tokens)
        • ERC-721 (Non-Fungible Token)
        • ERC-3643 Real World Assets (RWA)
        • ERC-1363 (Payable Tokens)
        • Hedera Token Service System Contract
      • Wrapped HBAR (WHBAR)
      • Smart Contract Rent
      • Smart Contract Security
      • EVM Archive Node Queries
    • Tokens
      • Tokenization on Hedera
      • Hedera Token Service (HTS) Native Tokenization
        • Token Types and ID Formats
        • Token Properties
        • Token Creation
        • Custom Fee Schedule
        • Token Airdrops
      • ERC/EVM-Compatible Tokenization
      • Hybrid (HTS + EVM ) Tokenization
    • Staking
      • Staking Program
      • Stake HBAR
    • Hashgraph Consensus Algorithm
      • Gossip About Gossip
      • Virtual Voting
    • Transactions and Queries
      • Transaction Properties
    • State and History
    • Mirror Nodes
      • Hedera Mirror Node
      • One Click Mirror Node Deployment
      • Run Your Own Mirror Node
        • Run Your Own Mirror Node with Google Cloud Storage (GCS)
        • Run Your Mirror Node with Amazon Web Services S3 (AWS)
  • Open Source Solutions and Integrations
    • AI Studio on Hedera
      • ElizaOS Plugin for Hedera
      • Hedera AI Agent Kit
      • MCP Server
      • OpenConvAI
    • AI Tools for Developers
      • Hedera Hivemind
      • Kapa AI
    • Asset Tokenization Studio (ATS)
      • Web User Interface (UI)
      • Frequently Asked Questions (FAQs)
    • HashioDAO
      • Governance Token DAO
      • NFT DAO
      • Multisig DAO
      • DAO Proposals
      • Local Environment Setup
    • Hedera CLI
    • Hedera Contract Builder
    • Hedera Custodians Library
      • How to use it
    • Hedera Developers Code Repository
    • Hedera Developer Playground
    • Hedera Wallet Snap By MetaMask
      • Hedera Wallet Snap Documentation
      • Tutorial: MetaMask Snaps – What Are They and How to Use Them
    • Interoperability and Bridging
      • LayerZero
    • NFT Studio
      • Airdrop List Verifier
      • Metadata Validator
      • NFT Rarity Inspector
      • NFT Token Holders List Builder
      • NFT Risk Calculator
      • Token Balance Snapshot
      • Hedera NFT SDK
    • Oracle Networks
      • Chainlink Oracles
      • Pyth Oracles
      • Supra Oracles
    • Stablecoin Studio
      • Core Concepts
      • Web UI Application
      • CLI Management
      • TypeScript SDK
    • Hedera Guardian
    • Hedera WalletConnect
  • SDKs & APIs
    • SDKs
      • Build Your Hedera Client
      • Set Up Your Local Network
      • Network Address Book
      • Keys
        • Generate a new key pair
        • Import an existing key
        • Create a key list
        • Create a threshold key
        • Generate a mnemonic phrase
        • Recover keys from a mnemonic phrase
      • HBAR
      • Specialized Types
      • Pseudorandom Number Generator
      • Transactions
        • Create a Batch Transaction
        • Transaction ID
        • Modify transaction fields
        • Create an unsigned transaction
        • Manually sign a transaction
        • Submit a transaction
        • Sign a multisignature transaction
        • Get a transaction receipt
        • Get a transaction record
      • Schedule Transaction
        • Schedule ID
        • Create a schedule transaction
        • Sign a scheduled transaction
        • Delete a schedule transaction
        • Get schedule info
        • Network Response Messages
      • Queries
      • General Network Response Messages
      • Accounts and HBAR
        • Create an account
        • Update an account
        • Transfer cryptocurrency
        • Approve an allowance
        • Delete an allowance
        • Delete an account
        • Get account balance
        • Get account info
        • Network Response Messages
      • Consensus Service
        • Create a topic
        • Update a topic
        • Submit a message
        • Delete a topic
        • Get topic messages
        • Get topic info
        • Network Response
      • Token Service
        • Token ID
        • NFT ID
        • Token types
        • Create a token
        • Custom token fees
        • Update a token
        • Update token custom fees
        • Update NFT metadata
        • Transfer tokens
        • Airdrop a token
        • Claim a token
        • Cancel a token
        • Reject a token
        • Delete a token
        • Mint a token
        • Burn a token
        • Freeze an account
        • Unfreeze an account
        • Enable KYC account flag
        • Disable KYC account flag
        • Associate tokens to an account
        • Dissociate tokens from an account
        • Pause a token
        • Unpause a token
        • Wipe a token
        • Atomic swaps
        • Get account token balance
        • Get token info
        • Get NFT info
        • Network Response Messages
      • File Service
        • Create a file
        • Append to a file
        • Update a file
        • Delete a file
        • Get file contents
        • Get file info
        • Network Response Messages
      • Smart Contract Service
        • Delegate Contract ID
        • Create a smart contract
        • Update a smart contract
        • Delete a smart contract
        • Call a smart contract function
        • Ethereum transaction
        • Get a smart contract function
        • Get smart contract bytecode
        • Get smart contract info
        • Hedera Service Solidity Libraries
        • Network Response Messages
      • Signature Provider
        • Provider
        • Signer
        • Wallet
        • Local Provider
    • Mirror Node REST API
      • Accounts
      • Balances
      • Blocks
      • Schedule Transactions
      • Smart Contracts
      • Tokens
      • Topics
      • Transactions
      • Network
    • Hedera Consensus Service gRPC API
    • Hedera APIs
      • Basic Types
        • AccountAmount
        • AccountID
        • ContractID
        • CryptoAllowance
        • CurrentAndNextFeeSchedule
        • FeeComponents
        • FeeData
        • FeeSchedule
        • FileID
        • Fraction
        • HederaFunctionality
        • Key
        • KeyList
        • NftAllowance
        • NftTransfer
        • NodeAddress
        • NodeAddressBook
        • RealmID
        • ScheduleID
        • SemanticVersion
        • ServicesConfigurationList
        • ServiceEndpoint
        • Setting
        • ShardID
        • Signature
        • SignatureList
        • SignatureMap
        • SignaturePair
        • SubType
        • TransferList
        • TransactionID
        • ThresholdKey
        • ThresholdSignature
        • TokenAllowance
        • TokenBalance
        • TokenBalances
        • TokenFreezeStatus
        • TokenPauseStatus
        • TokenID
        • TokenKycStatus
        • TokenRelationship
        • TokenTransferList
        • TokenType
        • TokenSupplyType
        • TopicID
        • TransactionFeeSchedule
      • Cryptocurrency Accounts
        • CryptoService
        • CryptApproveAllowance
        • CryptoDeleteAllowance
        • CryptoCreate
        • CryptoTransfer
        • CryptoUpdate
        • CryptoDelete
        • CryptoGetAccountBalance
        • CryptoGetAccountRecords
        • CryptoGetInfo
        • CryptoGetStakers
      • Consensus Service
        • Consensus Service
        • ConsensusCreateTopic
        • ConsensusUpdateTopic
        • ConsensusSubmitMessage
        • ConsensusDeleteTopic
        • ConsensusTopicInfo
        • ConsensusGetTopicInfo
      • Schedule Service
        • ScheduleService
        • SchedulableTransactionBody
        • ScheduleCreate
        • ScheduleDelete
        • ScheduleSign
        • ScheduleGetInfo
      • Token Service
        • TokenService
        • CustomFees
          • AssessedCustomFee
          • CustomFee
          • FractionalFee
          • FixedFee
          • RoyaltyFee
        • TokenCreate
        • TokenUpdate
        • TokenFeeScheduleUpdate
        • TokenDelete
        • TokenMint
        • TokenBurn
        • TokenFreezeAccount
        • TokenUnfreezeAccount
        • TokenGrantKyc
        • TokenRevokeKyc
        • TokenAssociate
        • TokenDissociate
        • TokenWipeAccount
        • TokenPause
        • TokenUnpause
        • TokenGetInfo
        • TokenGetNftInfo
        • TokenGetNftInfos
        • TokenGetAccountNftInfo
      • File Service
        • FileService
        • FileCreate
        • FileAppend
        • FileUpdate
        • FileDelete
        • FileGetContents
        • FileGetInfo
      • Smart Contracts
        • SmartContractService
        • ContractCall
        • ContractCallLocal
        • ContractCreate
        • ContractUpdate
        • ContractDelete
        • ContractGetByteCode
        • ContractGetInfo
        • ContractGetRecords
      • Miscellaneous
        • Duration
        • ExchangeRate
        • Freeze
        • FreezeType
        • GetByKey
        • GetBySolidityID
        • NetworkGetVersionInfo
        • NetworkService
        • Query
        • QueryHeader
        • Response
        • ResponseCode
        • ResponseHeader
        • SystemDelete
        • SystemUndelete
        • TimeStamp
        • Transaction
        • TransactionBody
        • TransactionContents
        • TransactionGetFastRecord
        • TransactionGetReceipt
        • TransactionGetRecord
        • TransactionReceipt
        • TransactionRecord
        • TransactionResponse
        • UncheckedSubmit
    • Hedera Status API
  • Support & Community
    • Glossary
    • Contributing to Hedera documentation
      • Contribution Guidelines
        • Creating Issues
        • Creating Pull Requests
        • Hedera Improvement Proposal (HIP)
        • Submit Demo Applications
      • Style Guide
        • Understanding different types of documentation
        • Use of HBAR and tinybars
        • Use of web2 and web3
        • Language and grammar
        • Formatting
        • Punctuation
        • GitBook Markdown Syntax
    • Discord
    • GitHub
    • Stack Overflow
    • Hedera Blog
    • Bug Bounty
    • Hedera Help
    • Documentation Survey
    • Meetups
    • Brand Guidelines
    • Status Page
Powered by GitBook
LogoLogo

INTRODUCTION

  • Fees
  • Core Concepts
  • Network Information

TOOLS

  • Bridge
  • Oracles
  • Explorers
  • Developer Portal & Faucet

RESOURCES

  • Status
  • Bug Bounty
  • Build on Hedera (course)
  • Documentation Survey
On this page
  • Introduction
  • Prerequisites
  • Get Familiar with the dApp Structure and UI
  • Overall dApp Structure
  • Step 1: Pair MetaMask Wallet (select network and account)
  • Step 2: Install the Hedera Wallet Snap
  • Step 3: Get the Snap EVM Address
  • Step 4: Create Snap Account and Check Its Balance
  • Step 5: Call Other Methods in the Hedera Wallet Snap
  • Summary
  • Additional Resources

Was this helpful?

Edit on GitHub
  1. Open Source Solutions and Integrations
  2. Hedera Wallet Snap By MetaMask

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.

PreviousHedera Wallet Snap By MetaMaskNextInteroperability and Bridging

Last updated 10 months ago

Was this helpful?

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 that MetaMask does not natively support. To learn more about Snaps, visit the .

The , developed by and managed by , 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 ()

  • MetaMask ()

  • Hedera Wallet Snap for MetaMask ()

  • Hedera JSON-RPC Relay ()

  • Ethers JS ()

  • Mirror Node REST API ()

  • Mirror Node Explorer ()


Prerequisites


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:

Run in Browser Using GitPod
Clone the Repository Locally

If you prefer to have a copy of the code files on your machine and run the application locally, follow these steps.

Clone the Repo

To clone the repository, open your terminal and navigate to the directory where you want to place the project. Then, run the following command:

git clone https://github.com/hedera-dev/hedera-example-metamask-snap.git

Navigate to Directory

Once the cloning process is complete, navigate to the project folder using:

cd hedera-example-metamask-snaps

Install Project Dependencies and Start the Application

npm install

To start the application, run:

npm start

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):

  1. The functions that are executed with each button press; we’ll look at these in the next few sections.

  2. The return statement groups the elements we see on the page.

Understanding the React Components

MyGroup
import React from "react";
import MyButton from "./MyButton.jsx";
import MyText from "./MyText.jsx";
function MyGroup(props) {
    return (
        <div>
            <MyText text={props.text} link={props.link} />
            <MyButton fcn={props.fcn} buttonLabel={props.buttonLabel} />
        </div>
    );
}
export default MyGroup;

MyGroup is a functional component that combines a text element with a button and receives props as an argument. These properties include:

  • text: The text to be displayed by the MyText component.

  • link: An optional link to be associated with the MyText component. If provided, the text will become clickable and redirect to the specified link.

  • fcn: A function to be executed when the button within the MyButton component is clicked.

  • buttonLabel: The label for the button in the MyButton component.

Inside the component, we return a div element that contains both the MyText and MyButton components. We pass the corresponding props down to these child components, allowing them to render the text, link, and button label and assign the click event handler. Finally, by exporting MyGroup, we make it available for use in other parts of the application, enabling us to quickly create reusable groups of text and button elements throughout the dApp. You can find the JSX files for the functional components mentioned under the folder src/components.

MyButton
import React from "react";
function MyButton(props) {
    return (
        <div>
            <button onClick={props.fcn} className="cta-button">
                {props.buttonLabel}
            </button>
        </div>
    );
}
export default MyButton;

MyButton accepts the following props:

  • fcn: A function to be executed when the button is clicked.

  • buttonLabel: The label for the button, which will be displayed as the button's text.

Inside the component, we return a div element that wraps a button element. The button element is assigned the onClick event handler with the function props.fcn. This allows us to execute a specified function when the button is clicked. The className attribute is set to "cta-button," which is used for styling the button with CSS. Finally, we display the props.buttonLabel as the button's text.

By exporting MyButton, we make it available for use in other groups or parts of the application, allowing us to easily create consistent and reusable buttons throughout the dApp with customized functionality and labels.

MyText
import React from "react";
function MyText(props) {
    if (props.link !== "") {
        return (
            <div>
                <a href={props.link} target={"_blank"} rel="noreferrer">
                    <p className="sub-text">{props.text}</p>
                </a>
            </div>
        );
    } else {
        return (
            <div>
                <p className="sub-text">{props.text}</p>
            </div>
        );
    }
}
export default MyText;

MyText displays text and optionally wraps it in a link. It takes props as an argument, including:

  • text: The text to be displayed.

  • link: An optional link to be associated with the text.

Inside the component, we use a conditional statement to check if props.link is provided. If it is, we wrap the text within an <a> element, making it clickable and redirecting to the specified link. If no link is provided, we display the text within a <p> element. Both elements have the className "sub-text" for consistent styling. Finally, we export the MyText component so it can be used in other parts of the application, allowing for easy creation of text elements with optional links.

MySendGroup
import React from "react";
import MyInputBox from "./MyInputBox";
import MyButton from "./MyButton";
import MyText from "./MyText";
function MySendGroup(props) {
    return (
        <div>
            <MyText text={props.text_app} link={props.link_app} />
            <div className="multi-col-group">
                <MyInputBox fcn={props.fcnI1_app} placeholderTxt={props.placeholderTxt1_app} />
                <MyInputBox fcn={props.fcnI2_app} placeholderTxt={props.placeholderTxt2_app} />
                <MyButton fcn={props.fcnB1_app} buttonLabel={props.buttonLabel_app} />
            </div>
        </div>
    );
}
export default MySendGroup;

MySendGroup uses a text display, two input boxes, and a button to facilitate data entry and user interaction. The component takes props as an argument, including:

  • fcnI1 and fcnI2: Functions to store the text provided via input fields in the application state.

  • placeholderTxt1_app and placeholderTxt2_app: Placeholder texts for the input fields.

  • The same arguments are mentioned for MyText and MyButton.

Inside the component, we return a div element that contains the MyText, MyInputBox, and MyButton components. We pass the corresponding props down to these child components. Finally, exporting MySendGroup makes it available for use in other parts of the application.

MyInputBox
import React from "react";
function MyInputBox(props) {
    return (
        <div>
            <input type="text" onChange={props.fcn} placeholder={props.placeholderTxt} className="text-input" />
        </div>
    );
}
export default MyInputBox;

MyInputBox accepts the following props:

  • fcn: A function to store the text provided via input fields in the application state.

  • placeholderTxt: Placeholder texts for the input fields.

Inside the component, we return a div that wraps an input element. The input element is assigned the onChange event handler with the function props.fcn. This triggers the function that stores the inputs (receiver address and HBAR amount) to the state of the application. We display the props.placeholderTxt as the box text. The className attribute is set to "text-input," which is used for styling the boxes with CSS. Finally, by exporting MyInputBox, we make it available for use in other parts of the application.


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.

async function connectWallet() {
        if (account !== undefined) {
                setConnectText(`🔌 Account ${account} already connected ⚡ ✅`);
        } else {
                const wData = await walletConnectFcn(network);

                let newAccount = wData[0];
             if (newAccount !== undefined) {
                        setConnectText(`🔌 Account ${newAccount} connected ⚡ ✅`);
                        setConnectLink(`https://hashscan.io/${network}/account/${newAccount}`);
                        setWalletData(wData);
                        setAccount(newAccount);
                        setSnapInstallText();
                        setSnapInfoText();
                        setSnapTransferText();
                }
        }
}
import { ethers } from "ethers";
async function walletConnectFcn(network) {
    console.log(`\n=======================================`);
    // ETHERS PROVIDER
    const provider = new ethers.providers.Web3Provider(window.ethereum, "any");
    // SWITCH TO HEDERA TEST NETWORK
    console.log(`- Switching network to the Hedera ${network}...🟠`);
    let chainId;
    if (network === "testnet") {
        chainId = "0x128";
    } else if (network === "previewnet") {
        chainId = "0x129";
    } else {
        chainId = "0x127";
    }
    await window.ethereum.request({
        method: "wallet_addEthereumChain",
        params: [
            {
                chainName: `Hedera ${network}`,
                chainId: chainId,
                nativeCurrency: { name: "HBAR", symbol: "HBAR", decimals: 18 },
                rpcUrls: [`https://${network}.hashio.io/api`],
                blockExplorerUrls: [`https://hashscan.io/${network}/`],
            },
        ],
    });
    console.log("- Switched ✅");
    // // CONNECT TO ACCOUNT
    console.log("- Connecting wallet...🟠");
    let selectedAccount;
    await provider
        .send("eth_requestAccounts", [])
        .then((accounts) => {
            selectedAccount = accounts[0];
            console.log(`- Selected account: ${selectedAccount} ✅`);
        })
        .catch((connectError) => {
            console.log(`- ${connectError.message.toString()}`);
            return;
        });
    return [selectedAccount, provider];
}
export default walletConnectFcn;

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:

  1. 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.

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.

async function snapInstall() {
        if (account === undefined) {
            setSnapInstallText("🛑Connect a wallet first!🛑");
        } else {
            const newSnapInstallText = await snapInstallFcn(snapId);
            setSnapInstallText(newSnapInstallText);
            setSnapInfoText();
async function snapInstallFcn(snapId) {
    console.log(`\n=======================================`);
    console.log(`- Installing Hedera Wallet Snap...🟠`);
    console.log(`SnapId: ${snapId}`);
    let outText;
    let snaps = await window.ethereum.request({
        method: "wallet_getSnaps",
    });
    console.log("Installed snaps...", snaps);
    try {
        if (!(snapId in snaps)) {
            console.log("Hedera Wallet Snap is not yet installed. Installing now...");
            const result = await window.ethereum.request({
                method: "wallet_requestSnaps",
                params: {
                    [snapId]: {},
                },
            });
            console.log("result: ", result);
            snaps = await window.ethereum.request({
                method: "wallet_getSnaps",
            });
        }
    } catch (e) {
        console.log(`Failed to obtain installed snap: ${JSON.stringify(e, null, 4)}`);
        alert(`Failed to obtain installed snap: ${JSON.stringify(e, null, 4)}`);
    }
    if (snapId in snaps) {
        outText = "Snap installed ✅";
        console.log(`- Snap installed successfully ✅`);
        alert("Snap installed successfully!");
    } else {
        console.log("Could not connect successfully. Please try again!");
        alert("Could not connect successfully. Please try again!");
    }
    return outText;
}
export default snapInstallFcn;

The snapInstallFcn() function performs the following steps:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

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.

 async function snapGetAccountInfo() {
        if (account === undefined || snapInstallText === undefined) {
            setSnapInfoText("🛑Connect a wallet and install the snap first!🛑");
        } else {
            const [snapAccountAddress, infoText] = await snapGetAccountInfoFcn(network, walletData, snapId);
            setSnapInfoText(infoText);
            setInfoLink(`https://hashscan.io/${network}/address/${snapAccountAddress}`);
            setSnapTransferText();
        }
    }
async function snapGetAccountInfoFcn(network, walletData, snapId) {
    console.log(`\n=======================================`);
    console.log(`- Invoking GetAccountInfo...🟠`);
    let outText;
    let snapAccountEvmAddress;
    let snapAccountBalance;
    try {
        const response = await window.ethereum.request({
            method: "wallet_invokeSnap",
            params: {
                snapId,
                request: {
                    method: "getAccountInfo",
                    params: {
                        network: network,
                        mirrorNodeUrl: `https://${network}.mirrornode.hedera.com`,
                    },
                },
            },
        });
        snapAccountEvmAddress = response.accountInfo.evmAddress;
        snapAccountBalance = response.accountInfo.balance.hbars;
        outText = `Snap Account ${snapAccountEvmAddress} has ${snapAccountBalance} ℏ ✅`;
    } catch (e) {
        snapAccountEvmAddress = e.message.match(/0x[a-fA-F0-9]{40}/)[0];
        outText = `Go to MetaMask and transfer HBAR to the snap address to activate it: ${snapAccountEvmAddress} 📤`;
    }
    console.log(`- ${outText}}`);
    console.log(`- Got account info ✅`);
    return [snapAccountEvmAddress, outText];
}
export default snapGetAccountInfoFcn;

The snapGetAccountInfoFcn() function performs the following steps:

  1. 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.

  2. 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.

  3. 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.

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.

 async function snapTransferHbar() {
        if (account === undefined || snapInstallText === undefined || snapInfoText === undefined) {
            setSnapTransferText("🛑Complete all the steps above first!🛑");
        } else {
            setSnapTransferText(`Transferring...`);
            const transferText = await snapTransferHbarFcn(network, walletData, snapId, [receiverAddress, hbarAmount]);
            setSnapTransferText(`${transferText}`);
        }
    }
async function snapTransferHbarFcn(network, walletData, snapId, args) {
    console.log(`\n=======================================`);
    console.log(`- Invoking transferCrypto...🟠`);
    let outText;
    const receiverAddress = args[0];
    const hbarAmount = parseFloat(args[1]);
    const maxFee = 0.05;
    const transfers = [
        {
            asset: "HBAR",
            to: receiverAddress,
            amount: hbarAmount, // in Hbar
        },
    ];
    // If you're sending to an exchange account,
    // you will likely need to fill this out
    const memo = "";
    try {
        await window.ethereum.request({
            method: "wallet_invokeSnap",
            params: {
                snapId,
                request: {
                    method: "transferCrypto",
                    params: {
                        network: network,
                        transfers,
                        memo,
                        maxFee: maxFee,
                    },
                },
            },
        });
        outText = `Transfer successful ✅ | Get the snap account info again to see the updated balance!`;
        console.log(`- ${outText}`);
    } catch (e) {
        outText = `Transaction failed. Try again 🛑`;
        console.log(`- Transfer failed 🛑: ${JSON.stringify(e, null, 4)}`);
    }
    return outText;
}
export default snapTransferHbarFcn;

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.


Additional Resources

NodeJS >= 18.13 ()

TypeScript >= 4.7 ()

Git Command Line ()

Hedera Testnet Account ()

MetaMask Wallet Extension ()

This option does not require installing anything on your machine. All you need is a compatible web browser. . You will see the following:

After cloning the repo and navigating to the right folder, install all project dependencies. Dependencies are listed in the file, so you can just run the following command:

The state management is the part that uses the .

The useState() hook helps store information about the state of the application. In this case, we store information like the , 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).

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 .

Creates an ethers provider: It initializes an ethers provider using the Web3Provider from the 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 , a community-hosted provided by (note that anyone can host their own relay and/or use other commercial providers, like ). For network explorer, is used. (Keep in mind that HashScan supports , which makes it easy to explore historical data like blocks, transactions, accounts, contracts, and tokens from wallets like MetaMask).

Note: The Hedera account selected in MetaMask for this example has ECDSA keys and is created using the .

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 .

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 .

Perform a transfer of 10 HBAR to the Snap address. In this case, the Snap address is:

Note: The Hedera account selected in MetaMask for this example has ECDSA keys and is created using the .

🎉 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 if you have any questions!

➡

➡

➡

➡

➡ Ask Questions on

Download
Download
Download
Create
Download
Click here to set up the GitPod environment
package.json
useState() React Hook
snapId
React props
ethers
Hashio
JSON-RPC relay
Hashgraph
Arkhia
HashScan
EIP-3091
Hedera Portal
this section of the Hedera Wallet Snap documentation
this section of the Hedera Wallet Snap documentation
0xa7deaf8acd6be555740f9672dff34f510480f0c9
Hedera Developer Portal
Discord
Project Repository
Hedera Wallet Snap
MetaMask Snap Guide
Hedera JSON-RPC Relay Repository
StackOverflow

Writer: Ed, Head of Developer Relations

Editor: Krystal, Technical Writer

MetaMask Snap Guide
Hedera Wallet Snap
Tuum Tech
Hashgraph
Documentation
Documentation
Documentation
Hashio
Documentation
Learn More
HashScan

|

|

GitHub
LinkedIn
GitHub
Hashnode
decentralized applications (dApps)