Skip to content

Commit 091cf28

Browse files
committed
feat: add ALT to pusher to reduce number of txs
1 parent b9082bb commit 091cf28

File tree

5 files changed

+80
-36
lines changed

5 files changed

+80
-36
lines changed

apps/price_pusher/src/solana/command.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ export default {
8181
type: "number",
8282
default: 6,
8383
} as Options,
84+
"address-lookup-table-account": {
85+
description: "The pubkey of the ALT to use when updating price feeds",
86+
type: "string",
87+
optional: true,
88+
} as Options,
8489
...options.priceConfigFile,
8590
...options.priceServiceEndpoint,
8691
...options.pythContractAddress,
@@ -107,6 +112,7 @@ export default {
107112
maxJitoTipLamports,
108113
jitoBundleSize,
109114
updatesPerJitoBundle,
115+
addressLookupTableAccount,
110116
logLevel,
111117
controllerLogLevel,
112118
} = argv;
@@ -145,12 +151,21 @@ export default {
145151
)
146152
);
147153

154+
const connection = new Connection(endpoint, "processed");
148155
const pythSolanaReceiver = new PythSolanaReceiver({
149-
connection: new Connection(endpoint, "processed"),
156+
connection,
150157
wallet,
151158
pushOracleProgramId: new PublicKey(pythContractAddress),
152159
});
153160

161+
// Fetch the account lookup table if provided
162+
const lookupTableAccount =
163+
(
164+
await connection.getAddressLookupTable(
165+
new PublicKey(addressLookupTableAccount)
166+
)
167+
).value ?? undefined;
168+
154169
let solanaPricePusher;
155170
if (jitoTipLamports) {
156171
const jitoKeypair = Keypair.fromSecretKey(
@@ -168,7 +183,8 @@ export default {
168183
maxJitoTipLamports,
169184
jitoClient,
170185
jitoBundleSize,
171-
updatesPerJitoBundle
186+
updatesPerJitoBundle,
187+
lookupTableAccount
172188
);
173189

174190
onBundleResult(jitoClient, logger.child({ module: "JitoClient" }));
@@ -178,7 +194,8 @@ export default {
178194
hermesClient,
179195
logger.child({ module: "SolanaPricePusher" }),
180196
shardId,
181-
computeUnitPriceMicroLamports
197+
computeUnitPriceMicroLamports,
198+
lookupTableAccount
182199
);
183200
}
184201

apps/price_pusher/src/solana/solana.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
import { SearcherClient } from "jito-ts/dist/sdk/block-engine/searcher";
1515
import { sliceAccumulatorUpdateData } from "@pythnetwork/price-service-sdk";
1616
import { Logger } from "pino";
17-
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
17+
import { AddressLookupTableAccount, LAMPORTS_PER_SOL } from "@solana/web3.js";
1818

1919
const HEALTH_CHECK_TIMEOUT_SECONDS = 60;
2020

@@ -97,7 +97,8 @@ export class SolanaPricePusher implements IPricePusher {
9797
private hermesClient: HermesClient,
9898
private logger: Logger,
9999
private shardId: number,
100-
private computeUnitPriceMicroLamports: number
100+
private computeUnitPriceMicroLamports: number,
101+
private addressLookupTableAccount?: AddressLookupTableAccount
101102
) {}
102103

103104
async updatePriceFeed(priceIds: string[]): Promise<void> {
@@ -126,9 +127,12 @@ export class SolanaPricePusher implements IPricePusher {
126127
return;
127128
}
128129

129-
const transactionBuilder = this.pythSolanaReceiver.newTransactionBuilder({
130-
closeUpdateAccounts: true,
131-
});
130+
const transactionBuilder = this.pythSolanaReceiver.newTransactionBuilder(
131+
{
132+
closeUpdateAccounts: true,
133+
},
134+
this.addressLookupTableAccount
135+
);
132136
await transactionBuilder.addUpdatePriceFeed(
133137
priceFeedUpdateData,
134138
this.shardId
@@ -164,7 +168,8 @@ export class SolanaPricePusherJito implements IPricePusher {
164168
private maxJitoTipLamports: number,
165169
private searcherClient: SearcherClient,
166170
private jitoBundleSize: number,
167-
private updatesPerJitoBundle: number
171+
private updatesPerJitoBundle: number,
172+
private addressLookupTableAccount?: AddressLookupTableAccount
168173
) {}
169174

170175
async getRecentJitoTipLamports(): Promise<number | undefined> {
@@ -215,9 +220,12 @@ export class SolanaPricePusherJito implements IPricePusher {
215220
}
216221

217222
for (let i = 0; i < priceIds.length; i += this.updatesPerJitoBundle) {
218-
const transactionBuilder = this.pythSolanaReceiver.newTransactionBuilder({
219-
closeUpdateAccounts: true,
220-
});
223+
const transactionBuilder = this.pythSolanaReceiver.newTransactionBuilder(
224+
{
225+
closeUpdateAccounts: true,
226+
},
227+
this.addressLookupTableAccount
228+
);
221229
await transactionBuilder.addUpdatePriceFeed(
222230
priceFeedUpdateData.map((x) => {
223231
return sliceAccumulatorUpdateData(

target_chains/solana/sdk/js/pyth_solana_receiver/examples/update_price_feed.ts

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ const SOL_PRICE_FEED_ID =
1111
"0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d";
1212
const ETH_PRICE_FEED_ID =
1313
"0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace";
14+
const PRICE_FEED_IDS = [SOL_PRICE_FEED_ID, ETH_PRICE_FEED_ID];
15+
16+
// Optionally use an account lookup table to reduce tx sizes
17+
const addressLookupTableAccount = new PublicKey(
18+
"5DNCErWQFBdvCxWQXaC1mrEFsvL3ftrzZ2gVZWNybaSX"
19+
);
1420

1521
let keypairFile = "";
1622
if (process.env["SOLANA_KEYPAIR"]) {
@@ -29,21 +35,30 @@ async function main() {
2935
const pythSolanaReceiver = new PythSolanaReceiver({ connection, wallet });
3036

3137
// Get the price update from hermes
32-
const priceUpdateData = await getPriceUpdateData();
33-
console.log(`Posting price update: ${priceUpdateData}`);
38+
const priceUpdateData = await getPriceUpdateData(PRICE_FEED_IDS);
39+
// console.log(`Posting price update: ${priceUpdateData}`);
3440

3541
// The shard indicates which set of price feed accounts you wish to update.
3642
const shardId = 1;
43+
const lookupTableAccount =
44+
(await connection.getAddressLookupTable(addressLookupTableAccount)).value ??
45+
undefined;
46+
const transactionBuilder = pythSolanaReceiver.newTransactionBuilder(
47+
{},
48+
lookupTableAccount
49+
);
3750

38-
const transactionBuilder = pythSolanaReceiver.newTransactionBuilder({});
3951
// Update the price feed accounts for the feed ids in priceUpdateData (in this example, SOL and ETH) and shard id.
4052
await transactionBuilder.addUpdatePriceFeed(priceUpdateData, shardId);
41-
console.log(
42-
"The SOL/USD price update will get posted to:",
43-
pythSolanaReceiver
44-
.getPriceFeedAccountAddress(shardId, SOL_PRICE_FEED_ID)
45-
.toBase58()
46-
);
53+
// Print all price feed accounts that will be updated
54+
for (const priceFeedId of PRICE_FEED_IDS) {
55+
console.log(
56+
`The ${priceFeedId} price update will get posted to:`,
57+
pythSolanaReceiver
58+
.getPriceFeedAccountAddress(shardId, priceFeedId)
59+
.toBase58()
60+
);
61+
}
4762

4863
await transactionBuilder.addPriceConsumerInstructions(
4964
async (
@@ -69,16 +84,12 @@ async function main() {
6984
}
7085

7186
// Fetch price update data from Hermes
72-
async function getPriceUpdateData() {
73-
const priceServiceConnection = new HermesClient(
74-
"https://hermes.pyth.network/",
75-
{}
76-
);
87+
async function getPriceUpdateData(price_feed_ids: string[]) {
88+
const hermesClient = new HermesClient("https://hermes.pyth.network/", {});
7789

78-
const response = await priceServiceConnection.getLatestPriceUpdates(
79-
[SOL_PRICE_FEED_ID, ETH_PRICE_FEED_ID],
80-
{ encoding: "base64" }
81-
);
90+
const response = await hermesClient.getLatestPriceUpdates(price_feed_ids, {
91+
encoding: "base64",
92+
});
8293

8394
return response.binary.data;
8495
}

target_chains/solana/sdk/js/pyth_solana_receiver/src/PythSolanaReceiver.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ export type PythTransactionBuilderConfig = {
6666
closeUpdateAccounts?: boolean;
6767
};
6868

69+
/**
70+
* A stable treasury ID. This ID's corresponding treasury address
71+
* can be cached in an account lookup table in order to reduce the overall txn size.
72+
*/
73+
export const DEFAULT_TREASURY_ID = 0;
74+
6975
/**
7076
* A builder class to build transactions that:
7177
* - Post price updates (fully or partially verified) or update price feed accounts
@@ -470,9 +476,10 @@ export class PythSolanaReceiver {
470476
* Get a new transaction builder to build transactions that interact with the Pyth Solana Receiver program and consume price updates
471477
*/
472478
newTransactionBuilder(
473-
config: PythTransactionBuilderConfig
479+
config: PythTransactionBuilderConfig,
480+
address_lookup_account?: AddressLookupTableAccount
474481
): PythTransactionBuilder {
475-
return new PythTransactionBuilder(this, config);
482+
return new PythTransactionBuilder(this, config, address_lookup_account);
476483
}
477484

478485
/**
@@ -497,7 +504,7 @@ export class PythSolanaReceiver {
497504
const priceFeedIdToPriceUpdateAccount: Record<string, PublicKey> = {};
498505
const closeInstructions: InstructionWithEphemeralSigners[] = [];
499506

500-
const treasuryId = getRandomTreasuryId();
507+
const treasuryId = DEFAULT_TREASURY_ID;
501508

502509
for (const priceUpdateData of priceUpdateDataArray) {
503510
const accumulatorUpdateData = parseAccumulatorUpdateData(
@@ -730,7 +737,7 @@ export class PythSolanaReceiver {
730737
const priceFeedIdToPriceUpdateAccount: Record<string, PublicKey> = {};
731738
const closeInstructions: InstructionWithEphemeralSigners[] = [];
732739

733-
const treasuryId = getRandomTreasuryId();
740+
const treasuryId = DEFAULT_TREASURY_ID;
734741

735742
for (const priceUpdateData of priceUpdateDataArray) {
736743
const accumulatorUpdateData = parseAccumulatorUpdateData(

target_chains/solana/sdk/js/pyth_solana_receiver/src/vaa.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ export const VAA_START = 46;
4242
*
4343
* The first one writes the first `VAA_SPLIT_INDEX` bytes and the second one writes the rest.
4444
*
45-
* This number was chosen as the biggest number such that one can still call `createInstruction`, `initEncodedVaa` and `writeEncodedVaa` in a single Solana transaction.
45+
* This number was chosen as the biggest number such that one can still call `createInstruction`,
46+
* `initEncodedVaa` and `writeEncodedVaa` in a single Solana transaction, while using an address lookup table.
4647
* This way, the packing of the instructions to post an encoded vaa is more efficient.
4748
*/
48-
export const VAA_SPLIT_INDEX = 755;
49+
export const VAA_SPLIT_INDEX = 700;
4950

5051
/**
5152
* Trim the number of signatures of a VAA.

0 commit comments

Comments
 (0)