Skip to content

Commit 1ca8a4a

Browse files
authored
test: use address lookup table in max refund test (#620)
* test: use address lookup table in max refund test Signed-off-by: Reinis Martinsons <reinis@umaproject.org> * fix: remove expensive debug msg Signed-off-by: Reinis Martinsons <reinis@umaproject.org> --------- Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
1 parent b8a2182 commit 1ca8a4a

File tree

3 files changed

+100
-17
lines changed

3 files changed

+100
-17
lines changed

programs/svm-spoke/src/instructions/bundle.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ impl RelayerRefundLeaf {
9292

9393
pub fn to_keccak_hash(&self) -> [u8; 32] {
9494
let input = self.to_bytes();
95-
msg!("input: {:?}", input);
9695
keccak::hash(&input).0
9796
}
9897
}

src/SvmUtils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ export async function readEvents(
4343
}
4444

4545
let events = [];
46+
47+
// TODO: Add support for version 0 transactions.
48+
if (txResult.transaction.message.version !== "legacy") return events;
49+
4650
for (const ixBlock of txResult.meta.innerInstructions) {
4751
for (const ix of ixBlock.instructions) {
4852
for (const program of programs) {

test/svm/SvmSpoke.Bundle.ts

Lines changed: 96 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import * as anchor from "@coral-xyz/anchor";
22
import * as crypto from "crypto";
33
import { BN } from "@coral-xyz/anchor";
4-
import { Keypair, PublicKey } from "@solana/web3.js";
4+
import {
5+
AddressLookupTableProgram,
6+
ComputeBudgetProgram,
7+
Keypair,
8+
PublicKey,
9+
TransactionMessage,
10+
VersionedTransaction,
11+
} from "@solana/web3.js";
512
import { assert } from "chai";
613
import { common } from "./SvmSpoke.common";
714
import { MerkleTree } from "@uma/common/dist/MerkleTree";
@@ -612,7 +619,10 @@ describe("svm_spoke.bundle", () => {
612619
it("Execute Max Refunds", async () => {
613620
const relayerRefundLeaves: RelayerRefundLeafType[] = [];
614621

615-
const numberOfRefunds = 5;
622+
// Higher refund count hits message size limit due to refund accounts also being present in calldata. This could be
623+
// resolved by feeding calldata from a buffer account.
624+
const numberOfRefunds = 21;
625+
const maxExtendedAccounts = 30; // Maximum number of accounts that can be added to ALT in a single transaction.
616626

617627
const refundAccounts: anchor.web3.PublicKey[] = [];
618628
const refundAmounts: BN[] = [];
@@ -655,25 +665,95 @@ describe("svm_spoke.bundle", () => {
655665
.accounts({ state, rootBundle, signer: owner })
656666
.rpc();
657667

658-
const remainingAccounts = refundAccounts.map((account) => ({ pubkey: account, isWritable: true, isSigner: false }));
659-
660668
// Verify valid leaf
661669
const proofAsNumbers = proof.map((p) => Array.from(p));
662670

663-
await program.methods
671+
// We will be using Address Lookup Table (ALT), so to test maximum refunds we better add, not only refund accounts,
672+
// but also all static accounts.
673+
const staticAccounts = {
674+
state: state,
675+
rootBundle: rootBundle,
676+
signer: owner,
677+
vault: vault,
678+
tokenProgram: TOKEN_PROGRAM_ID,
679+
mint: mint,
680+
transferLiability,
681+
systemProgram: anchor.web3.SystemProgram.programId,
682+
// Appended by Acnhor `event_cpi` macro:
683+
eventAuthority: PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], program.programId)[0],
684+
program: program.programId,
685+
};
686+
687+
const remainingAccounts = refundAccounts.map((account) => ({ pubkey: account, isWritable: true, isSigner: false }));
688+
689+
// Consolidate all accounts (static and remaining) into a single array for the ALT.
690+
const allAccounts = [...Object.values(staticAccounts), ...refundAccounts];
691+
692+
// Create instructions for creating and extending the ALT.
693+
const [lookupTableInstruction, lookupTableAddress] = await AddressLookupTableProgram.createLookupTable({
694+
authority: payer.publicKey,
695+
payer: payer.publicKey,
696+
recentSlot: await connection.getSlot(),
697+
});
698+
699+
// Submit the ALT creation transaction
700+
await anchor.web3.sendAndConfirmTransaction(
701+
connection,
702+
new anchor.web3.Transaction().add(lookupTableInstruction),
703+
[payer],
704+
{
705+
skipPreflight: true, // Avoids recent slot mismatch in simulation.
706+
}
707+
);
708+
709+
// Extend the ALT with all accounts making sure not to exceed the maximum number of accounts per transaction.
710+
for (let i = 0; i < allAccounts.length; i += maxExtendedAccounts) {
711+
const extendInstruction = AddressLookupTableProgram.extendLookupTable({
712+
lookupTable: lookupTableAddress,
713+
authority: payer.publicKey,
714+
payer: payer.publicKey,
715+
addresses: allAccounts.slice(i, i + maxExtendedAccounts),
716+
});
717+
718+
await anchor.web3.sendAndConfirmTransaction(
719+
connection,
720+
new anchor.web3.Transaction().add(extendInstruction),
721+
[payer],
722+
{
723+
skipPreflight: true, // Avoids recent slot mismatch in simulation.
724+
}
725+
);
726+
}
727+
728+
// Avoids invalid ALT index as ALT might not be active yet on the following tx.
729+
await new Promise((resolve) => setTimeout(resolve, 500));
730+
731+
// Fetch the AddressLookupTableAccount
732+
const lookupTableAccount = (await connection.getAddressLookupTable(lookupTableAddress)).value;
733+
assert(lookupTableAccount !== null, "AddressLookupTableAccount not fetched");
734+
735+
// Build the instruction to execute relayer refund leaf
736+
const executeInstruction = await program.methods
664737
.executeRelayerRefundLeaf(stateAccountData.rootBundleId, leaf, proofAsNumbers)
665-
.accounts({
666-
state: state,
667-
rootBundle: rootBundle,
668-
signer: owner,
669-
vault: vault,
670-
tokenProgram: TOKEN_PROGRAM_ID,
671-
mint: mint,
672-
transferLiability,
673-
systemProgram: anchor.web3.SystemProgram.programId,
674-
})
738+
.accounts(staticAccounts)
675739
.remainingAccounts(remainingAccounts)
676-
.rpc();
740+
.instruction();
741+
742+
// Build the instruction to increase the CU limit as the default 200k is not sufficient.
743+
const computeBudgetInstruction = ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 });
744+
745+
// Create the versioned transaction
746+
const versionedTx = new VersionedTransaction(
747+
new TransactionMessage({
748+
payerKey: payer.publicKey,
749+
recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
750+
instructions: [computeBudgetInstruction, executeInstruction],
751+
}).compileToV0Message([lookupTableAccount])
752+
);
753+
754+
// Sign and submit the versioned transaction.
755+
versionedTx.sign([payer]);
756+
await connection.sendTransaction(versionedTx);
677757
});
678758

679759
it("Increments pending amount to HubPool", async () => {

0 commit comments

Comments
 (0)