Skip to content

Commit 2c2e445

Browse files
committed
add lut init
1 parent ddf1e8c commit 2c2e445

File tree

1 file changed

+134
-4
lines changed

1 file changed

+134
-4
lines changed

sdk/solana/src/ntt.ts

+134-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { Program } from "@coral-xyz/anchor";
1+
import { Program, web3 } from "@coral-xyz/anchor";
22
import * as splToken from "@solana/spl-token";
33
import {
44
createAssociatedTokenAccountInstruction,
55
getAssociatedTokenAddressSync,
66
} from "@solana/spl-token";
77
import {
8+
AddressLookupTableAccount,
9+
AddressLookupTableProgram,
810
Connection,
911
Keypair,
1012
PublicKey,
@@ -71,6 +73,7 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
7173
program: Program<NttBindings.NativeTokenTransfer>;
7274
config?: NttBindings.Config;
7375
quoter?: NttQuoter<N, C>;
76+
addressLookupTable?: AddressLookupTableAccount;
7477

7578
constructor(
7679
readonly network: N,
@@ -263,6 +266,100 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
263266
{ transaction: tx, signers: [] },
264267
"Ntt.Initialize"
265268
);
269+
270+
yield* this.initializeOrUpdateLUT({ payer: args.payer });
271+
}
272+
273+
// This function should be called after each upgrade. If there's nothing to
274+
// do, it won't actually submit a transaction, so it's cheap to call.
275+
async *initializeOrUpdateLUT(args: { payer: Keypair }) {
276+
// TODO: find a more robust way of fetching a recent slot
277+
const slot = (await this.program.provider.connection.getSlot()) - 1;
278+
279+
const [_, lutAddress] = web3.AddressLookupTableProgram.createLookupTable({
280+
authority: this.pdas.lutAuthority(),
281+
payer: args.payer.publicKey,
282+
recentSlot: slot,
283+
});
284+
285+
const whAccs = utils.getWormholeDerivedAccounts(
286+
this.program.programId,
287+
this.core.address
288+
);
289+
const config = await this.getConfig();
290+
291+
const entries = {
292+
config: this.pdas.configAccount(),
293+
custody: config.custody,
294+
tokenProgram: config.tokenProgram,
295+
mint: config.mint,
296+
tokenAuthority: this.pdas.tokenAuthority(),
297+
outboxRateLimit: this.pdas.outboxRateLimitAccount(),
298+
wormhole: {
299+
bridge: whAccs.wormholeBridge,
300+
feeCollector: whAccs.wormholeFeeCollector,
301+
sequence: whAccs.wormholeSequence,
302+
program: this.core.address,
303+
systemProgram: SystemProgram.programId,
304+
clock: web3.SYSVAR_CLOCK_PUBKEY,
305+
rent: web3.SYSVAR_RENT_PUBKEY,
306+
},
307+
};
308+
309+
// collect all pubkeys in entries recursively
310+
const collectPubkeys = (obj: any): Array<PublicKey> => {
311+
const pubkeys = new Array<PublicKey>();
312+
for (const key in obj) {
313+
const value = obj[key];
314+
if (value instanceof PublicKey) {
315+
pubkeys.push(value);
316+
} else if (typeof value === "object") {
317+
pubkeys.push(...collectPubkeys(value));
318+
}
319+
}
320+
return pubkeys;
321+
};
322+
const pubkeys = collectPubkeys(entries).map((pk) => pk.toBase58());
323+
324+
var existingLut: web3.AddressLookupTableAccount | null = null;
325+
try {
326+
existingLut = await this.getAddressLookupTable(false);
327+
} catch {
328+
// swallow errors here, it just means that lut doesn't exist
329+
}
330+
331+
if (existingLut !== null) {
332+
const existingPubkeys =
333+
existingLut.state.addresses?.map((a) => a.toBase58()) ?? [];
334+
335+
// if pubkeys contains keys that are not in the existing LUT, we need to
336+
// add them to the LUT
337+
const missingPubkeys = pubkeys.filter(
338+
(pk) => !existingPubkeys.includes(pk)
339+
);
340+
341+
if (missingPubkeys.length === 0) {
342+
return existingLut;
343+
}
344+
}
345+
346+
const ix = await this.program.methods
347+
.initializeLut(new BN(slot))
348+
.accountsStrict({
349+
payer: args.payer.publicKey,
350+
authority: this.pdas.lutAuthority(),
351+
lutAddress,
352+
lut: this.pdas.lutAccount(),
353+
lutProgram: AddressLookupTableProgram.programId,
354+
systemProgram: SystemProgram.programId,
355+
entries,
356+
})
357+
.instruction();
358+
359+
const tx = new Transaction().add(ix);
360+
tx.feePayer = args.payer.publicKey;
361+
362+
yield this.createUnsignedTx({ transaction: tx }, "Ntt.InitializeLUT");
266363
}
267364

