pyth-network / pyth-network/pyth-crosschain

[price_pusher/solana] Encoded VAA accounts leak when an update tx fails (18.7k accounts / 143 SOL on one pusher wallet)

Open
#4,049 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
247
Forks
348
Avg merge
2d 1h
Merged PRs (30d)
27

Description

Summary

The Solana price pusher (apps/price_pusher/src/solana/solana.ts, v13.0.0) relies on closeUpdateAccounts: true, so the closeEncodedVaa instructions are appended to the end of the transaction batch built by buildVersionedTransactions. If any transaction in that batch fails, sendTransactions throws, updatePriceFeed logs the error and returns, and the encoded VAA account is never closed. Nothing retries the close later. The SDK already has addClosePreviousEncodedVaasInstructions() for exactly this case (its doc comment describes it), but price_pusher never calls it.

The most common failure I see is update_price_feed running out of compute. With tightComputeBudget: true it gets the hard-coded UPDATE_PRICE_FEED_COMPUTE_BUDGET = 55_000, and some updates need more than that.

Evidence (mainnet, 2026-09-16)

Wallet 4p16wya1Vw2u9w22oah4yXQgySb6eWKRRLMsEXCreish pushes shard-0 sponsored feeds through pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT. Is this a Pyth-operated pusher?

  • It is the write authority of 18,734 open EncodedVaa accounts under HDwcJBJXjL9FpJ7UBsYBtaDjsBUhuLCUYoz3zr8SWWaQ, holding 143.11 SOL of rent. The count went up by 4 over a few hours today.
  • The oldest leftovers I found date back to 2025: 73LkscNLkJS56thftKSLiWM31EURromdA1Tu3bC4YzZe (2025-09-07, status Writing) and 838BC4LMBqUgDP4adEaEgjHf9QCbyZadYfzJS8qUNWrR (2025-12-25, status Verified).
  • None of the sampled leaked accounts was touched again after its failed transaction.

Example 1: buffer FHxy8nAq5SEwXEKTMGZWbf6j8pEZPT1ppiHhYR6wmdUL

  1. init_encoded_vaa + write_encoded_vaa: ok
  2. write_encoded_vaa + verify_encoded_vaa_v1 + update_price_feed: ok
  3. update_price_feed ×2: ok
  4. update_price_feed ×2: ok
  5. update_price_feed (+ close): failed, tx 1274WAypYJVmyCXefvuDhfBE43jDY3i5yN8jGW2xzG1wY1tDt8asxnT2GzUwtaYErazuqcRRWuX8JfqYiycvYRmi
Program rec5EKMGg6MxZYaMdyBfgwp4d5rB9T1VQH5pJv5LtFJ failed: exceeded CUs meter at BPF instruction
Program pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT consumed 55000 of 55000 compute units

Example 2: buffer Fkdq7321xi9qRs9LsHgy4JvXTN92dw8rxsZi3KJE6YQ9. Its second transaction (write + verify + update_price_feed) failed with ComputationalBudgetExceeded at 408,000 CU (350k verify + 55k update + 3k write): 4RS4wkHxEd25jSjsqwWqPorN1P71aXnJgBSENkD8B8rCpRAqYrYKbZ5f7k5gbjEXZjuheBhuX6fpiAJ6Uy5pXWLE

The rent is recoverable. I simulated a transaction with 10 closeEncodedVaa instructions as that write authority (sigVerify: false), and it succeeds on verified accounts.

This isn't limited to one wallet. Across mainnet there are currently 127,129 open EncodedVaa accounts (~998 SOL) from 634 write authorities, so other SDK users probably hit the same path.

Suggested fixes

  1. price_pusher: after a failed update (or every N minutes), call addClosePreviousEncodedVaasInstructions() and send the resulting transactions.
  2. SDK: make UPDATE_PRICE_FEED_COMPUTE_BUDGET configurable, or add headroom when tightComputeBudget is set. Some updates currently need more than 55k.
  3. SDK: add dataSlice: { offset: 0, length: 0 } to findEncodedVaaAccountsByWriteAuthority. It currently downloads the full data of every account (about 1 KB each), which gets heavy with thousands of leftovers.

One-off cleanup

For the existing accounts, something like the following, run in a loop until nothing is left:

const builder = pythSolanaReceiver.newTransactionBuilder({});
await builder.addClosePreviousEncodedVaasInstructions(500);
const txs = await builder.buildVersionedTransactions({ computeUnitPriceMicroLamports: 10_000 });
await sendTransactions(txs, pythSolanaReceiver.connection, pythSolanaReceiver.wallet);

Happy to help test a fix.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in apps/price_pusher/src/solana/solana.ts and trace updatePriceFeed, sendTransactions, and the SDK method addClosePreviousEncodedVaasInstructions(). Compare the failed-transaction path with the existing closeUpdateAccounts flow and review the SDK handling of UPDATE_PRICE_FEED_COMPUTE_BUDGET and findEncodedVaaAccountsByWriteAuthority. Done means the agreed failure-recovery behavior prevents leaked EncodedVaa accounts, with the selected SDK changes validated against the reported cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
blockchain
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.