Send a versioned transaction
Solana versioned transactions (v0) support Address Lookup Tables, which let you reference up to 256 addresses in a single transaction — 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.
Prerequisites
Set up a Solana client and connect to the user's wallet:
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://api.devnet.solana.com')
Create a versioned transaction
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)
Sign and send a versioned transaction
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)
Create an Address Lookup Table
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',
})
Extend an Address Lookup Table
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',
})
Create, sign, and send a versioned transaction with an ALT
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',
})