268365
async *registerTransceiver(args: {
@@ -294,7 +391,7 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
294391
);
295392
const broadcastIx = await this.program.methods
296393
.broadcastWormholeId()
297-
.accounts({
394+
.accountsStrict({
298395
payer: args.payer.publicKey,
299396
config: this.pdas.configAccount(),
300397
mint: config.mint,
@@ -306,6 +403,8 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
306403
sequence: whAccs.wormholeSequence,
307404
program: this.core.address,
308405
systemProgram: SystemProgram.programId,
406+
clock: web3.SYSVAR_CLOCK_PUBKEY,
407+
rent: web3.SYSVAR_RENT_PUBKEY,
309408
},
310409
})
311410
.instruction();
@@ -346,7 +445,7 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
346445
.instruction(),
347446
this.program.methods
348447
.broadcastWormholePeer({ chainId: toChainId(peer.chain) })
349-
.accounts({
448+
.accountsStrict({
350449
payer: sender,
351450
config: this.pdas.configAccount(),
352451
peer: this.pdas.transceiverPeerAccount(peer.chain),
@@ -357,6 +456,9 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
357456
feeCollector: whAccs.wormholeFeeCollector,
358457
sequence: whAccs.wormholeSequence,
359458
program: this.core.address,
459+
clock: web3.SYSVAR_CLOCK_PUBKEY,
460+
rent: web3.SYSVAR_RENT_PUBKEY,
461+
systemProgram: SystemProgram.programId,
360462
},
361463
})
362464
.instruction(),
@@ -751,7 +853,7 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
751853
.releaseWormholeOutbound({
752854
revertOnDelay: args.revertOnDelay,
753855
})
754-
.accounts({
856+
.accountsStrict({
755857
payer: args.payer,
756858
config: { config: this.pdas.configAccount() },
757859
outboxItem: args.outboxItem,
@@ -764,6 +866,8 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
764866
sequence: whAccs.wormholeSequence,
765867
program: this.core.address,
766868
systemProgram: SystemProgram.programId,
869+
clock: web3.SYSVAR_CLOCK_PUBKEY,
870+
rent: web3.SYSVAR_RENT_PUBKEY,
767871
},
768872
})
769873
.instruction();
@@ -943,6 +1047,32 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
9431047
}
9441048
}
9451049

1050+
async getAddressLookupTable(
1051+
useCache = true
1052+
): Promise<AddressLookupTableAccount> {
1053+
if (!useCache || !this.addressLookupTable) {
1054+
const lut = await this.program.account.lut.fetchNullable(
1055+
this.pdas.lutAccount()
1056+
);
1057+
if (!lut)
1058+
throw new Error(
1059+
"Address lookup table not found. Did you forget to call initializeLUT?"
1060+
);
1061+
1062+
const response = await this.connection.getAddressLookupTable(lut.address);
1063+
if (response.value === null) throw new Error("Could not fetch LUT");
1064+
1065+
this.addressLookupTable = response.value;
1066+
}
1067+
1068+
if (!this.addressLookupTable)
1069+
throw new Error(
1070+
"Address lookup table not found. Did you forget to call initializeLUT?"
1071+
);
1072+
1073+
return this.addressLookupTable;
1074+
}
1075+
9461076
createUnsignedTx(
9471077
txReq: SolanaTransaction,
9481078
description: string,

0 commit comments

Comments
 (0)