| name | setup-solana-browser-app |
| description | Set up a vanilla browser (non-React) app with @metamask/connect-solana using wallet-standard features directly. Use when integrating MetaMask Solana without a framework or wallet adapter library. |
Setup Solana Browser App with MetaMask
When to use
Use this skill when:
- Integrating MetaMask with Solana in a vanilla JavaScript or non-React browser app
- Using wallet-standard features directly without
@solana/wallet-adapter-react
- Building connect, sign, and send flows with the
SolanaClient API
- Accessing wallet-standard features like
solana:signTransaction or solana:signMessage directly
Workflow
Step 1: Install dependencies
npm install @metamask/connect-solana @metamask/connect-multichain @solana/web3.js
@metamask/connect-multichain is a regular dependency of @metamask/connect-solana and is installed transitively. (Only the 2.0.0 release briefly made it a peer dependency; 2.1.0 reverted that.) Installing it explicitly is harmless but not required. The SDK warns at runtime if duplicate or mismatched copies are resolved.
Step 2: Create the Solana client
import { createSolanaClient } from '@metamask/connect-solana';
const solanaClient = await createSolanaClient({
dapp: {
name: 'My Solana DApp',
url: window.location.href,
},
api: {
supportedNetworks: {
mainnet: 'https://api.mainnet-beta.solana.com',
},
},
});
createSolanaClient returns Promise<SolanaClient>:
| Property | Type | Description |
|---|
core | MultichainCore | The underlying multichain client instance |
getWallet() | () => Wallet | Returns the wallet-standard Wallet (from @wallet-standard/base) |
registerWallet() | () => Promise<void> | Manually register the wallet (auto-called unless skipAutoRegister: true) |
disconnect() | () => Promise<void> | Disconnect and revoke Solana scopes |
Step 3: Get the wallet and connect
const wallet = solanaClient.getWallet();
console.log('Wallet name:', wallet.name);
console.log('Available features:', Object.keys(wallet.features));
Connect using the standard:connect feature:
const connectFeature = wallet.features['standard:connect'];
const { accounts } = await connectFeature.connect();
if (accounts.length > 0) {
const account = accounts[0];
console.log('Address:', account.address);
console.log('Public key:', account.publicKey);
console.log('Chains:', account.chains);
}
Step 4: Access wallet-standard features
The wallet exposes these wallet-standard features:
| Feature Key | Description |
|---|
standard:connect | Connect and request accounts |
standard:disconnect | Disconnect the wallet |
standard:events | Subscribe to account/chain change events |
solana:signIn | Sign-In-With-Solana (SIWS) authentication |
solana:signTransaction | Sign a transaction without sending |
solana:signAndSendTransaction | Sign and broadcast a transaction |
solana:signMessage | Sign an arbitrary message |
There is no solana:signAndSendAllTransactions feature — to batch, pass multiple inputs to signAndSendTransaction(...inputs) (it is variadic and returns one result per input).
Step 5: Sign a message
const signMessageFeature = wallet.features['solana:signMessage'];
const message = new TextEncoder().encode('Hello from MetaMask on Solana!');
const [{ signature }] = await signMessageFeature.signMessage({
account: accounts[0],
message,
});
console.log('Signature:', Buffer.from(signature).toString('hex'));
Step 6: Sign and send a transaction
import {
Connection,
Transaction,
SystemProgram,
PublicKey,
LAMPORTS_PER_SOL,
} from '@solana/web3.js';
const connection = new Connection('https://api.mainnet-beta.solana.com');
const { blockhash } = await connection.getLatestBlockhash();
const senderPubkey = new PublicKey(accounts[0].address);
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: senderPubkey,
toPubkey: new PublicKey('11111111111111111111111111111112'),
lamports: 0.001 * LAMPORTS_PER_SOL,
}),
);
transaction.recentBlockhash = blockhash;
transaction.feePayer = senderPubkey;
const serializedTransaction = transaction.serialize({
requireAllSignatures: false,
});
const signAndSendFeature = wallet.features['solana:signAndSendTransaction'];
const [{ signature: txSignature }] = await signAndSendFeature.signAndSendTransaction({
account: accounts[0],
transaction: serializedTransaction,
chain: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
});
const signatureBase58 = bs58.encode(txSignature);
console.log('Transaction signature:', signatureBase58);
const confirmation = await connection.confirmTransaction(signatureBase58, 'confirmed');
console.log('Confirmed:', confirmation);
Step 7: Listen for account and chain changes
const eventsFeature = wallet.features['standard:events'];
eventsFeature.on('change', ({ accounts: newAccounts }) => {
if (newAccounts) {
console.log('Accounts changed:', newAccounts.map((a) => a.address));
}
});
Step 8: Disconnect
await solanaClient.disconnect();
Step 9: Full working example
import { createSolanaClient } from '@metamask/connect-solana';
import {
Connection,
Transaction,
SystemProgram,
PublicKey,
LAMPORTS_PER_SOL,
} from '@solana/web3.js';
async function main() {
const solanaClient = await createSolanaClient({
dapp: {
name: 'My Solana DApp',
url: window.location.href,
},
api: {
supportedNetworks: {
mainnet: 'https://api.mainnet-beta.solana.com',
},
},
});
const wallet = solanaClient.getWallet();
const connectFeature = wallet.features['standard:connect'];
const { accounts } = await connectFeature.connect();
const account = accounts[0];
console.log('Connected:', account.address);
const signMessageFeature = wallet.features['solana:signMessage'];
const [{ signature }] = await signMessageFeature.signMessage({
account,
message: new TextEncoder().encode('Hello Solana!'),
});
console.log('Message signed');
const connection = new Connection('https://api.mainnet-beta.solana.com');
const { blockhash } = await connection.getLatestBlockhash();
const senderPubkey = new PublicKey(account.address);
const tx = new Transaction().add(
SystemProgram.transfer({
fromPubkey: senderPubkey,
toPubkey: new PublicKey('11111111111111111111111111111112'),
lamports: 0.001 * LAMPORTS_PER_SOL,
}),
);
tx.recentBlockhash = blockhash;
tx.feePayer = senderPubkey;
const signAndSendFeature = wallet.features['solana:signAndSendTransaction'];
const [{ signature: txSig }] = await signAndSendFeature.signAndSendTransaction({
account,
transaction: tx.serialize({ requireAllSignatures: false }),
chain: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
});
console.log('Transaction sent');
await solanaClient.disconnect();
}
main().catch(console.error);
Important Notes