Skip to content

Commit 6c5a550

Browse files
committed
add lut init
1 parent ddf1e8c commit 6c5a550

File tree

1 file changed

+133
-4
lines changed

1 file changed

+133
-4
lines changed

sdk/solana/src/ntt.ts

+133-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,99 @@ 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+
361+
yield this.createUnsignedTx({ transaction: tx }, "Ntt.InitializeLUT");
266362
}
267363

268364
async *registerTransceiver(args: {
@@ -294,7 +390,7 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
294390
);
295391
const broadcastIx = await this.program.methods
296392
.broadcastWormholeId()
297-
.accounts({
393+
.accountsStrict({
298394
payer: args.payer.publicKey,
299395
config: this.pdas.configAccount(),
300396
mint: config.mint,
@@ -306,6 +402,8 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
306402
sequence: whAccs.wormholeSequence,
307403
program: this.core.address,
308404
systemProgram: SystemProgram.programId,
405+
clock: web3.SYSVAR_CLOCK_PUBKEY,
406+
rent: web3.SYSVAR_RENT_PUBKEY,
309407
},
310408
})
311409
.instruction();
@@ -346,7 +444,7 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
346444
.instruction(),
347445
this.program.methods
348446
.broadcastWormholePeer({ chainId: toChainId(peer.chain) })
349-
.accounts({
447+
.accountsStrict({
350448
payer: sender,
351449
config: this.pdas.configAccount(),
352450
peer: this.pdas.transceiverPeerAccount(peer.chain),
@@ -357,6 +455,9 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
357455
feeCollector: whAccs.wormholeFeeCollector,
358456
sequence: whAccs.wormholeSequence,
359457
program: this.core.address,
458+
clock: web3.SYSVAR_CLOCK_PUBKEY,
459+
rent: web3.SYSVAR_RENT_PUBKEY,
460+
systemProgram: SystemProgram.programId,
360461
},
361462
})
362463
.instruction(),
@@ -751,7 +852,7 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
751852
.releaseWormholeOutbound({
752853
revertOnDelay: args.revertOnDelay,
753854
})
754-
.accounts({
855+
.accountsStrict({
755856
payer: args.payer,
756857
config: { config: this.pdas.configAccount() },
757858
outboxItem: args.outboxItem,
@@ -764,6 +865,8 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
764865
sequence: whAccs.wormholeSequence,
765866
program: this.core.address,
766867
systemProgram: SystemProgram.programId,
868+
clock: web3.SYSVAR_CLOCK_PUBKEY,
869+
rent: web3.SYSVAR_RENT_PUBKEY,
767870
},
768871
})
769872
.instruction();
@@ -943,6 +1046,32 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
9431046
}
9441047
}
9451048

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

0 commit comments

Comments
 (0)