feat(solana_utils): support account lookup table (#1424)
* feat: support account lookup table * remove console log * Support ALT * Commas * feat: support lta * Go * Bump
This commit is contained in:
parent
bb830e1760
commit
62d189e3b5
|
@ -59582,7 +59582,7 @@
|
||||||
},
|
},
|
||||||
"target_chains/solana/sdk/js/solana_utils": {
|
"target_chains/solana/sdk/js/solana_utils": {
|
||||||
"name": "@pythnetwork/solana-utils",
|
"name": "@pythnetwork/solana-utils",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@coral-xyz/anchor": "^0.29.0",
|
"@coral-xyz/anchor": "^0.29.0",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@pythnetwork/solana-utils",
|
"name": "@pythnetwork/solana-utils",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"description": "Utility functions for Solana",
|
"description": "Utility functions for Solana",
|
||||||
"homepage": "https://pyth.network",
|
"homepage": "https://pyth.network",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
AddressLookupTableAccount,
|
||||||
ComputeBudgetProgram,
|
ComputeBudgetProgram,
|
||||||
Keypair,
|
Keypair,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
|
@ -81,4 +82,33 @@ it("Unit test for getSizeOfTransaction", async () => {
|
||||||
expect(versionedTransaction.serialize().length).toBe(
|
expect(versionedTransaction.serialize().length).toBe(
|
||||||
getSizeOfTransaction(ixsToSend)
|
getSizeOfTransaction(ixsToSend)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const addressLookupTable: AddressLookupTableAccount =
|
||||||
|
new AddressLookupTableAccount({
|
||||||
|
key: PublicKey.unique(),
|
||||||
|
state: {
|
||||||
|
lastExtendedSlot: 0,
|
||||||
|
lastExtendedSlotStartIndex: 0,
|
||||||
|
deactivationSlot: BigInt(0),
|
||||||
|
addresses: [
|
||||||
|
SystemProgram.programId,
|
||||||
|
ComputeBudgetProgram.programId,
|
||||||
|
...ixsToSend[0].keys.map((key) => key.pubkey),
|
||||||
|
...ixsToSend[1].keys.map((key) => key.pubkey),
|
||||||
|
...ixsToSend[2].keys.map((key) => key.pubkey),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const versionedTransactionWithAlt = new VersionedTransaction(
|
||||||
|
new TransactionMessage({
|
||||||
|
recentBlockhash: transaction.recentBlockhash,
|
||||||
|
payerKey: payer.publicKey,
|
||||||
|
instructions: ixsToSend,
|
||||||
|
}).compileToV0Message([addressLookupTable])
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(versionedTransactionWithAlt.serialize().length).toBe(
|
||||||
|
getSizeOfTransaction(ixsToSend, true, addressLookupTable)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Wallet } from "@coral-xyz/anchor";
|
import { Wallet } from "@coral-xyz/anchor";
|
||||||
import {
|
import {
|
||||||
|
AddressLookupTableAccount,
|
||||||
ComputeBudgetProgram,
|
ComputeBudgetProgram,
|
||||||
Connection,
|
Connection,
|
||||||
PACKET_DATA_SIZE,
|
PACKET_DATA_SIZE,
|
||||||
|
@ -63,7 +64,7 @@ export const DEFAULT_PRIORITY_FEE_CONFIG: PriorityFeeConfig = {
|
||||||
* - A compact array of instructions
|
* - A compact array of instructions
|
||||||
*
|
*
|
||||||
* If the transaction is a `VersionedTransaction`, it also contains an extra byte at the beginning, indicating the version and an array of `MessageAddressTableLookup` at the end.
|
* If the transaction is a `VersionedTransaction`, it also contains an extra byte at the beginning, indicating the version and an array of `MessageAddressTableLookup` at the end.
|
||||||
* We don't support Account Lookup Tables, so that array has a size of 0.
|
* After this field there is an array of indexes into the address lookup table that represents the accounts from the address lookup table used in the transaction.
|
||||||
*
|
*
|
||||||
* Each instruction has the following layout :
|
* Each instruction has the following layout :
|
||||||
* - One byte indicating the index of the program in the account addresses array
|
* - One byte indicating the index of the program in the account addresses array
|
||||||
|
@ -72,13 +73,16 @@ export const DEFAULT_PRIORITY_FEE_CONFIG: PriorityFeeConfig = {
|
||||||
*/
|
*/
|
||||||
export function getSizeOfTransaction(
|
export function getSizeOfTransaction(
|
||||||
instructions: TransactionInstruction[],
|
instructions: TransactionInstruction[],
|
||||||
versionedTransaction = true
|
versionedTransaction = true,
|
||||||
|
addressLookupTable?: AddressLookupTableAccount
|
||||||
): number {
|
): number {
|
||||||
|
const programs = new Set<string>();
|
||||||
const signers = new Set<string>();
|
const signers = new Set<string>();
|
||||||
const accounts = new Set<string>();
|
let accounts = new Set<string>();
|
||||||
|
|
||||||
instructions.map((ix) => {
|
instructions.map((ix) => {
|
||||||
accounts.add(ix.programId.toBase58()),
|
programs.add(ix.programId.toBase58());
|
||||||
|
accounts.add(ix.programId.toBase58());
|
||||||
ix.keys.map((key) => {
|
ix.keys.map((key) => {
|
||||||
if (key.isSigner) {
|
if (key.isSigner) {
|
||||||
signers.add(key.pubkey.toBase58());
|
signers.add(key.pubkey.toBase58());
|
||||||
|
@ -98,6 +102,19 @@ export function getSizeOfTransaction(
|
||||||
)
|
)
|
||||||
.reduce((a, b) => a + b, 0);
|
.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
|
let numberOfAddressLookups = 0;
|
||||||
|
if (addressLookupTable) {
|
||||||
|
const lookupTableAddresses = addressLookupTable.state.addresses.map(
|
||||||
|
(address) => address.toBase58()
|
||||||
|
);
|
||||||
|
const totalNumberOfAccounts = accounts.size;
|
||||||
|
accounts = new Set(
|
||||||
|
[...accounts].filter((account) => !lookupTableAddresses.includes(account))
|
||||||
|
);
|
||||||
|
accounts = new Set([...accounts, ...programs, ...signers]);
|
||||||
|
numberOfAddressLookups = totalNumberOfAccounts - accounts.size; // This number is equal to the number of accounts that are in the lookup table and are neither signers nor programs
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
getSizeOfCompressedU16(signers.size) +
|
getSizeOfCompressedU16(signers.size) +
|
||||||
signers.size * 64 + // array of signatures
|
signers.size * 64 + // array of signatures
|
||||||
|
@ -107,7 +124,10 @@ export function getSizeOfTransaction(
|
||||||
32 + // recent blockhash
|
32 + // recent blockhash
|
||||||
getSizeOfCompressedU16(instructions.length) +
|
getSizeOfCompressedU16(instructions.length) +
|
||||||
instruction_sizes + // array of instructions
|
instruction_sizes + // array of instructions
|
||||||
(versionedTransaction ? 1 + getSizeOfCompressedU16(0) : 0) // we don't support Account Lookup Tables
|
(versionedTransaction ? 1 + getSizeOfCompressedU16(0) : 0) + // transaction version and number of address lookup tables
|
||||||
|
(versionedTransaction && addressLookupTable ? 32 : 0) + // address lookup table address (we only support 1 address lookup table)
|
||||||
|
(versionedTransaction && addressLookupTable ? 2 : 0) + // number of address lookup indexes
|
||||||
|
numberOfAddressLookups // address lookup indexes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,11 +150,17 @@ export class TransactionBuilder {
|
||||||
}[] = [];
|
}[] = [];
|
||||||
readonly payer: PublicKey;
|
readonly payer: PublicKey;
|
||||||
readonly connection: Connection;
|
readonly connection: Connection;
|
||||||
|
readonly addressLookupTable: AddressLookupTableAccount | undefined;
|
||||||
|
|
||||||
/** Make a new `TransactionBuilder`. It requires a `payer` to populate the `payerKey` field and a connection to populate `recentBlockhash` in the versioned transactions. */
|
/** Make a new `TransactionBuilder`. It requires a `payer` to populate the `payerKey` field and a connection to populate `recentBlockhash` in the versioned transactions. */
|
||||||
constructor(payer: PublicKey, connection: Connection) {
|
constructor(
|
||||||
|
payer: PublicKey,
|
||||||
|
connection: Connection,
|
||||||
|
accountLookupTable?: AddressLookupTableAccount
|
||||||
|
) {
|
||||||
this.payer = payer;
|
this.payer = payer;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
this.addressLookupTable = accountLookupTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,11 +175,16 @@ export class TransactionBuilder {
|
||||||
computeUnits: computeUnits ?? 0,
|
computeUnits: computeUnits ?? 0,
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
getSizeOfTransaction([
|
getSizeOfTransaction(
|
||||||
...this.transactionInstructions[this.transactionInstructions.length - 1]
|
[
|
||||||
.instructions,
|
...this.transactionInstructions[
|
||||||
|
this.transactionInstructions.length - 1
|
||||||
|
].instructions,
|
||||||
instruction,
|
instruction,
|
||||||
]) <= PACKET_DATA_SIZE_WITH_ROOM_FOR_COMPUTE_BUDGET
|
],
|
||||||
|
true,
|
||||||
|
this.addressLookupTable
|
||||||
|
) <= PACKET_DATA_SIZE_WITH_ROOM_FOR_COMPUTE_BUDGET
|
||||||
) {
|
) {
|
||||||
this.transactionInstructions[
|
this.transactionInstructions[
|
||||||
this.transactionInstructions.length - 1
|
this.transactionInstructions.length - 1
|
||||||
|
@ -218,7 +249,9 @@ export class TransactionBuilder {
|
||||||
recentBlockhash: blockhash,
|
recentBlockhash: blockhash,
|
||||||
instructions: instructionsWithComputeBudget,
|
instructions: instructionsWithComputeBudget,
|
||||||
payerKey: this.payer,
|
payerKey: this.payer,
|
||||||
}).compileToV0Message()
|
}).compileToV0Message(
|
||||||
|
this.addressLookupTable ? [this.addressLookupTable] : []
|
||||||
|
)
|
||||||
),
|
),
|
||||||
signers: signers,
|
signers: signers,
|
||||||
};
|
};
|
||||||
|
@ -289,9 +322,14 @@ export class TransactionBuilder {
|
||||||
payer: PublicKey,
|
payer: PublicKey,
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
instructions: InstructionWithEphemeralSigners[],
|
instructions: InstructionWithEphemeralSigners[],
|
||||||
priorityFeeConfig: PriorityFeeConfig
|
priorityFeeConfig: PriorityFeeConfig,
|
||||||
|
addressLookupTable?: AddressLookupTableAccount
|
||||||
): Promise<{ tx: VersionedTransaction; signers: Signer[] }[]> {
|
): Promise<{ tx: VersionedTransaction; signers: Signer[] }[]> {
|
||||||
const transactionBuilder = new TransactionBuilder(payer, connection);
|
const transactionBuilder = new TransactionBuilder(
|
||||||
|
payer,
|
||||||
|
connection,
|
||||||
|
addressLookupTable
|
||||||
|
);
|
||||||
transactionBuilder.addInstructions(instructions);
|
transactionBuilder.addInstructions(instructions);
|
||||||
return transactionBuilder.buildVersionedTransactions(priorityFeeConfig);
|
return transactionBuilder.buildVersionedTransactions(priorityFeeConfig);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue