Create a Snap to calculate gas fee percentages
This tutorial walks you through creating a Snap that calculates the percentage of gas fees that a user pays when creating a transaction. The Snap provides transaction insights in MetaMask's transaction confirmation window.
Prerequisites
- MetaMask Flask installed
- An account on your MetaMask Flask instance with testnet ETH
tip
You can use Infura's Sepolia faucet to get Sepolia ETH.
- A text editor (for example, VS Code)
- Node version 20.11 or later
- Yarn
Steps
1. Set up the project
Create a new Snap project using the
@metamask/create-snap
starter kit by running:
yarn create @metamask/snap transaction-insights-snap
or
npx @metamask/create-snap transaction-insights-snap
or
npm create @metamask/snap transaction-insights-snap
Next, cd
into the transaction-insights-snap
project directory and run:
yarn install
This initializes your development environment with the required dependencies. You may get a warning similar to the following:
@lavamoat/allow-scripts has detected dependencies without configuration. explicit configuration required.
run "allow-scripts auto" to automatically populate the configuration.
You can resolve the issue by running the following command:
yarn run allow-scripts auto
2. Enable transaction insights and the Ethereum provider
The default template Snap, such as the one in Create a gas estimation Snap, is configured to expose a JSON-RPC API with a simple hello command, which brings up a dialog box. In contrast, the Snap you're creating in this tutorial doesn't expose any API. Instead, it provides transaction insights directly in MetaMask's transaction confirmation window.
In particular, the Snap shows the user the percentage of gas fees they would pay for their transaction.
It gets the current gas price by calling the
eth_gasPrice
RPC
method using the global Ethereum provider made available to Snaps.
To enable your Snap to provide transaction insights and
use the global Ethereum provider, request the
endowment:transaction-insight
and
endowment:ethereum-provider
permissions in packages/snap/snap.manifest.json
:
"initialPermissions": {
"endowment:transaction-insight": {},
"endowment:ethereum-provider": {}
}
In this tutorial, you can replace what was previously in initialPermissions
.
You do not need any permissions other than endowment:transaction-insight
and endowment:ethereum-provider
.
3. Calculate and display the percentage of gas fees
To calculate and display the gas fees a user would pay as a percentage of their outgoing transaction,
replace the code in packages/snap/src/index.ts
with the following:
import type { OnTransactionHandler } from "@metamask/snaps-sdk";
import { heading, panel, text } from "@metamask/snaps-sdk";
// Handle outgoing transactions.
export const onTransaction: OnTransactionHandler = async ({ transaction }) => {
// Use the Ethereum provider to fetch the gas price.
const currentGasPrice = await ethereum.request({
method: "eth_gasPrice",
}) as string;
// Get fields from the transaction object.
const transactionGas = parseInt(transaction.gas as string, 16);
const currentGasPriceInWei = parseInt(currentGasPrice ?? "", 16);
const maxFeePerGasInWei = parseInt(transaction.maxFeePerGas as string, 16);
const maxPriorityFeePerGasInWei = parseInt(
transaction.maxPriorityFeePerGas as string,
16,
);
// Calculate gas fees the user would pay.
const gasFees = Math.min(
maxFeePerGasInWei * transactionGas,
(currentGasPriceInWei + maxPriorityFeePerGasInWei) * transactionGas,
);
// Calculate gas fees as percentage of transaction.
const transactionValueInWei = parseInt(transaction.value as string, 16);
const gasFeesPercentage = (gasFees / (gasFees + transactionValueInWei)) * 100;
// Display percentage of gas fees in the transaction insights UI.
return {
content: panel([
heading("Transaction insights Snap"),
text(
`As set up, you are paying **${gasFeesPercentage.toFixed(2)}%**
in gas fees for this transaction.`,
),
]),
};
};
If you have previously developed a dapp, you're likely familiar with accessing the Ethereum provider using window.ethereum
.
In a Snap, the window
object is not available.
Instead, when you request the endowment:ethereum-provider
permission, your Snap is granted access to the ethereum
global object.
4. Build and test the Snap
To build and test your Snap:
-
From the command line, run
yarn start
in the root of your project. This starts two development servers: one for watching and compiling the Snap, and another for the React site. The Snap bundle is served fromlocalhost:8080
, and the site is served fromlocalhost:8000
. You should get a message that includes:You can now view site in the browser.
http://localhost:8000/ -
Open
localhost:8000
in your browser (with MetaMask Flask installed). -
Select Connect and accept the permission request.
-
After connecting, you're prompted to install the Snap with the Fetch and display transaction insights and Access the Ethereum provider permissions. Select Approve > Install.
-
From MetaMask Flask, create a new testnet ETH transfer. You can set up multiple accounts to transfer between your accounts.
-
In the transaction confirmation window, switch to the tab named TYPESCRIPT EXAMPLE SNAP. Switching to the tab activates the
onTransaction
entry point of your Snap and displays the percentage of gas fees in the transaction insights UI:
5. Display a different UI for contract interactions
The Snap should display a gas fee percentage for ETH transfers initiated by the user.
For contract interactions, add the following code to the beginning of the onTransaction
entry point:
if (typeof transaction.data === "string" && transaction.data !== "0x") {
return {
content: panel([
heading("Percent Snap"),
text(
"This Snap only provides transaction insights for simple ETH transfers.",
),
]),
};
}
6. Next steps
The initial project has generic names in multiple places.
You can update the fields in snap.manifest.json
to match your custom Snap:
proposedName
- The name of your Snap. This replaces TYPESCRIPT EXAMPLE SNAP in the transaction insights UI.description
- The description of your Snap.source
- Theshasum
is set automatically when you build from the command line. If you decided to publish your Snap to npm, update thelocation
to its published location.
Similarly, you should update the name
, version
, description
, and repository
fields of
packages/snap/package.json
even if you don't plan to publish your Snap to npm.
The version
and repository
fields in snap.manifest.json
inherit the values from
package.json
and overwrite them in snap.manifest.json
.
We recommend updating version
and repository
in package.json
first, then building the Snap project.
You should also add an icon by following the steps outlined in the gas estimation Snap tutorial.
Lastly, you can update the content of packages/site/src/pages/index.tsx
, such as removing the
template Send Hello button.
After you've made all necessary changes, you can publish your Snap to npm.