Simple dApp Integration
For this tutorial, we'll be building a basic dApp using an HTML/CSS/JS base. This dApp onboards users to MetaMask Flask, installs the quai-snap npm package, and generates a set of 13 addresses, one for each shard. To get started, you can clone our Project Files from GitHub or use your own clean directory.
Because of the limited scope of a traditional javascript backend, the dApp offers no additional functionalities. It is highly recommended that developers utilize a more advanced framework such as React.
MetaMask Flask is an experimental development environment that allows developers to create and test custom MetaMask APIs and other features. MetaMask Flask operates almost identically to vanilla MetaMask, but with the option to integrate custom Snaps.
Snaps are unique JavaScript programs that allow code to run inside a secure execution environment within the MetaMask Flask browser extension. Snaps bring MetaMask functionality to blockchain networks that have yet to be integrated into the official MetaMask browser extension. The official Snaps development guide with full documentation can be found here. Snaps are currently distributed as npm packages.
As mentioned earlier, Snaps allow developing chains to integrate into the MetaMask ecosystem. Quai-Snap is an npm package that offers Quai Network users a streamlined and familiar experience when using Quai based dApps and managing their funds. A list of available API calls for application developers can be found on the Quai-Snap npm registry page. Utilization of Quai-Snap requires users to disable the traditional MetaMask browser extension temporarily.
Now that you're familiar with MetaMask Flask and Snaps, make sure you've read through our docs and understand the basics of Quai Network's architecture.
To get started, make sure you have:
- 1.
- 2.
- 3.
- 4.
- 5.
This tutorial follows the app structure outlined in the quai-sample-dapp repo. If you've already cloned the quai-sample-dapp, open a terminal and navigate to its location. Inside the folder, the file structure should look like this:
.
├─ assets
├─ index.html
├─ index.js
├─ css
| └─ styles.css
├─ package-lock.json
├─ package.json
└─ README.md
There are some extra files inside assets that aren't pertinent to this tutorial. No need to worry!
To install your basic dependencies, open up your terminal again and run the following:
npm install
Once the installation has finished, you should have a
/node-modules
folder that houses all of the required dependencies.For ease of use, I recommend using the VS Code Live Server extension to spin up a server for running and testing your app.
Navigate to the index.js file, where you'll find the majority of the logic for our app.
It should look like this. Don't worry about lines 1-8, we'll touch on that later.
const initialize = async () => {
// Flask logic goes here
};
window.addEventListener('DOMContentLoaded', initialize);
The function above is called as soon as the DOM content is initialized. Before we get started writing any logic, let's layout the todo list for our app.
- Connecting to our MetaMask Flask Wallet
- Display the current connected account
- Sort accounts by shard ID and display them
The first and most important thing our dApp should do is connect to our Flask Wallet. To do that we need to:
- 1.Create a function that checks if the MetaMask Flask Chrome extension is installed.
- 2.If Flask is not installed we should:
- 1.Change our
connectButton
to displayClick here to install Flask
- 2.
- 3.Disable
connectButton
functionality
- 3.If Flask is installed, we should:
- 1.Change our
connectButton
toConnect
- 2.When clicked, that button should connect to our Flask Wallet
- 3.Disable the
connectButton.
In our code, we need to access the
connectButton
from our index.html. const connectButton = document.getElementById('connect');
const initialize = async () => {
// Flask logic goes here
...
We now need to create a provider check that determines if Flask is installed. For vanilla MetaMask, this is typically done with a simple function, but since we're using Flask, we have to get a little creative. More info can be found here.
For this part we will be using the '@metamask/detect-provider' library we installed during the npm install. Learn more here.
Inclusion of the
detect-provider
library can be seen below.const detectEthereumProvider = require('@metamask/detect-provider');
const connectButton = document.getElementById('connect');
const initialize = async () => {
// returns Ethereum provider as string
const provider = await detectEthereumProvider();
// returns isFlask boolean to indicate whether flask is installed
const isFlask = (await provider?.request({ method: 'web3_clientVersion' }))?.includes('flask');
};
window.addEventListener('DOMContentLoaded', initialize);
The
isFlask
variable returns a boolean that indicates whether or not flask is installed. Next, we can use this check to change the content of the connect button based on if the Flask Extension is installed or not.
const detectEthereumProvider = require('@metamask/detect-provider');
const connectButton = document.getElementById('connect');
const initialize = async () => {
// returns Ethereum provider as string
const provider = await detectEthereumProvider();
// returns isFlask boolean to indicate whether flask is installed
const isFlask = (await provider?.request({ method: 'web3_clientVersion' }))?.includes('flask');
const updateConnectButton = () => {
// Now we check to see if Flask is installed
if (!isFlask) {
// If flask isn't installed, ask the user to install it!
connectButton.innerHTML = "Install MetaMask Flask";
} else {
// If flask is installed, ask the user to connect their wallet.
connectButton.innerHTML = "Connect with Flask";
}
}
updateConnectButton();
};
window.addEventListener('DOMContentLoaded', initialize);
In the code above where Flask is not installed and the
connectButton
displays Install MetaMask Flask
, we must make sure that when the button is clicked we:- 1.Display an alert that provides information about MetaMask Flask usage.
- 2.Direct the user to the Metamask Flask installation page.
const updateConnectButton = () => {
// Now we check to see if Flask is installed
if (!isFlask) {
console.log("Flask is not installed")
// Display flask information alert
$('flask-notinstalled-alert').show();
// If flask isn't installed, ask user to do so. Make button link to install page.
connectButton.innerHTML = '<a href="https://metamask.io/flask/" target="_blank">Install MetaMask Flask</a>';
} else {
// If flask is installed, ask the user to connect their wallet.
connectButton.innerHTML = "Connect with Flask";
}
}
updateConnectButton();
Now if a user of our dApp does not have the MetaMask Flask Extension, they can easily install it! After installing the extension, they can refresh the page and continue connecting to our dApp.
Now that we've accounted for our users not having Flask installed, we can handle the case where a user already has the Flask Extension. We'll need to revisit our
updateConnectButton
block of code to add some extra functionality.const updateConnectButton = () => {
// Now we check to see if Flask is installed
if (!isFlask) {
console.log("Flask is not installed")
// Display flask information alert
$('flask-notinstalled-alert').show();
// If flask isn't installed, ask user to do so. Make button link to install page.
connectButton.innerHTML = '<a href="https://metamask.io/flask/" target="_blank">Install MetaMask Flask</a>';
} else {
console.log("Flask is installed")
// If flask is installed, ask the user to connect their wallet.
connectButton.innerHTML = "Connect with Flask";
// When the button is clicked, call this function to connect the user's wallet.
connectButton.onclick = onClickConnect;
// Enable clicking of the connect button
connectButton.disabled = false;
}
}
updateConnectButton();
We've now created an
onClickConnect
function that will be called when the connectButton
is clicked. This will prompt the user to connect with their Flask wallet. In the next section, we'll talk more about what goes inside of the
onClickConnect
function as well as other Flask RPC API calls you can make inside of your dApp.To introduce key functionality into our dApp, we must utilize altered Ethereum RPC API calls. Documentation on the wide spectrum of Quai Snap API calls can be found here.
Referring back to the
onClickConnect
function from before, we must connect to the user's Flask wallet and obtain their set of accounts. Let's build the onClickConnect
function and the logic that goes along with it.Above your
updateConnectButton
function insert this code:const onClickConnect = async () => {
await ethereum.request({
method: 'wallet_enable',
params: [{wallet_snap: { ['npm:@quainetwork/quai-snap']: {version: '0.1.0-pre.27'} },
}]
});
let newAccounts = await ethereum.request({
method: 'wallet_invokeSnap',
params: ['npm:@quainetwork/quai-snap', {
method: 'getAccounts'
}]
});
}
The above code will prompt Flask to connect your wallet when you click the
connectButton
, install the specified version of quai-snap
and get the list of addresses associated with your Flask wallet.We can also introduce a check that verifies the
accounts
object is not empty, and if it is, creates a new set of addresses, one per shard, for our user. Modify the onClickConnect
function like below:const onClickConnect = async () => {
await ethereum.request({
method: 'wallet_enable',
params: [{wallet_snap: { ['npm:@quainetwork/quai-snap']: {version: '0.1.0-pre.27'} },
}]
});
let accounts = await ethereum.request({
method: 'wallet_invokeSnap',
params: ['npm:@quainetwork/quai-snap', {
method: 'getAccounts'
}]
});
if (accounts.length == 0) {
const response = await ethereum.request({
method: 'wallet_invokeSnap',
params: ['npm:@quainetwork/quai-snap', {
method: 'generateAllAccounts'
}]
})
}
}
You can now access your user's addresses to display, manipulate or print out to the console! Additional account display and sorting functionalities can be found in
quai-sample-dapp
.Congratulations! You've completed the setup of a basic Quai Network dApp that onboards users to MetaMask Flask, allows them to connect their wallet, and return all of their addresses.
A number of other functionalities that can be leveraged inside of the HTML/CSS framework can be found in the quai-sample-dapp repo.
Here's a quick overview of the code we've worked on so far.
const detectEthereumProvider = require('@metamask/detect-provider');
const connectButton = document.getElementById('connect');
const initialize = async () => {
// returns Ethereum provider as string
const provider = await detectEthereumProvider();
// returns isFlask boolean to indicate whether flask is installed
const isFlask = (await provider?.request({ method: 'web3_clientVersion' }))?.includes('flask');
// Function called upon clicking connect button
const onClickConnect = async () => {
// enables and installs quai-snap
await ethereum.request({
method: 'wallet_enable',
params: [{wallet_snap: { ['npm:@quainetwork/quai-snap']: {version: '0.1.0-pre.27'} },
}]
});
// gets object newAccounts
let accounts = await ethereum.request({
method: 'wallet_invokeSnap',
params: ['npm:@quainetwork/quai-snap', {
method: 'getAccounts'
}]
});
// generates accounts if newAccounts is an empty object
if (accounts.length == 0) {
const response = await ethereum.request({
method: 'wallet_invokeSnap',
params: ['npm:@quainetwork/quai-snap', {
method: 'generateAllAccounts'
}]
})
}
}
const updateConnectButton = () => {
// Now we check to see if Flask is installed
if (!isFlask) {
console.log("Flask is not installed")
// Display flask information alert
$('flask-notinstalled-alert').show();
// If flask isn't installed, ask user to do so. Make button link to install page.
connectButton.innerHTML = '<a href="https://metamask.io/flask/" target="_blank">Install MetaMask Flask</a>';
} else {
console.log("Flask is installed")
// If flask is installed, ask the user to connect their wallet.
connectButton.innerHTML = "Connect with Flask";
// When the button is clicked, call this function to connect the user's wallet.
connectButton.onclick = onClickConnect;
// Enable clicking of the connect button
connectButton.disabled = false;
}
}
updateConnectButton();
};
window.addEventListener('DOMContentLoaded', initialize);
Last modified 1mo ago