Skip to content

Commit d54be3f

Browse files
committed
test(svm): fill test with kit and lut
Signed-off-by: Pablo Maldonado <[email protected]>
1 parent 14bb4bd commit d54be3f

File tree

4 files changed

+343
-107
lines changed

4 files changed

+343
-107
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"@openzeppelin/contracts-upgradeable": "4.9.6",
5555
"@scroll-tech/contracts": "^0.1.0",
5656
"@solana-developers/helpers": "^2.4.0",
57+
"@solana-program/address-lookup-table": "^0.7.0",
5758
"@solana-program/token": "^0.5.1",
5859
"@solana/kit": "^2.1.0",
5960
"@solana/spl-token": "^0.4.6",

src/svm/web3-v2/solanaProgramUtils.ts

+111-8
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,52 @@
11
import { BorshEventCoder, Idl, utils } from "@coral-xyz/anchor";
22
import web3, {
33
Address,
4+
AddressesByLookupTableAddress,
5+
appendTransactionMessageInstruction,
6+
appendTransactionMessageInstructions,
47
Commitment,
8+
compressTransactionMessageUsingAddressLookupTables as compressTxWithAlt,
9+
getSignatureFromTransaction,
510
GetSignaturesForAddressApi,
611
GetTransactionApi,
12+
IInstruction,
13+
KeyPairSigner,
14+
pipe,
15+
Rpc,
16+
RpcSubscriptions,
717
RpcTransport,
18+
sendAndConfirmTransactionFactory,
819
Signature,
20+
SignatureNotificationsApi,
21+
signTransactionMessageWithSigners,
22+
SlotNotificationsApi,
23+
SolanaRpcApiFromTransport,
924
} from "@solana/kit";
1025

26+
import {
27+
fetchAddressLookupTable,
28+
findAddressLookupTablePda,
29+
getCreateLookupTableInstructionAsync,
30+
getExtendLookupTableInstruction,
31+
} from "@solana-program/address-lookup-table";
32+
import { createDefaultTransaction, signAndSendTransaction } from "../../../test/svm/utils";
33+
1134
type GetTransactionReturnType = ReturnType<GetTransactionApi["getTransaction"]>;
1235

1336
type GetSignaturesForAddressConfig = Parameters<GetSignaturesForAddressApi["getSignaturesForAddress"]>[1];
1437

1538
type GetSignaturesForAddressTransaction = ReturnType<GetSignaturesForAddressApi["getSignaturesForAddress"]>[number];
1639

40+
export type RpcClient = {
41+
rpc: Rpc<SolanaRpcApiFromTransport<RpcTransport>>;
42+
rpcSubscriptions: RpcSubscriptions<SignatureNotificationsApi & SlotNotificationsApi>;
43+
};
44+
1745
/**
1846
* Reads all events for a specific program.
1947
*/
2048
export async function readProgramEvents(
21-
rpc: web3.Rpc<web3.SolanaRpcApiFromTransport<RpcTransport>>,
49+
rpc: RpcClient,
2250
program: Address,
2351
anchorIdl: Idl,
2452
finality: Commitment = "confirmed",
@@ -45,14 +73,14 @@ export async function readProgramEvents(
4573
}
4674

4775
async function searchSignaturesUntilLimit(
48-
rpc: web3.Rpc<web3.SolanaRpcApiFromTransport<RpcTransport>>,
76+
client: RpcClient,
4977
program: Address,
5078
options: GetSignaturesForAddressConfig = { limit: 1000 }
5179
): Promise<GetSignaturesForAddressTransaction[]> {
5280
const allSignatures: GetSignaturesForAddressTransaction[] = [];
5381
// Fetch all signatures in sequential batches
5482
while (true) {
55-
const signatures = await rpc.getSignaturesForAddress(program, options).send();
83+
const signatures = await client.rpc.getSignaturesForAddress(program, options).send();
5684
allSignatures.push(...signatures);
5785

5886
// Update options for the next batch. Set before to the last fetched signature.
@@ -69,13 +97,15 @@ async function searchSignaturesUntilLimit(
6997
* Reads events from a transaction.
7098
*/
7199
export async function readEvents(
72-
rpc: web3.Rpc<web3.SolanaRpcApiFromTransport<RpcTransport>>,
100+
client: RpcClient,
73101
txSignature: Signature,
74102
programId: Address,
75103
programIdl: Idl,
76104
commitment: Commitment = "confirmed"
77105
) {
78-
const txResult = await rpc.getTransaction(txSignature, { commitment, maxSupportedTransactionVersion: 0 }).send();
106+
const txResult = await client.rpc
107+
.getTransaction(txSignature, { commitment, maxSupportedTransactionVersion: 0 })
108+
.send();
79109

80110
if (txResult === null) return [];
81111

@@ -132,16 +162,89 @@ async function processEventFromTx(
132162
* For a given fillStatusPDa & associated spokePool ProgramID, return the fill event.
133163
*/
134164
export async function readFillEventFromFillStatusPda(
135-
rpc: web3.Rpc<web3.SolanaRpcApiFromTransport<RpcTransport>>,
165+
client: RpcClient,
136166
fillStatusPda: Address,
137167
programId: Address,
138168
programIdl: Idl
139169
): Promise<{ event: any; slot: number }> {
140-
const signatures = await searchSignaturesUntilLimit(rpc, fillStatusPda);
170+
const signatures = await searchSignaturesUntilLimit(client, fillStatusPda);
141171
if (signatures.length === 0) return { event: null, slot: 0 };
142172

143173
// The first signature will always be PDA creation, and therefore CPI event carrying signature. Any older signatures
144174
// will therefore be either spam or PDA closure signatures and can be ignored when looking for the fill event.
145-
const events = await readEvents(rpc, signatures[signatures.length - 1].signature, programId, programIdl);
175+
const events = await readEvents(client, signatures[signatures.length - 1].signature, programId, programIdl);
146176
return { event: events[0], slot: Number(signatures[signatures.length - 1].slot) };
147177
}
178+
179+
export async function createAlt(client: RpcClient, authority: KeyPairSigner): Promise<Address> {
180+
const recentSlot = await client.rpc.getSlot({ commitment: "finalized" }).send();
181+
182+
const [alt] = await findAddressLookupTablePda({
183+
authority: authority.address,
184+
recentSlot,
185+
});
186+
187+
const createAltIx = await getCreateLookupTableInstructionAsync({
188+
authority,
189+
recentSlot,
190+
});
191+
192+
await pipe(
193+
await createDefaultTransaction(client, authority),
194+
(tx) => appendTransactionMessageInstruction(createAltIx, tx),
195+
(tx) => signAndSendTransaction(client, tx)
196+
);
197+
198+
return alt;
199+
}
200+
201+
export async function extendAlt(client: RpcClient, authority: KeyPairSigner, alt: Address, addresses: Address[]) {
202+
const extendAltIx = getExtendLookupTableInstruction({
203+
address: alt,
204+
authority,
205+
payer: authority,
206+
addresses,
207+
});
208+
209+
await pipe(
210+
await createDefaultTransaction(client, authority),
211+
(tx) => appendTransactionMessageInstruction(extendAltIx, tx),
212+
(tx) => signAndSendTransaction(client, tx)
213+
);
214+
215+
const altAccount = await fetchAddressLookupTable(client.rpc, alt);
216+
217+
const addressesByLookupTableAddress: AddressesByLookupTableAddress = {};
218+
addressesByLookupTableAddress[alt] = altAccount.data.addresses;
219+
220+
// Delay a second here to let lookup table warm up
221+
await sleep(1000);
222+
223+
return addressesByLookupTableAddress;
224+
}
225+
226+
async function sleep(ms: number) {
227+
return new Promise((resolve) => setTimeout(resolve, ms));
228+
}
229+
230+
export async function sendTransactionWithAlt(
231+
client: RpcClient,
232+
payer: KeyPairSigner,
233+
instructions: IInstruction[],
234+
addressesByLookupTableAddress: AddressesByLookupTableAddress
235+
) {
236+
return pipe(
237+
await createDefaultTransaction(client, payer),
238+
(tx) => appendTransactionMessageInstructions(instructions, tx),
239+
(tx) => compressTxWithAlt(tx, addressesByLookupTableAddress),
240+
(tx) => signTransactionMessageWithSigners(tx),
241+
async (tx) => {
242+
const signedTx = await tx;
243+
await sendAndConfirmTransactionFactory(client)(signedTx, {
244+
commitment: "confirmed",
245+
skipPreflight: false,
246+
});
247+
return getSignatureFromTransaction(signedTx);
248+
}
249+
);
250+
}

0 commit comments

Comments
 (0)