Interact with multiple networks
You can use the Multichain API to interact with multiple blockchain networks in MetaMask simultaneously. The API allows you to target specific chains as part of each method call, eliminating the need to detect and switch networks before executing signatures and transactions.
Prerequisites
Steps
1. Set up your project
Establish a connection to MetaMask Flask and set up basic message handling using the
wallet_notify
event:
// Initialize the connection to Flask.
const EXTENSION_ID = "ljfoeinjpaedjfecbmggjgodbgkmjkjk"; // Flask extension ID
const extensionPort = chrome.runtime.connect(EXTENSION_ID)
// Set up message listener for events.
extensionPort.onMessage.addListener((msg) => {
// Format wallet_notify events to be able to read them later.
if (msg.data.method === "wallet_notify") {
console.log("wallet_notify:", {
scope: msg.data.params.scope,
method: msg.data.params.notification.method,
subscription: msg.data.params.notification.params.subscription,
number: msg.data.params.notification.params.result.number
})
return;
}
console.log(msg.data)
})
2. Manage multichain connections
To interact with multiple networks simultaneously, you'll create and manage CAIP-25 multichain connections with MetaMask.
2.1. Check existing connections
Before creating a new connection, check if one already exists using the
wallet_getSession
method.
For example:
extensionPort.postMessage({
type: "caip-x",
data: {
jsonrpc: "2.0",
method: "wallet_getSession",
params: {}
}
});
If the result returns an empty sessionScopes
parameter, then a multichain connection is not active
and you must create a new connection.
2.2. Create a new connection
Create a new connection using the wallet_createSession
method.
Specify which chains and methods your dapp needs access to, using the optionalScopes
parameter.
For example:
extensionPort.postMessage({
type: "caip-x",
data: {
jsonrpc: "2.0",
method: "wallet_createSession",
params: {
optionalScopes: {
"wallet:eip155": { // General Ethereum wallet functions
methods: ["wallet_addEthereumChain"],
notifications: [],
accounts: []
},
"eip155:1": { // Ethereum Mainnet
methods: [
"personal_sign",
"eth_blockNumber",
"eth_gasPrice",
"eth_getBalance",
"eth_getTransactionCount",
"eth_sendTransaction",
"eth_subscribe"
],
notifications: ["eth_subscription"],
accounts: []
},
"eip155:59141": { // Linea Sepolia
methods: [
"personal_sign",
"eth_blockNumber",
"eth_gasPrice",
"eth_getBalance",
"eth_getTransactionCount",
"eth_sendTransaction",
"eth_subscribe"
],
notifications: ["eth_subscription"],
accounts: []
}
}
}
}
});
In optionalScopes
:
- Request access to networks that your dapp intends to interact with. If a requested network is not configured by the MetaMask user, you might need to add the network.
- For each network, request access to Wallet API methods
that your dapp expects to call at any time.
The methods listed in the
sessionScope
of each Multichain API response indicate which wallet capabilities your dapp can use during the connection.
2.3. Check for connection changes
To ensure your dapp responds appropriately to changes in the multichain connection, such as network or
account updates, check for connection changes using the
wallet_sessionChanged
event.
Based on the event data, you can determine whether your dapp needs to request additional permissions
using wallet_createSession
.
extensionPort.onMessage.addListener((msg) => {
// Check for wallet_sessionChanged events.
if (msg.data.method === "wallet_sessionChanged") {
// Update permissions if required.
}
});
3. Invoke Wallet API methods
You can invoke a subset of the Wallet JSON-RPC API methods
on a specified chain using the wallet_invokeMethod
Multichain API method.
The following are example Wallet API functionalities that are compatible with the Multichain API.
3.1. Sign in with Ethereum
You can implement Sign-In with Ethereum (SIWE) by invoking
personal_sign
.
For example:
// Specify an account that the signature will be requested for.
const address = "0xAddress";
const message = `Sign-in request for ${address} at ${new Date().toLocaleString()}`;
// Invoke the personal_sign Wallet API method.
const sign = await extensionPort.postMessage({
type: "caip-x",
data: {
"jsonrpc": "2.0",
method: "wallet_invokeMethod",
params: {
scope: "eip155:1",
request: {
method: "personal_sign",
params: [message, address],
}
}
}
})
3.2. Check balances
You can read gas token balances by invoking
eth_getBalance
.
For example:
extensionPort.postMessage({
type: "caip-x",
data: {
jsonrpc: "2.0",
method: "wallet_invokeMethod",
params: {
scope: "eip155:1",
request: {
method: "eth_getBalance",
params: ["0xAddress", "latest"],
}
}
}
});
3.3. Send transactions
You can send transactions on a specific network, by invoking
eth_sendTransaction
.
For example:
return extensionPort.postMessage({
type: "caip-x",
data: {
jsonrpc: "2.0",
method: "wallet_invokeMethod",
params: {
// Specify a chain ID where the user has sufficient gas.
scope: "eip155:1",
request: {
method: "eth_sendTransaction",
params: [{
from: "0xFromAccount",
to: "0xToAccount",
value: "0x0",
gasLimit: "0x5028",
maxPriorityFeePerGas: "0x3b9aca00",
maxFeePerGas: "0x2540be400",
}]
}
}
}
});