Connect to multichain using Node.js
Get started with MetaMask Connect Multichain in a Node.js application. Connect to EVM and Solana networks simultaneously through a single session. The SDK displays a QR code in the terminal that you scan with MetaMask Mobile.
Node.js has native support for Buffer, crypto, stream, and other modules that require
polyfilling in browser or React Native environments.
Prerequisites
- Node.js version 20 or later installed.
- A package manager installed, such as npm, Yarn, or pnpm.
- MetaMask Mobile installed on your phone.
- An Infura API key from the MetaMask Developer dashboard.
Steps
1. Install dependencies
- npm
- Yarn
- pnpm
- Bun
npm install @metamask/connect-multichain
yarn add @metamask/connect-multichain
pnpm add @metamask/connect-multichain
bun add @metamask/connect-multichain
2. Initialize the multichain client
Create a file (for example, index.mjs) and initialize the client.
In Node.js, there is no window.location, so you must set dapp.url explicitly.
Use getInfuraRpcUrls to generate RPC URLs for all Infura-supported chains:
import {
createMultichainClient,
getInfuraRpcUrls,
} from '@metamask/connect-multichain'
const client = await createMultichainClient({
dapp: {
name: 'My Node.js Multichain App',
url: 'https://myapp.com',
},
api: {
supportedNetworks: getInfuraRpcUrls({
infuraApiKey: 'YOUR_INFURA_API_KEY',
}),
},
})
createMultichainClient is asynccreateMultichainClient returns a promise. Always await it before using the client.
The client is a singleton -- calling it again returns the same instance with merged options.
3. Connect to MetaMask
Connect with both EVM and Solana scopes in a single call. A QR code appears in the terminal -- scan it with MetaMask Mobile:
await client.connect(
['eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
[],
)
const session = await client.getSession()
const ethAccounts = session?.sessionScopes?.['eip155:1']?.accounts ?? []
const solAccounts =
session?.sessionScopes?.['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp']
?.accounts ?? []
console.log('ETH accounts:', ethAccounts)
console.log('SOL accounts:', solAccounts)
The user sees a single approval prompt for all requested chains.
4. Invoke EVM methods
Use invokeMethod with an EVM scope to make JSON-RPC requests.
Read methods route through the RPC node; signing methods route through the wallet:
const ethAddress = ethAccounts[0]?.split(':').pop()
// Read: get balance via RPC node
const balance = await client.invokeMethod({
scope: 'eip155:1',
request: {
method: 'eth_getBalance',
params: [ethAddress, 'latest'],
},
})
console.log('ETH balance:', balance)
// Sign: personal_sign via wallet
const message = '0x' + Buffer.from('Hello from Node.js!', 'utf8').toString('hex')
const signature = await client.invokeMethod({
scope: 'eip155:1',
request: {
method: 'personal_sign',
params: [message, ethAddress],
},
})
console.log('ETH signature:', signature)
5. Invoke Solana methods
Use invokeMethod with a Solana scope. All Solana methods route through the wallet:
const solAddress = solAccounts[0]?.split(':').pop()
const solMessage = Buffer.from('Hello from Node.js!', 'utf8').toString('base64')
const solSignature = await client.invokeMethod({
scope: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
request: {
method: 'signMessage',
params: {
account: { address: solAddress },
message: solMessage,
},
},
})
console.log('SOL signature:', solSignature)
6. Disconnect
// Disconnect all scopes
await client.disconnect()
console.log('Disconnected')
// Or disconnect specific scopes only
// await client.disconnect(['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'])
Listen for session events
Register event listeners before calling connect() to track session changes:
client.on('wallet_sessionChanged', (session) => {
if (session?.sessionScopes) {
const scopes = Object.keys(session.sessionScopes)
console.log('Active scopes:', scopes)
for (const [scope, data] of Object.entries(session.sessionScopes)) {
console.log(` ${scope}:`, data.accounts)
}
} else {
console.log('Session ended')
}
})
Multichain client methods at a glance
| Method | Description |
|---|---|
connect(scopes, caipAccountIds) | Connects to MetaMask with multichain scopes |
getSession() | Returns the current session with approved accounts |
invokeMethod({ scope, request }) | Calls an RPC method on a specific chain using a scope |
disconnect() | Disconnects all scopes and ends the session |
disconnect(scopes) | Disconnects specific scopes without ending the session |
on(event, handler) | Registers an event handler |
getInfuraRpcUrls(apiKey) | Generates Infura RPC URLs keyed by CAIP-2 chain ID |
Full example
import {
createMultichainClient,
getInfuraRpcUrls,
} from '@metamask/connect-multichain'
const ETH_MAINNET = 'eip155:1'
const SOLANA_MAINNET = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'
const client = await createMultichainClient({
dapp: {
name: 'My Node.js Multichain App',
url: 'https://myapp.com',
},
api: {
supportedNetworks: getInfuraRpcUrls({
infuraApiKey: 'YOUR_INFURA_API_KEY',
}),
},
})
// Connect -- scan the QR code with MetaMask Mobile
await client.connect([ETH_MAINNET, SOLANA_MAINNET], [])
const session = await client.getSession()
const ethAddress = session?.sessionScopes?.[ETH_MAINNET]?.accounts?.[0]?.split(':').pop()
const solAddress = session?.sessionScopes?.[SOLANA_MAINNET]?.accounts?.[0]?.split(':').pop()
console.log('ETH:', ethAddress)
console.log('SOL:', solAddress)
// Get ETH balance
const balance = await client.invokeMethod({
scope: ETH_MAINNET,
request: {
method: 'eth_getBalance',
params: [ethAddress, 'latest'],
},
})
console.log('ETH balance:', balance)
// Sign an Ethereum message
const ethMsg = '0x' + Buffer.from('Hello Ethereum!', 'utf8').toString('hex')
const ethSig = await client.invokeMethod({
scope: ETH_MAINNET,
request: {
method: 'personal_sign',
params: [ethMsg, ethAddress],
},
})
console.log('ETH signature:', ethSig)
// Sign a Solana message
const solMsg = Buffer.from('Hello Solana!', 'utf8').toString('base64')
const solSig = await client.invokeMethod({
scope: SOLANA_MAINNET,
request: {
method: 'signMessage',
params: {
account: { address: solAddress },
message: solMsg,
},
},
})
console.log('SOL signature:', solSig)
// Disconnect
await client.disconnect()
console.log('Disconnected')
Run it with:
node index.mjs
Next steps
- Understand multichain scopes for CAIP-2 chain identifiers and CAIP-25 sessions.
- Sign multichain transactions using
invokeMethod. - Send multichain transactions from a single session.
- Create a multichain dapp for a full step-by-step tutorial with React.