[docs] versioned transactions and lookup tables (#28060)

* feat: added versioned transaction and ALT docs

* fix: relocated links and fixed sidebar parser
This commit is contained in:
Nick Frostbutter 2022-09-26 19:16:18 -04:00 committed by GitHub
parent c7cda6c658
commit c893f92508
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 337 additions and 16 deletions

View File

@ -12,18 +12,10 @@ function CardLayout({
path = "",
}) {
// load the sidebar item from the master `sidebars.js` file
const sidebarItems = (sidebarKey && sidebar?.[sidebarKey]) || [];
let sidebarItems = (sidebarKey && sidebar?.[sidebarKey]) || [];
// process each of the loaded sidebar items for formatting
if (sidebarItems?.length) {
Object.keys(sidebarItems).forEach((key) => {
if (sidebarItems[key]?.type?.toLowerCase() === "category") {
for (let i = 0; i < sidebarItems[key]?.items?.length; i++)
sidebarItems[key].items[i] = formatter(sidebarItems[key].items[i]);
sidebarItems[key].collapsed = true;
} else sidebarItems[key] = formatter(sidebarItems[key]);
});
}
if (sidebarItems?.length) sidebarItems = parseSidebar(sidebarItems);
// return the page layout, ready to go
return (
@ -52,6 +44,18 @@ const computeLabel = (label) => {
return label && label;
};
/*
Recursively parse the sidebar
*/
const parseSidebar = (sidebarItems) => {
Object.keys(sidebarItems).forEach((key) => {
if (sidebarItems[key]?.type?.toLowerCase() === "category") {
sidebarItems[key].items = parseSidebar(sidebarItems[key].items);
} else sidebarItems[key] = formatter(sidebarItems[key]);
});
return sidebarItems;
};
/*
Parser to format a sidebar item to be compatible with the `DocSidebar` component
*/
@ -76,9 +80,9 @@ const formatter = (item) => {
// fix for local routing that does not specify starting at the site root
if (
!(
item?.href.startsWith("/") ||
item?.href.startsWith("http:") ||
item?.href.startsWith("https")
item?.href?.startsWith("/") ||
item?.href?.startsWith("http:") ||
item?.href?.startsWith("https")
)
)
item.href = `/${item?.href}`;

View File

@ -138,9 +138,25 @@ module.exports = {
label: "Accounts",
},
{
type: "doc",
id: "developing/programming-model/transactions",
type: "category",
label: "Transactions",
items: [
{
type: "doc",
id: "developing/programming-model/transactions",
label: "Overview",
},
{
type: "doc",
id: "developing/versioned-transactions",
label: "Versioned Transactions",
},
{
type: "doc",
id: "developing/lookup-tables",
label: "Address Lookup Tables",
},
],
},
{
type: "doc",

View File

@ -470,7 +470,7 @@ Request:
```bash
curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d '
{"jsonrpc": "2.0","id":1,"method":"getBlock","params":[430, {"encoding": "json","transactionDetails":"full","rewards":false}]}
{"jsonrpc": "2.0","id":1,"method":"getBlock","params":[430, {"encoding": "json","maxSupportedTransactionVersion":0,"transactionDetails":"full","rewards":false}]}
'
```

View File

@ -0,0 +1,151 @@
---
title: Address Lookup Tables
description: ""
keywords: ""
---
Address Lookup Tables, commonly referred to as "_lookup tables_" or "_ALTs_" for short, allow developers to create a collection of related addresses to efficiently load more addresses in a single transaction.
Since each transaction on the Solana blockchain requires a listing of every address that is interacted with as part of the transaction, this listing would be effectively be capped at 32 address per transaction. With the help of [Address Lookup Tables](./lookup-tables.md), a transaction would be now be able to raise that limit to 256 addresses per transaction.
## Compressing on chain addresses
After all the desired address have been stored on chain in an Address Lookup Table, each address can be referenced inside a transaction by its 1-byte index within the table (instead of their full 32-byte address). This lookup method effectively "_compresses_" a 32-byte address into a 1-byte index value.
This "_compression_" enables storing up to 256 address in a single lookup table for use inside any given transaction.
## Versioned Transactions
To utilize an Address Lookup Table inside a transaction, developers must use v0 transactions that were introduced with the new [Versioned Transaction format](./versioned-transactions.md).
## How to create an address lookup table
Creating a new lookup table with the `@solana/web3.js` library is similar to the older `legacy` transactions, but with some differences.
Using the `@solana/web3.js` library, you can use the [`createLookupTable`](https://solana-labs.github.io/solana-web3.js/classes/AddressLookupTableProgram.html#createLookupTable) function to construct the instruction needed to create a new lookup table, as well as determine its address:
```js
const web3 = require("@solana/web3.js");
// connect to a cluster and get the current `slot`
const connection = new web3.Connection(web3.clusterApiUrl("devnet"));
const slot = await connection.getSlot();
// Assumption:
// `payer` is a valid `Keypair` with enough SOL to pay for the execution
const [lookupTableInst, lookupTableAddress] =
web3.AddressLookupTableProgram.createLookupTable({
authority: payer.publicKey,
payer: payer.publicKey,
recentSlot: slot,
});
console.log("lookup table address:", lookupTableAddress.toBase58());
// To create the Address Lookup Table on chain:
// send the `lookupTableInst` instruction in a transaction
```
> NOTE:
> Address lookup tables can be **created** with either a `v0` transaction or a `legacy` transaction. But the Solana runtime can only retrieve and handle the additional addresses within a lookup table while using [v0 Versioned Transactions](./versioned-transactions.md#current-transaction-versions).
## Add addresses to a lookup table
Adding addresses to a lookup table is known as "_extending_". Using the the `@solana/web3.js` library, you can create a new _extend_ instruction using the [`extendLookupTable`](https://solana-labs.github.io/solana-web3.js/classes/AddressLookupTableProgram.html#extendLookupTable) method:
```js
// add addresses to the `lookupTableAddress` table via an `extend` instruction
const extendInstruction = web3.AddressLookupTableProgram.extendLookupTable({
payer: payer.publicKey,
authority: payer.publicKey,
lookupTable: lookupTableAddress,
addresses: [
payer.publicKey,
web3.SystemProgram.programId,
// list more `publicKey` addresses here
],
});
// Send this `extendInstruction` in a transaction to the cluster
// to insert the listing of `addresses` into your lookup table with address `lookupTableAddress`
```
> NOTE:
> Due to the same memory limits of `legacy` transactions, any transaction used to _extend_ an Address Lookup Table is also limited in how many addresses can be added at a time. Because of this, you will need to use multiple transactions to _extend_ any table with more addresses (~20) that can fit withing a single transaction's memory limits.
Once these address have been inserted into the table, and stored on chain, you will be able to utilize the Address Lookup Table in future transactions. Enabling up to 256 address in those future transactions.
## Fetch an Address Lookup Table
Similar to requesting another account (or PDA) from the cluster, you can fetch a complete Address Lookup Table with the [`getAddressLookupTable`](https://solana-labs.github.io/solana-web3.js/classes/Connection.html#getAddressLookupTable) method:
```js
// define the `PublicKey` of the lookup table to fetch
const lookupTableAddress = new web3.PublicKey("");
// get the table from the cluster
const lookupTableAccount = await connection
.getAddressLookupTable(lookupTableAddress)
.then((res) => res.value);
// `lookupTableAccount` will now be a `AddressLookupTableAccount` object
console.log("Table address from cluster:", lookupTableAccount.key.toBase58());
```
Our `lookupTableAccount` variable will now be a `AddressLookupTableAccount` object which we can parse to read the listing of all the addresses stored on chain in the lookup table:
```js
// loop through and parse all the address stored in the table
for (let i = 0; i < lookupTableAccount.state.addresses.length; i++) {
const address = lookupTableAccount.state.addresses[i];
console.log(i, address.toBase58());
}
```
## How to use an address lookup table in a transaction
After you have created your lookup table, and stored your needed address on chain (via extending the lookup table), you can create a `v0` transaction to utilize the on chain lookup capabilities.
Just like older `legacy` transactions, you can create all the [instructions](./../terminology.md#instruction) your transaction will execute on chain. You can then provide an array of these instructions to the [Message](./../terminology.md#message) used in the `v0 transaction.
> NOTE:
> The instructions used inside a `v0` transaction can be constructed using the same methods and functions used to create the instructions in the past. There is no required change to the instructions used involving an Address Lookup Table.
```js
// Assumptions:
// - `arrayOfInstructions` has been created as an `array` of `TransactionInstruction`
// - we are are using the `lookupTableAccount` obtained above
// construct a v0 compatible transaction `Message`
const messageV0 = new web3.TransactionMessage({
payerKey: payer.publicKey,
recentBlockhash: blockhash,
instructions: arrayOfInstructions, // note this is an array of instructions
}).compileToV0Message([lookupTableAccount]);
// create a v0 transaction from the v0 message
const transactionV0 = new web3.VersionedTransaction(messageV0);
// sign the v0 transaction using the file system wallet we created named `payer`
transactionV0.sign([payer]);
// send and confirm the transaction
// (NOTE: There is NOT an array of Signers here; see the note below...)
const txid = await web3.sendAndConfirmTransaction(connection, transactionV0);
console.log(
`Transaction: https://explorer.solana.com/tx/${txidV0}?cluster=devnet`,
);
```
> NOTE:
> When sending a `VersionedTransaction` to the cluster, it must be signed BEFORE calling the
> `sendAndConfirmTransaction` method. If you pass an array of `Signer`
> (like with `legacy` transactions) the method will trigger an error!
## More Resources
- Read the [proposal](./../proposals/transactions-v2.md) for Address Lookup Tables and Versioned transactions
- [Example Rust program using Address Lookup Tables](https://github.com/TeamRaccoons/address-lookup-table-multi-swap)

View File

@ -0,0 +1,150 @@
---
title: Versioned Transactions
description: ""
keywords: ""
---
[Versioned Transactions](./versioned-transactions.md) are the new transaction format that allow for additional functionality in the Solana runtime, including [Address Lookup Tables](./lookup-tables.md).
While changes to [on chain](./on-chain-programs/overview.md) programs are **NOT** required to support the new functionality of versioned transactions (or for backwards compatibility), developers **WILL** need update their client side code to prevent [errors due to different transaction versions](#max-supported-transaction-version).
## Current Transaction Versions
The Solana runtime supports two transaction versions:
- `legacy` - older transaction format with no additional benefit
- `0` - added support for [Address Lookup Tables](./lookup-tables.md)
## Max supported transaction version
All RPC requests that return a transaction **_should_** specify the highest version of transactions they will support in their application using the `maxSupportedTransactionVersion` option. Including [`getBlock`](./clients/jsonrpc-api.md#getblock) and [`getTransaction`](./clients/jsonrpc-api.md#gettransaction),
An RPC request will fail if a [Versioned Transaction](./versioned-transactions.md) is returned that is higher than the set `maxSupportedTransactionVersion`. (i.e. if a version `0` transaction is returned when `legacy` is selected)
> WARNING:
> If no `maxSupportedTransactionVersion` value is set, then only `legacy` transactions will be allowed in the RPC response. Therefore, your RPC requests **WILL** fail if any version `0` transactions are returned.
## How to set max supported version
You can set the `maxSupportedTransactionVersion` using both the [`@solana/web3.js`](https://solana-labs.github.io/solana-web3.js/) library and JSON formatted requests directly to an RPC endpoint.
### Using web3.js
Using the [`@solana/web3.js`](https://solana-labs.github.io/solana-web3.js/) library, you can retrieve the most recent block or get a specific transaction:
```js
// connect to the `devnet` cluster and get the current `slot`
const connection = new web3.Connection(web3.clusterApiUrl("devnet"));
const slot = await connection.getSlot();
// get the latest block (allowing for v0 transactions)
const block = await connection.getBlock(slot, {
maxSupportedTransactionVersion: 0,
});
// get a specific transaction (allowing for v0 transactions)
const getTx = await connection.getTransaction(
"3jpoANiFeVGisWRY5UP648xRXs3iQasCHABPWRWnoEjeA93nc79WrnGgpgazjq4K9m8g2NJoyKoWBV1Kx5VmtwHQ",
{
maxSupportedTransactionVersion: 0,
},
);
```
### JSON requests to the RPC
Using a standard JSON formatted POST request, you can set the `maxSupportedTransactionVersion` when retrieving a specific block:
```bash
curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d \
'{"jsonrpc": "2.0", "id":1, "method": "getBlock", "params": [430, {
"encoding":"json",
"maxSupportedTransactionVersion":0,
"transactionDetails":"full",
"rewards":false
}]}'
```
## How create a Versioned Transaction
Versioned transactions can be created similar to the older method of creating transactions. There are differences in using certain libraries that should be noted.
Below is an example of how to create a Versioned Transaction, using the `@solana/web3.js` library, to send perform a SOL transfer between two accounts.
#### Notes:
- `payer` is a valid `Keypair` wallet, funded with SOL
- `toAccount` a valid `Keypair`
Firstly, import the web3.js library and create a `connection` to your desired cluster.
We then define the recent `blockhash` and `minRent` we will need for our transaction and the account.
```js
const web3 = require("@solana/web3.js");
// connect to the cluster and get the minimum rent for rent exempt status
const connection = new web3.Connection(web3.clusterApiUrl("devnet"));
let minRent = await connection.getMinimumBalanceForRentExemption(0);
let blockhash = await connection
.getLatestBlockhash()
.then((res) => res.blockhash);
```
Create an `array` of all the `instructions` you desire to send in your transaction. In this example below, we are creating a simple SOL transfer instruction:
```js
// create an array with your desires `instructions`
const instructions = [
web3.SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: toAccount.publicKey,
lamports: minRent,
}),
];
```
Next, construct a `MessageV0` formatted transaction message with your desired `instructions`:
```js
// create v0 compatible message
const messageV0 = new web3.TransactionMessage({
payerKey: payer.publicKey,
recentBlockhash: blockhash,
instructions,
}).compileToV0Message();
```
Then, create a new `VersionedTransaction`, passing in our v0 compatible message:
```js
const transaction = new web3.VersionedTransaction(messageV0);
// sign your transaction with the required `Signers`
transaction.sign([payer]);
```
You can sign the transaction by either:
- passing an array of `signatures` into the `VersionedTransaction` method, or
- call the `transaction.sign()` method, passing an array of the required `Signers`
> NOTE:
> After calling the `transaction.sign()` method, all the previous transaction `signatures` will be fully replaced by new signatures created from the provided in `Signers`.
After your `VersionedTransaction` has been signed by all required accounts, you can send it to the cluster and `await` the response.
```js
// send our v0 transaction to the cluster
const txid = await connection.sendTransaction(transaction);
console.log(`https://explorer.solana.com/tx/${txid}?cluster=devnet`);
```
> NOTE:
> Unlike `legacy` transactions, sending a `VersionedTransaction` via `sendTransaction` does **NOT** support transaction signing via passing in an array of `Signers` as the second parameter. You will need to sign the transaction before calling `connection.sendTransaction()`.
## More Resources
- using [Versioned Transactions for Address Lookup Tables](./lookup-tables.md#how-to-create-an-address-lookup-table)
- view an [example of a v0 transaction](https://explorer.solana.com/tx/3jpoANiFeVGisWRY5UP648xRXs3iQasCHABPWRWnoEjeA93nc79WrnGgpgazjq4K9m8g2NJoyKoWBV1Kx5VmtwHQ/?cluster=devnet) on Solana Explorer
- read the [accepted proposal](./../proposals/transactions-v2.md) for Versioned Transaction and Address Lookup Tables