| title | Send Solana Versioned Transactions - MetaMask Connect | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| sidebar_label | Send a versioned transaction | ||||||||
| description | Create, sign, and send Solana versioned (v0) transactions with Address Lookup Tables through MetaMask Connect using @solana/web3.js. | ||||||||
| keywords |
|
Solana versioned transactions (v0) support Address Lookup Tables, which let you reference up to 256 addresses in a single transaction. This is useful for complex operations that would exceed the limits of legacy transactions.
This guide shows you how to create, sign, and send versioned transactions through MetaMask.
-
Follow Step 1 of the quickstart to install the Solana client.
-
Initialize a Solana client using
createSolanaClientand connect to the user's wallet usinggetWalletandstandard:connect:import { createSolanaClient } from '@metamask/connect-solana' import { Connection, PublicKey, SystemProgram, TransactionMessage, VersionedTransaction, AddressLookupTableProgram, } from '@solana/web3.js' const solanaClient = await createSolanaClient({ dapp: { name: 'My Solana Dapp', url: window.location.origin, }, }) const wallet = solanaClient.getWallet() const { accounts } = await wallet.features['standard:connect'].connect() const account = accounts[0] const publicKey = new PublicKey(account.address) const connection = new Connection('https://solana-devnet.infura.io/v3/YOUR_INFURA_API_KEY')
Solana versioned transactions are created in a similar way to legacy transactions.
The only difference is to use the VersionedTransaction class instead of the Transaction class.
The following example shows how to create a simple transfer instruction, format the instruction into a v0-compatible transaction message, and create a versioned transaction that parses the message:
const { blockhash } = await connection.getLatestBlockhash()
const instructions = [
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: publicKey,
lamports: 10,
}),
]
const messageV0 = new TransactionMessage({
payerKey: publicKey,
recentBlockhash: blockhash,
instructions,
}).compileToV0Message()
const transactionV0 = new VersionedTransaction(messageV0)After creating an unsigned versioned transaction, use the wallet's solana:signAndSendTransaction feature to ask the user's MetaMask wallet to sign and send it.
The method returns a promise for an object containing the signature.
const [{ signature }] = await wallet.features[
'solana:signAndSendTransaction'
].signAndSendTransaction({
account,
transaction: transactionV0.serialize(),
chain: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
})
await connection.getSignatureStatus(signature)Use the solana:signTransaction feature when you need a signed versioned transaction but want to submit it yourself.
For example, for offline signing or multisig workflows.
const [{ signedTransaction }] = await wallet.features['solana:signTransaction'].signTransaction({
account,
transaction: transactionV0.serialize(),
chain: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
})
const txSignature = await connection.sendRawTransaction(signedTransaction)Create an Address Lookup Table (ALT) to efficiently load addresses into tables, significantly increasing the number of addresses that can be used in a single transaction.
Use the createLookupTable method to create the instruction needed to create a new ALT and determine its address.
With this instruction, create a transaction, sign it, and send it to create an ALT onchain.
For example:
const slot = await connection.getSlot()
const { blockhash } = await connection.getLatestBlockhash()
const [lookupTableInst, lookupTableAddress] = AddressLookupTableProgram.createLookupTable({
authority: publicKey,
payer: publicKey,
recentSlot: slot,
})
const lookupMessage = new TransactionMessage({
payerKey: publicKey,
recentBlockhash: blockhash,
instructions: [lookupTableInst],
}).compileToV0Message()
const lookupTransaction = new VersionedTransaction(lookupMessage)
const [{ signature: lookupSignature }] = await wallet.features[
'solana:signAndSendTransaction'
].signAndSendTransaction({
account,
transaction: lookupTransaction.serialize(),
chain: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
})After creating an ALT, extend it by appending addresses to the table.
Use the extendLookupTable method to create a new extend instruction, and send it in a transaction.
For example:
const extendInstruction = AddressLookupTableProgram.extendLookupTable({
payer: publicKey,
authority: publicKey,
lookupTable: lookupTableAddress,
addresses: [publicKey, SystemProgram.programId],
})
const { blockhash: extensionBlockhash } = await connection.getLatestBlockhash()
const extensionMessageV0 = new TransactionMessage({
payerKey: publicKey,
recentBlockhash: extensionBlockhash,
instructions: [extendInstruction],
}).compileToV0Message()
const extensionTransactionV0 = new VersionedTransaction(extensionMessageV0)
const [{ signature: extensionSignature }] = await wallet.features[
'solana:signAndSendTransaction'
].signAndSendTransaction({
account,
transaction: extensionTransactionV0.serialize(),
chain: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
})After creating an ALT, create a versioned transaction with the ALT and ask the user's MetaMask wallet to sign and send it.
First, use the getAddressLookupTable method to fetch the account of the created ALT:
const lookupTableAccount = await connection
.getAddressLookupTable(lookupTableAddress)
.then(res => res.value)
console.log('Table address from cluster:', lookupTableAccount.key.toBase58())Then, parse and read all the addresses currently stored in the fetched ALT:
for (let i = 0; i < lookupTableAccount.state.addresses.length; i++) {
const address = lookupTableAccount.state.addresses[i]
console.log(i, address.toBase58())
}The following example creates a simple transfer instruction, formats the instruction into a v0-compatible transaction message using the ALT's account, and creates a versioned transaction that parses the message.
Sign and send the transaction using the wallet's solana:signAndSendTransaction feature.
const { blockhash: altBlockhash } = await connection.getLatestBlockhash()
const instructions = [
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: publicKey,
lamports: minRent,
}),
]
const messageV0 = new TransactionMessage({
payerKey: publicKey,
recentBlockhash: altBlockhash,
instructions,
}).compileToV0Message([lookupTableAccount])
const transactionV0 = new VersionedTransaction(messageV0)
const [{ signature }] = await wallet.features[
'solana:signAndSendTransaction'
].signAndSendTransaction({
account,
transaction: transactionV0.serialize(),
chain: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
})- Send a legacy transaction for simpler transactions that don't require Address Lookup Tables.
- Sign messages to verify wallet ownership or authorize offchain actions.
- MetaMask Connect Solana methods for the full API reference.
- Use the Multichain SDK to send transactions on both EVM and Solana from a single session.