MetaMask
MetaMask is one of the most widely used wallets for interacting with Ethereum-compatible blockchains.
It allows users to connect their existing accounts securely and sign messages using their own keys, providing full control over custody.
Why combine MetaMask with Notus API?
By connecting MetaMask to Notus API, developers can:
- Leverage a user's existing wallet for authentication
- Register and manage smart wallets (ERC-4337)
- Sign and execute onchain actions using Account Abstraction
- Support gasless experiences with Paymasters
This enables Web3-native users to access advanced smart wallet features using their existing MetaMask accounts.
This guide demonstrates how to use MetaMask with the Notus API in a Next.js project.
You’ll connect a wallet, register a smart wallet, request a swap quote, and execute a UserOperation using viem
.
Eager to get started? Check out our GitHub repository for a complete example and see how to integrate Notus API into your project.
How to Use MetaMask with Notus API
Let’s walk through the full integration step by step.
Start a Next.js Project
First, create a new Next.js project. Run the following command in your terminal:
npx create-next-app@latest
pnpm dlx create-next-app@latest
yarn dlx create-next-app@latest
bun x create-next-app@latest
This will scaffold a new Next.js application, setting up a basic React-based environment for building your dApp.
Install viem
We’ll use viem
, a library for blockchain interactions, to simplify our integration. Install it by running:
npm i viem
pnpm add viem
yarn add viem
bun add viem
Connect to MetaMask with Viem
Now, let’s set up a connection to Metamask. This step allows users to connect their wallets and retrieve their account addresses. Add the following code to your App component:
"use client";
import { createWalletClient, custom } from "viem";
import { polygon } from "viem/chains";
export default function App() {
const [externallyOwnedAccount, setExternallyOwnedAccount] = useState("");
const account = createWalletClient({
chain: polygon,
transport: custom(window.ethereum!),
});
const requestAddress = async () => {
const [externallyOwnedAccount] = await account.requestAddresses();
if (externallyOwnedAccount) {
setExternallyOwnedAccount(externallyOwnedAccount);
}
};
return (
<div>
<button onClick={async () => await requestAddress()}>Metamask</button>
</div>
);
}
Explanation:
createWalletClient
initializes the wallet connection.requestAddresses
fetches the connected wallet's account address.- The address is stored in the
externallyOwnedAccount
state for future use.
Register or Query a Smart Wallet
Use the connected MetaMask account (EOA) to register or query a smart wallet onchain using the Notus API:
const FACTORY_ADDRESS = "0xE77f2C7D79B2743d39Ad73DC47a8e9C6416aD3f3";
const getSmartWalletAddress = async (eoa: string) => {
let res = await fetch("https://<baseUrl>/api/v1/wallets/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "<api-key>",
},
body: JSON.stringify({
externallyOwnedAccount: eoa,
factory: FACTORY_ADDRESS,
salt: "0",
}),
});
if (!res.ok) {
res = await fetch(
`https://<baseUrl>/api/v1/wallets/address?externallyOwnedAccount=${eoa}&factory=${FACTORY_ADDRESS}&salt=0`,
{
method: "GET",
headers: {
"x-api-key": "<api-key>",
},
}
);
}
const data = await res.json();
return data.wallet.accountAbstraction;
};
A Factory is a smart contract responsible for creating smart wallets. It helps ensure that wallet addresses are consistent and predictable, even before the wallet is fully deployed. This simplifies wallet creation and management while maintaining compatibility with blockchain standards.
If you're new to the concept of factories, we recommend visiting the What is a Factory? page for a more detailed explanation.
Explanation: This function interacts with the Notus API to either register or query a smart wallet based on the provided Externally Owned Account (EOA).
- The
salt
is a unique value that, when combined with the EOA and factory, generates a deterministic smart wallet address. If no salt is provided, the default value is zero. Each distinct salt creates a new smart wallet for the same EOA and factory, allowing flexibility while ensuring consistent and predictable address generation. - The
POST
request is sent to register a smart wallet if it hasn’t been registered yet. The request uses the EOA, a factory address, and a salt value. - If the
POST
request fails (e.g., because the wallet is already registered), a fallbackGET
request retrieves the existing smart wallet details. - Once the smart wallet address is retrieved, it’s stored in the
accountAbstraction
state for further use.
Note: At this stage, the smart wallet address is not yet deployed onchain. Deployment happens automatically with the user's first onchain transaction (e.g., swap or transfer) via a UserOperation.
Generate a Swap Quote
Request a quote to swap tokens using the smart wallet:
const getSwapQuote = async ({
eoa,
accountAbstraction,
}: {
eoa: string;
accountAbstraction: string;
}) => {
const res = await fetch("https://<baseUrl>/api/v1/crypto/swap", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "<api-key>",
},
body: JSON.stringify({
payGasFeeToken: "<token-address>",
tokenIn: "<token-address>",
tokenOut: "<token-address>",
amountIn: "5",
walletAddress: accountAbstraction,
toAddress: accountAbstraction,
signerAddress: eoa,
chainIdIn: 137,
chainIdOut: 137,
gasFeePaymentMethod: "DEDUCT_FROM_AMOUNT",
}),
});
return await res.json();
};
To swap native tokens like ETH, BNB, or AVAX, use 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
as the token address.
Note: The payGasFeeToken
field should contain the address of an ERC-20 token held in the smart wallet. This token will be used to pay both the partner’s transactionFeePercent
and the gas fees for the UserOperation. In most cases, payGasFeeToken
is the same as token
, typically the token the user already holds in their wallet.
Explanation:
- The
swapParams
object contains details of the swap, including token addresses, wallet address, and fees. - The Notus API returns a swap quote, which is stored in the
quote
state.
Sign and Execute the User Operation
Sign the quote using MetaMask and send the signed message to Notus for execution:
const signingAndExecute = async ({
eoa,
quoteId,
}: {
eoa: `0x${string}`;
quoteId: `0x${string}`;
}) => {
const signature = await account.signMessage({
account: eoa,
message: {
raw: quoteId,
},
});
const res = await fetch("https://<baseUrl>/api/v1/crypto/execute-user-op", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "<api-key>",
},
body: JSON.stringify({
quoteId,
signature,
}),
});
return await res.json();
};
A UserOperation is a pseudo-transaction used in Account Abstraction (ERC-4337).
It is submitted to the EntryPoint contract by bundlers, allowing smart wallets to operate without traditional transactions or native gas.
Explanation:
- The quote is signed using the wallet’s private key.
- The signed operation is sent to Notus for execution, and the resulting hash
userOpHash
confirms the transaction. - Signatures are validated on-chain, transactions won't be executed unless the EOA match