Pyth push update simulations

gm team, we integrated pyth on sonic svm. everything works nicely.

the problem is the push transaction happens in three tranasctions and the simulation fails for our transaction.

the transaction can be sent with skipPreflight tho.

is there any ideal flow I can use so that it simulations would not fail? due to price feed not being stale.

I followed this example’s method of addPriceConsumerInstructions()

example of what the end user would see.

Gmgm, can you please share a snippet of your code that builds and sends the txs? And can you send the exact error log you’re getting in simulation?

on chain we have price stale checker, we do a check for the price update for both the feed and the update account PriceUpdateV2

client code snippet:

import { Wallet } from "@coral-xyz/anchor";
import {
    InstructionWithEphemeralSigners,
    PythSolanaReceiver,
} from "@pythnetwork/pyth-solana-receiver";
import { Connection } from "@solana/web3.js";
import { AnchorWallet } from "@solana/wallet-adapter-react";
import { HermesClient } from "@pythnetwork/hermes-client";

const HERMES_URL = "https://hermes.pyth.network/";

const SOL_PRICE_FEED_ID =
    "0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d";

export async function postPriceUpdate(
    connection: Connection,
    wallet: AnchorWallet | undefined,
    instructions?: InstructionWithEphemeralSigners[],
) {
    if (!wallet) return;
    const hermesClient = new HermesClient(HERMES_URL);
    const pythSolanaReceiver = new PythSolanaReceiver({
        connection,
        wallet: wallet as Wallet,
    });

    const priceUpdateData = await hermesClient.getLatestPriceUpdates(
        [SOL_PRICE_FEED_ID],
        { encoding: "base64" },
    );
    const transactionBuilder = pythSolanaReceiver.newTransactionBuilder({
        closeUpdateAccounts: true,
    });
    const {
        postInstructions,
        closeInstructions,
    } = await pythSolanaReceiver.buildUpdatePriceFeedInstructions(
        priceUpdateData.binary.data,
        0,
    );
    transactionBuilder.closeInstructions.push(...closeInstructions);
    transactionBuilder.addInstructions(postInstructions);

    // add any instructions here
    if (instructions) {
        await transactionBuilder.addPriceConsumerInstructions(
            async (): Promise<InstructionWithEphemeralSigners[]> => {
                return [...instructions];
            },
        );
    }
    return await transactionBuilder.buildVersionedTransactions({
        computeUnitPriceMicroLamports: 0,
        tightComputeBudget: true,
    });
}

we use the shardId = 0. and if the price is stale after 300 seconds or 5 minutes. we throw an error on chain.

so simulations assume that the price is too old.

I’m assuming this is the reason you have skipPreflight = true in your examples.

what I want to know is if there is a flow that would make the simulations not fail.

If you’re using the pull model (fetching & updating the price on-demand and inlining your app’s instructions,) I would suggest using postPriceUpdate instead of updatePriceFeed. This posts an ephemeral price update to the chain for your app to consume, instead of updating a feed (push model). It’s cheaper as well.

With postPriceUpdate from the sendusd example, I’m able to run the example with skipPreflight=false and I get a wallet popup without a “no simulation” warning:

Devnet tx here.

It’s invoking the SendUSD app’s transfer function, which enforces that the price is no older than 1 hour.

Let me know if that works. If you do want to update the price feed versus posting a single update, lmk and I can help debug that side of things.

Info on single update (Price Update Account) vs updating a feed (Price Feed Account) here: https://docs.pyth.network/price-feeds/use-real-time-data/solana#write-frontend-code

the reason you are able to run this in one txn is that your sendUSD instruction is simple.

I have a complex instruction to create domains. its pretty hefty since we validate domains on chain.

we hardcode the on chain the price update account for safety reasons that’s why we use:

await pythSolanaReceiver.buildUpdatePriceFeedInstructions(
        priceUpdateData.binary.data,
        0,
    );

with shardId 0 or 7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE

because I have noticed some programs don’t check for the owner for price_update_account.owner and in the heat of the moment when I wrote the program 11 months ago, I made sure to include the price update account but with a hardcode check. it did take me 30 minutes to integrate the onchain account price checker. so big ups there.
Screenshot 2025-04-19 at 18.36.59

anywho, I believe I found my answer. I will have to rewrite some logic and make sure that the postPriceUpdate account is verified on our side.

the reason being is we add the price_update_account in the remaining_accounts rather than have it in the list of anchor accounts.

and FYI, everything works nicely in solana. the above is on sonic svm chain.

since Account 7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE | Solscan always gets updated on solana.

thank you @tejas_pyth for your help. much appreciated.

Ah I see, glad you were able to find your answer!

hey @cryptomiester, sorry but I saw that you said you’ve integrated pyth on sonic svm and everything is working, just wonder if you can share the snippet where you fetch the price? A couple tokens should be fine e.g. USDT, USDC, SONIC and maybe a meme token

thanks