feat: added web3 bindings for Address Lookup Table Program instructions (#26469)
* feat: added web3 bindings for Address Lookup Table Program * fix: refactoring + addresses PR comments * fix: typos fixed and minor refactoring * add lookup table instruction decoding support + fixes recent slot serialization bug * export lookup table program * linting * fix: type annotations * add tests cases for address lookup table program * fix: alloc encoding buffer properly for seq layouts * fix: typedoc issue Co-authored-by: Antematter <hello@antematter.io> Co-authored-by: Muhammad Saad <msaadahmed039@gmail.com> Co-authored-by: Justin Starry <justin@solana.com>
This commit is contained in:
parent
048b9f670b
commit
e3c9c58032
|
@ -0,0 +1,433 @@
|
|||
import {toBufferLE} from 'bigint-buffer';
|
||||
import * as BufferLayout from '@solana/buffer-layout';
|
||||
|
||||
import * as Layout from './layout';
|
||||
import {PublicKey} from './publickey';
|
||||
import * as bigintLayout from './util/bigint';
|
||||
import {SystemProgram} from './system-program';
|
||||
import {TransactionInstruction} from './transaction';
|
||||
import {decodeData, encodeData, IInstructionInputData} from './instruction';
|
||||
|
||||
export type CreateLookupTableParams = {
|
||||
/** Account used to derive and control the new address lookup table. */
|
||||
authority: PublicKey;
|
||||
/** Account that will fund the new address lookup table. */
|
||||
payer: PublicKey;
|
||||
/** A recent slot must be used in the derivation path for each initialized table. */
|
||||
recentSlot: bigint | number;
|
||||
};
|
||||
|
||||
export type FreezeLookupTableParams = {
|
||||
/** Address lookup table account to freeze. */
|
||||
lookupTable: PublicKey;
|
||||
/** Account which is the current authority. */
|
||||
authority: PublicKey;
|
||||
};
|
||||
|
||||
export type ExtendLookupTableParams = {
|
||||
/** Address lookup table account to extend. */
|
||||
lookupTable: PublicKey;
|
||||
/** Account which is the current authority. */
|
||||
authority: PublicKey;
|
||||
/** Account that will fund the table reallocation.
|
||||
* Not required if the reallocation has already been funded. */
|
||||
payer?: PublicKey;
|
||||
/** List of Public Keys to be added to the lookup table. */
|
||||
addresses: Array<PublicKey>;
|
||||
};
|
||||
|
||||
export type DeactivateLookupTableParams = {
|
||||
/** Address lookup table account to deactivate. */
|
||||
lookupTable: PublicKey;
|
||||
/** Account which is the current authority. */
|
||||
authority: PublicKey;
|
||||
};
|
||||
|
||||
export type CloseLookupTableParams = {
|
||||
/** Address lookup table account to close. */
|
||||
lookupTable: PublicKey;
|
||||
/** Account which is the current authority. */
|
||||
authority: PublicKey;
|
||||
/** Recipient of closed account lamports. */
|
||||
recipient: PublicKey;
|
||||
};
|
||||
|
||||
/**
|
||||
* An enumeration of valid LookupTableInstructionType's
|
||||
*/
|
||||
export type LookupTableInstructionType =
|
||||
| 'CreateLookupTable'
|
||||
| 'ExtendLookupTable'
|
||||
| 'CloseLookupTable'
|
||||
| 'FreezeLookupTable'
|
||||
| 'DeactivateLookupTable';
|
||||
|
||||
type LookupTableInstructionInputData = {
|
||||
CreateLookupTable: IInstructionInputData &
|
||||
Readonly<{
|
||||
recentSlot: bigint;
|
||||
bumpSeed: number;
|
||||
}>;
|
||||
FreezeLookupTable: IInstructionInputData;
|
||||
ExtendLookupTable: IInstructionInputData &
|
||||
Readonly<{
|
||||
numberOfAddresses: bigint;
|
||||
addresses: Array<Uint8Array>;
|
||||
}>;
|
||||
DeactivateLookupTable: IInstructionInputData;
|
||||
CloseLookupTable: IInstructionInputData;
|
||||
};
|
||||
|
||||
/**
|
||||
* An enumeration of valid address lookup table InstructionType's
|
||||
* @internal
|
||||
*/
|
||||
export const LOOKUP_TABLE_INSTRUCTION_LAYOUTS = Object.freeze({
|
||||
CreateLookupTable: {
|
||||
index: 0,
|
||||
layout: BufferLayout.struct<
|
||||
LookupTableInstructionInputData['CreateLookupTable']
|
||||
>([
|
||||
BufferLayout.u32('instruction'),
|
||||
bigintLayout.u64('recentSlot'),
|
||||
BufferLayout.u8('bumpSeed'),
|
||||
]),
|
||||
},
|
||||
FreezeLookupTable: {
|
||||
index: 1,
|
||||
layout: BufferLayout.struct<
|
||||
LookupTableInstructionInputData['FreezeLookupTable']
|
||||
>([BufferLayout.u32('instruction')]),
|
||||
},
|
||||
ExtendLookupTable: {
|
||||
index: 2,
|
||||
layout: BufferLayout.struct<
|
||||
LookupTableInstructionInputData['ExtendLookupTable']
|
||||
>([
|
||||
BufferLayout.u32('instruction'),
|
||||
bigintLayout.u64(),
|
||||
BufferLayout.seq(
|
||||
Layout.publicKey(),
|
||||
BufferLayout.offset(BufferLayout.u32(), -8),
|
||||
'addresses',
|
||||
),
|
||||
]),
|
||||
},
|
||||
DeactivateLookupTable: {
|
||||
index: 3,
|
||||
layout: BufferLayout.struct<
|
||||
LookupTableInstructionInputData['DeactivateLookupTable']
|
||||
>([BufferLayout.u32('instruction')]),
|
||||
},
|
||||
CloseLookupTable: {
|
||||
index: 4,
|
||||
layout: BufferLayout.struct<
|
||||
LookupTableInstructionInputData['CloseLookupTable']
|
||||
>([BufferLayout.u32('instruction')]),
|
||||
},
|
||||
});
|
||||
|
||||
export class AddressLookupTableInstruction {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor() {}
|
||||
|
||||
static decodeInstructionType(
|
||||
instruction: TransactionInstruction,
|
||||
): LookupTableInstructionType {
|
||||
this.checkProgramId(instruction.programId);
|
||||
|
||||
const instructionTypeLayout = BufferLayout.u32('instruction');
|
||||
const index = instructionTypeLayout.decode(instruction.data);
|
||||
|
||||
let type: LookupTableInstructionType | undefined;
|
||||
for (const [layoutType, layout] of Object.entries(
|
||||
LOOKUP_TABLE_INSTRUCTION_LAYOUTS,
|
||||
)) {
|
||||
if ((layout as any).index == index) {
|
||||
type = layoutType as LookupTableInstructionType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!type) {
|
||||
throw new Error(
|
||||
'Invalid Instruction. Should be a LookupTable Instruction',
|
||||
);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
static decodeCreateLookupTable(
|
||||
instruction: TransactionInstruction,
|
||||
): CreateLookupTableParams {
|
||||
this.checkProgramId(instruction.programId);
|
||||
this.checkKeysLength(instruction.keys, 4);
|
||||
|
||||
const {recentSlot} = decodeData(
|
||||
LOOKUP_TABLE_INSTRUCTION_LAYOUTS.CreateLookupTable,
|
||||
instruction.data,
|
||||
);
|
||||
|
||||
return {
|
||||
authority: instruction.keys[1].pubkey,
|
||||
payer: instruction.keys[2].pubkey,
|
||||
recentSlot: Number(recentSlot),
|
||||
};
|
||||
}
|
||||
|
||||
static decodeExtendLookupTable(
|
||||
instruction: TransactionInstruction,
|
||||
): ExtendLookupTableParams {
|
||||
this.checkProgramId(instruction.programId);
|
||||
if (instruction.keys.length < 2) {
|
||||
throw new Error(
|
||||
`invalid instruction; found ${instruction.keys.length} keys, expected at least 2`,
|
||||
);
|
||||
}
|
||||
|
||||
const {addresses} = decodeData(
|
||||
LOOKUP_TABLE_INSTRUCTION_LAYOUTS.ExtendLookupTable,
|
||||
instruction.data,
|
||||
);
|
||||
return {
|
||||
lookupTable: instruction.keys[0].pubkey,
|
||||
authority: instruction.keys[1].pubkey,
|
||||
payer:
|
||||
instruction.keys.length > 2 ? instruction.keys[2].pubkey : undefined,
|
||||
addresses: addresses.map(buffer => new PublicKey(buffer)),
|
||||
};
|
||||
}
|
||||
|
||||
static decodeCloseLookupTable(
|
||||
instruction: TransactionInstruction,
|
||||
): CloseLookupTableParams {
|
||||
this.checkProgramId(instruction.programId);
|
||||
this.checkKeysLength(instruction.keys, 3);
|
||||
|
||||
return {
|
||||
lookupTable: instruction.keys[0].pubkey,
|
||||
authority: instruction.keys[1].pubkey,
|
||||
recipient: instruction.keys[2].pubkey,
|
||||
};
|
||||
}
|
||||
|
||||
static decodeFreezeLookupTable(
|
||||
instruction: TransactionInstruction,
|
||||
): FreezeLookupTableParams {
|
||||
this.checkProgramId(instruction.programId);
|
||||
this.checkKeysLength(instruction.keys, 2);
|
||||
|
||||
return {
|
||||
lookupTable: instruction.keys[0].pubkey,
|
||||
authority: instruction.keys[1].pubkey,
|
||||
};
|
||||
}
|
||||
|
||||
static decodeDeactivateLookupTable(
|
||||
instruction: TransactionInstruction,
|
||||
): DeactivateLookupTableParams {
|
||||
this.checkProgramId(instruction.programId);
|
||||
this.checkKeysLength(instruction.keys, 2);
|
||||
|
||||
return {
|
||||
lookupTable: instruction.keys[0].pubkey,
|
||||
authority: instruction.keys[1].pubkey,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
static checkProgramId(programId: PublicKey) {
|
||||
if (!programId.equals(AddressLookupTableProgram.programId)) {
|
||||
throw new Error(
|
||||
'invalid instruction; programId is not AddressLookupTable Program',
|
||||
);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
static checkKeysLength(keys: Array<any>, expectedLength: number) {
|
||||
if (keys.length < expectedLength) {
|
||||
throw new Error(
|
||||
`invalid instruction; found ${keys.length} keys, expected at least ${expectedLength}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class AddressLookupTableProgram {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor() {}
|
||||
|
||||
static programId: PublicKey = new PublicKey(
|
||||
'AddressLookupTab1e1111111111111111111111111',
|
||||
);
|
||||
|
||||
static createLookupTable(params: CreateLookupTableParams) {
|
||||
const [lookupTableAddress, bumpSeed] = PublicKey.findProgramAddressSync(
|
||||
[params.authority.toBuffer(), toBufferLE(BigInt(params.recentSlot), 8)],
|
||||
this.programId,
|
||||
);
|
||||
|
||||
const type = LOOKUP_TABLE_INSTRUCTION_LAYOUTS.CreateLookupTable;
|
||||
const data = encodeData(type, {
|
||||
recentSlot: BigInt(params.recentSlot),
|
||||
bumpSeed: bumpSeed,
|
||||
});
|
||||
|
||||
const keys = [
|
||||
{
|
||||
pubkey: lookupTableAddress,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: params.authority,
|
||||
isSigner: true,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: params.payer,
|
||||
isSigner: true,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: SystemProgram.programId,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
];
|
||||
|
||||
return [
|
||||
new TransactionInstruction({
|
||||
programId: this.programId,
|
||||
keys: keys,
|
||||
data: data,
|
||||
}),
|
||||
lookupTableAddress,
|
||||
] as [TransactionInstruction, PublicKey];
|
||||
}
|
||||
|
||||
static freezeLookupTable(params: FreezeLookupTableParams) {
|
||||
const type = LOOKUP_TABLE_INSTRUCTION_LAYOUTS.FreezeLookupTable;
|
||||
const data = encodeData(type);
|
||||
|
||||
const keys = [
|
||||
{
|
||||
pubkey: params.lookupTable,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: params.authority,
|
||||
isSigner: true,
|
||||
isWritable: false,
|
||||
},
|
||||
];
|
||||
|
||||
return new TransactionInstruction({
|
||||
programId: this.programId,
|
||||
keys: keys,
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
static extendLookupTable(params: ExtendLookupTableParams) {
|
||||
const type = LOOKUP_TABLE_INSTRUCTION_LAYOUTS.ExtendLookupTable;
|
||||
const data = encodeData(type, {
|
||||
addresses: params.addresses.map(addr => addr.toBytes()),
|
||||
});
|
||||
|
||||
const keys = [
|
||||
{
|
||||
pubkey: params.lookupTable,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: params.authority,
|
||||
isSigner: true,
|
||||
isWritable: false,
|
||||
},
|
||||
];
|
||||
|
||||
if (params.payer) {
|
||||
keys.push(
|
||||
{
|
||||
pubkey: params.payer,
|
||||
isSigner: true,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: SystemProgram.programId,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return new TransactionInstruction({
|
||||
programId: this.programId,
|
||||
keys: keys,
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
static deactivateLookupTable(params: DeactivateLookupTableParams) {
|
||||
const type = LOOKUP_TABLE_INSTRUCTION_LAYOUTS.DeactivateLookupTable;
|
||||
const data = encodeData(type);
|
||||
|
||||
const keys = [
|
||||
{
|
||||
pubkey: params.lookupTable,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: params.authority,
|
||||
isSigner: true,
|
||||
isWritable: false,
|
||||
},
|
||||
];
|
||||
|
||||
return new TransactionInstruction({
|
||||
programId: this.programId,
|
||||
keys: keys,
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
static closeLookupTable(params: CloseLookupTableParams) {
|
||||
const type = LOOKUP_TABLE_INSTRUCTION_LAYOUTS.CloseLookupTable;
|
||||
const data = encodeData(type);
|
||||
|
||||
const keys = [
|
||||
{
|
||||
pubkey: params.lookupTable,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: params.authority,
|
||||
isSigner: true,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: params.recipient,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
];
|
||||
|
||||
return new TransactionInstruction({
|
||||
programId: this.programId,
|
||||
keys: keys,
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
export * from './account';
|
||||
export * from './address-lookup-table-program';
|
||||
export * from './blockhash';
|
||||
export * from './bpf-loader-deprecated';
|
||||
export * from './bpf-loader';
|
||||
|
|
|
@ -135,13 +135,25 @@ export const voteInit = (property: string = 'voteInit') => {
|
|||
};
|
||||
|
||||
export function getAlloc(type: any, fields: any): number {
|
||||
const getItemAlloc = (item: any): number => {
|
||||
if (item.span >= 0) {
|
||||
return item.span;
|
||||
} else if (typeof item.alloc === 'function') {
|
||||
return item.alloc(fields[item.property]);
|
||||
} else if ('count' in item && 'elementLayout' in item) {
|
||||
const field = fields[item.property];
|
||||
if (Array.isArray(field)) {
|
||||
return field.length * getItemAlloc(item.elementLayout);
|
||||
}
|
||||
}
|
||||
// Couldn't determine allocated size of layout
|
||||
return 0;
|
||||
};
|
||||
|
||||
let alloc = 0;
|
||||
type.layout.fields.forEach((item: any) => {
|
||||
if (item.span >= 0) {
|
||||
alloc += item.span;
|
||||
} else if (typeof item.alloc === 'function') {
|
||||
alloc += item.alloc(fields[item.property]);
|
||||
}
|
||||
alloc += getItemAlloc(item);
|
||||
});
|
||||
|
||||
return alloc;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
import {expect, use} from 'chai';
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
|
||||
import {
|
||||
Keypair,
|
||||
AddressLookupTableProgram,
|
||||
Transaction,
|
||||
AddressLookupTableInstruction,
|
||||
Connection,
|
||||
sendAndConfirmTransaction,
|
||||
} from '../src';
|
||||
import {sleep} from '../src/util/sleep';
|
||||
import {helpers} from './mocks/rpc-http';
|
||||
import {url} from './url';
|
||||
|
||||
use(chaiAsPromised);
|
||||
|
||||
describe('AddressLookupTableProgram', () => {
|
||||
it('createAddressLookupTable', () => {
|
||||
const recentSlot = 0;
|
||||
const authorityPubkey = Keypair.generate().publicKey;
|
||||
const payerPubkey = Keypair.generate().publicKey;
|
||||
const [instruction] = AddressLookupTableProgram.createLookupTable({
|
||||
authority: authorityPubkey,
|
||||
payer: payerPubkey,
|
||||
recentSlot,
|
||||
});
|
||||
|
||||
const transaction = new Transaction().add(instruction);
|
||||
const createLutParams = {
|
||||
authority: authorityPubkey,
|
||||
payer: payerPubkey,
|
||||
recentSlot,
|
||||
};
|
||||
expect(transaction.instructions).to.have.length(1);
|
||||
expect(createLutParams).to.eql(
|
||||
AddressLookupTableInstruction.decodeCreateLookupTable(instruction),
|
||||
);
|
||||
});
|
||||
|
||||
it('extendLookupTableWithPayer', () => {
|
||||
const lutAddress = Keypair.generate().publicKey;
|
||||
const authorityPubkey = Keypair.generate().publicKey;
|
||||
const payerPubkey = Keypair.generate().publicKey;
|
||||
|
||||
const addressesToAdd = [
|
||||
Keypair.generate().publicKey,
|
||||
Keypair.generate().publicKey,
|
||||
Keypair.generate().publicKey,
|
||||
Keypair.generate().publicKey,
|
||||
];
|
||||
|
||||
const instruction = AddressLookupTableProgram.extendLookupTable({
|
||||
lookupTable: lutAddress,
|
||||
authority: authorityPubkey,
|
||||
payer: payerPubkey,
|
||||
addresses: addressesToAdd,
|
||||
});
|
||||
const transaction = new Transaction().add(instruction);
|
||||
const extendLutParams = {
|
||||
lookupTable: lutAddress,
|
||||
authority: authorityPubkey,
|
||||
payer: payerPubkey,
|
||||
addresses: addressesToAdd,
|
||||
};
|
||||
expect(transaction.instructions).to.have.length(1);
|
||||
expect(extendLutParams).to.eql(
|
||||
AddressLookupTableInstruction.decodeExtendLookupTable(instruction),
|
||||
);
|
||||
});
|
||||
|
||||
it('extendLookupTableWithoutPayer', () => {
|
||||
const lutAddress = Keypair.generate().publicKey;
|
||||
const authorityPubkey = Keypair.generate().publicKey;
|
||||
|
||||
const addressesToAdd = [
|
||||
Keypair.generate().publicKey,
|
||||
Keypair.generate().publicKey,
|
||||
Keypair.generate().publicKey,
|
||||
Keypair.generate().publicKey,
|
||||
];
|
||||
|
||||
const instruction = AddressLookupTableProgram.extendLookupTable({
|
||||
lookupTable: lutAddress,
|
||||
authority: authorityPubkey,
|
||||
addresses: addressesToAdd,
|
||||
});
|
||||
const transaction = new Transaction().add(instruction);
|
||||
const extendLutParams = {
|
||||
lookupTable: lutAddress,
|
||||
authority: authorityPubkey,
|
||||
payer: undefined,
|
||||
addresses: addressesToAdd,
|
||||
};
|
||||
expect(transaction.instructions).to.have.length(1);
|
||||
expect(extendLutParams).to.eql(
|
||||
AddressLookupTableInstruction.decodeExtendLookupTable(instruction),
|
||||
);
|
||||
});
|
||||
|
||||
it('closeLookupTable', () => {
|
||||
const lutAddress = Keypair.generate().publicKey;
|
||||
const authorityPubkey = Keypair.generate().publicKey;
|
||||
const recipientPubkey = Keypair.generate().publicKey;
|
||||
|
||||
const instruction = AddressLookupTableProgram.closeLookupTable({
|
||||
lookupTable: lutAddress,
|
||||
authority: authorityPubkey,
|
||||
recipient: recipientPubkey,
|
||||
});
|
||||
const transaction = new Transaction().add(instruction);
|
||||
const closeLutParams = {
|
||||
lookupTable: lutAddress,
|
||||
authority: authorityPubkey,
|
||||
recipient: recipientPubkey,
|
||||
};
|
||||
expect(transaction.instructions).to.have.length(1);
|
||||
expect(closeLutParams).to.eql(
|
||||
AddressLookupTableInstruction.decodeCloseLookupTable(instruction),
|
||||
);
|
||||
});
|
||||
|
||||
it('freezeLookupTable', () => {
|
||||
const lutAddress = Keypair.generate().publicKey;
|
||||
const authorityPubkey = Keypair.generate().publicKey;
|
||||
|
||||
const instruction = AddressLookupTableProgram.freezeLookupTable({
|
||||
lookupTable: lutAddress,
|
||||
authority: authorityPubkey,
|
||||
});
|
||||
const transaction = new Transaction().add(instruction);
|
||||
const freezeLutParams = {
|
||||
lookupTable: lutAddress,
|
||||
authority: authorityPubkey,
|
||||
};
|
||||
expect(transaction.instructions).to.have.length(1);
|
||||
expect(freezeLutParams).to.eql(
|
||||
AddressLookupTableInstruction.decodeFreezeLookupTable(instruction),
|
||||
);
|
||||
});
|
||||
|
||||
it('deactivateLookupTable', () => {
|
||||
const lutAddress = Keypair.generate().publicKey;
|
||||
const authorityPubkey = Keypair.generate().publicKey;
|
||||
|
||||
const instruction = AddressLookupTableProgram.deactivateLookupTable({
|
||||
lookupTable: lutAddress,
|
||||
authority: authorityPubkey,
|
||||
});
|
||||
|
||||
const transaction = new Transaction().add(instruction);
|
||||
const deactivateLutParams = {
|
||||
lookupTable: lutAddress,
|
||||
authority: authorityPubkey,
|
||||
};
|
||||
expect(transaction.instructions).to.have.length(1);
|
||||
expect(deactivateLutParams).to.eql(
|
||||
AddressLookupTableInstruction.decodeDeactivateLookupTable(instruction),
|
||||
);
|
||||
});
|
||||
|
||||
if (process.env.TEST_LIVE) {
|
||||
it('live address lookup table actions', async () => {
|
||||
const connection = new Connection(url, 'confirmed');
|
||||
const authority = Keypair.generate();
|
||||
const payer = Keypair.generate();
|
||||
|
||||
const slot = await connection.getSlot('confirmed');
|
||||
const payerMinBalance =
|
||||
await connection.getMinimumBalanceForRentExemption(44 * 10);
|
||||
|
||||
const [createInstruction, lutAddress] =
|
||||
AddressLookupTableProgram.createLookupTable({
|
||||
authority: authority.publicKey,
|
||||
payer: payer.publicKey,
|
||||
recentSlot: slot,
|
||||
});
|
||||
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: payer.publicKey,
|
||||
amount: payerMinBalance,
|
||||
});
|
||||
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: authority.publicKey,
|
||||
amount: payerMinBalance,
|
||||
});
|
||||
|
||||
// Creating a new lut
|
||||
const createLutTransaction = new Transaction();
|
||||
createLutTransaction.add(createInstruction);
|
||||
createLutTransaction.feePayer = payer.publicKey;
|
||||
|
||||
await sendAndConfirmTransaction(
|
||||
connection,
|
||||
createLutTransaction,
|
||||
[authority, payer],
|
||||
{preflightCommitment: 'confirmed'},
|
||||
);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
// Extending a lut without a payer
|
||||
await helpers.airdrop({
|
||||
connection,
|
||||
address: lutAddress,
|
||||
amount: payerMinBalance,
|
||||
});
|
||||
|
||||
const extendWithoutPayerInstruction =
|
||||
AddressLookupTableProgram.extendLookupTable({
|
||||
lookupTable: lutAddress,
|
||||
authority: authority.publicKey,
|
||||
addresses: [...Array(10)].map(() => Keypair.generate().publicKey),
|
||||
});
|
||||
const extendLutWithoutPayerTransaction = new Transaction();
|
||||
extendLutWithoutPayerTransaction.add(extendWithoutPayerInstruction);
|
||||
|
||||
await sendAndConfirmTransaction(
|
||||
connection,
|
||||
extendLutWithoutPayerTransaction,
|
||||
[authority],
|
||||
{preflightCommitment: 'confirmed'},
|
||||
);
|
||||
|
||||
// Extending an lut with a payer
|
||||
const extendWithPayerInstruction =
|
||||
AddressLookupTableProgram.extendLookupTable({
|
||||
lookupTable: lutAddress,
|
||||
authority: authority.publicKey,
|
||||
payer: payer.publicKey,
|
||||
addresses: [...Array(10)].map(() => Keypair.generate().publicKey),
|
||||
});
|
||||
|
||||
const extendLutWithPayerTransaction = new Transaction();
|
||||
extendLutWithPayerTransaction.add(extendWithPayerInstruction);
|
||||
|
||||
await sendAndConfirmTransaction(
|
||||
connection,
|
||||
extendLutWithPayerTransaction,
|
||||
[authority, payer],
|
||||
{preflightCommitment: 'confirmed'},
|
||||
);
|
||||
|
||||
//deactivating the lut
|
||||
const deactivateInstruction =
|
||||
AddressLookupTableProgram.deactivateLookupTable({
|
||||
lookupTable: lutAddress,
|
||||
authority: authority.publicKey,
|
||||
});
|
||||
|
||||
const deactivateLutTransaction = new Transaction();
|
||||
deactivateLutTransaction.add(deactivateInstruction);
|
||||
await sendAndConfirmTransaction(
|
||||
connection,
|
||||
deactivateLutTransaction,
|
||||
[authority],
|
||||
{preflightCommitment: 'confirmed'},
|
||||
);
|
||||
|
||||
// After deactivation, LUTs can be closed *only* after a short perioid of time
|
||||
}).timeout(10 * 1000);
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue