Connect to Solana - Node.js quickstart
Get started with MetaMask Connect Solana in a Node.js application. The SDK displays a QR code in the terminal that you scan with the MetaMask mobile app to establish a connection.
Wallet Standard features (getWallet,
wallet.features[...]) are designed for browser environments.
In Node.js, use the multichain core directly via client.core.connect and
client.core.invokeMethod to interact with Solana.
Prerequisites
- Node.js version 20 or later installed.
- A package manager installed, such as npm, Yarn, or pnpm.
- The MetaMask mobile app installed on your phone.
- An Infura API key from the Infura dashboard.
Steps
1. Install MetaMask Connect Solana
Install the Solana client in an existing Node.js project:
- npm
- Yarn
- pnpm
- Bun
npm install @metamask/connect-solana
yarn add @metamask/connect-solana
pnpm add @metamask/connect-solana
bun add @metamask/connect-solana
2. Initialize MetaMask Connect Solana
Create a file (index.mjs) and initialize the client using createSolanaClient.
In Node.js, there is no window.location, so you must set dapp.url explicitly.
The supportedNetworks map uses network names (mainnet, devnet) as keys:
import { createSolanaClient, getInfuraRpcUrls } from '@metamask/connect-solana'
const SOLANA_MAINNET = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'
const solanaClient = await createSolanaClient({
dapp: {
name: 'My Node.js Solana App',
url: 'https://myapp.com',
},
api: {
supportedNetworks: getInfuraRpcUrls({
infuraApiKey: 'YOUR_INFURA_API_KEY',
networks: ['mainnet'],
}),
},
})
createSolanaClient returns a promise. Always await it before using the client.
The client uses a singleton multichain core under the hood; calling it multiple times
returns the same underlying session.
3. Connect to MetaMask
Register a wallet_sessionChanged listener to capture session data, then connect with a Solana scope.
A QR code appears in the terminal. Scan it with the MetaMask mobile app:
let session
solanaClient.core.on('wallet_sessionChanged', s => {
session = s
})
await solanaClient.core.connect([SOLANA_MAINNET], [])
const accounts = session?.sessionScopes?.[SOLANA_MAINNET]?.accounts ?? []
const address = accounts[0]?.split(':').pop()
console.log('Connected:', address)
4. Sign a message
Use invokeMethod to call the signMessage method on the Solana scope:
const message = Buffer.from('Hello from Node.js!', 'utf8').toString('base64')
const result = await solanaClient.core.invokeMethod({
scope: SOLANA_MAINNET,
request: {
method: 'signMessage',
params: {
account: { address },
message,
},
},
})
console.log('Signature:', result.signature)
5. Disconnect
Use disconnect to revoke the Solana scopes. Any EVM or other scopes in the same multichain session stay active.
await solanaClient.disconnect()
console.log('Disconnected')
Listen for session events
Step 3 captures the session with a minimal wallet_sessionChanged listener. For production use, expand the handler to track account changes throughout the session lifecycle:
solanaClient.core.on('wallet_sessionChanged', session => {
if (session?.sessionScopes) {
const solanaAccounts = session.sessionScopes[SOLANA_MAINNET]?.accounts ?? []
console.log('Solana accounts:', solanaAccounts)
} else {
console.log('Session ended')
}
})
Solana CAIP-2 scope reference
| Network | CAIP-2 scope |
|---|---|
| Mainnet | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp |
| Devnet | solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 |
| Testnet | solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z |
Devnet and testnet are supported only in the MetaMask browser extension (such as MetaMask Flask). On mobile—including the QR code flow in this quickstart—only Solana mainnet is supported.
Full example
import { createSolanaClient, getInfuraRpcUrls } from '@metamask/connect-solana'
const SOLANA_MAINNET = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'
const solanaClient = await createSolanaClient({
dapp: {
name: 'My Node.js Solana App',
url: 'https://myapp.com',
},
api: {
supportedNetworks: getInfuraRpcUrls({
infuraApiKey: 'YOUR_INFURA_API_KEY',
networks: ['mainnet'],
}),
},
})
// Capture session data via event before connecting
let session
solanaClient.core.on('wallet_sessionChanged', s => {
session = s
})
// Connect — scan the QR code with the MetaMask mobile app
await solanaClient.core.connect([SOLANA_MAINNET], [])
const accounts = session?.sessionScopes?.[SOLANA_MAINNET]?.accounts ?? []
const address = accounts[0]?.split(':').pop()
console.log('Connected:', address)
// Sign a message
const message = Buffer.from('Hello from Node.js!', 'utf8').toString('base64')
const result = await solanaClient.core.invokeMethod({
scope: SOLANA_MAINNET,
request: {
method: 'signMessage',
params: {
account: { address },
message,
},
},
})
console.log('Signature:', result.signature)
// Disconnect
await solanaClient.disconnect()
console.log('Disconnected')
Run it with:
node index.mjs
Next steps
- Send a legacy Solana transaction.
- Send a versioned Solana transaction.
- Sign a Solana message.
- Use the multichain client to connect to both EVM and Solana from a single session.