Abstract
On Solana, a stake account is not merely a balance‐holding public key. Each stake account delegates distinct rights to two different authorities, a stake authority and a withdraw authority, allowing separate entities to manage delegation and withdrawals. While this dual‑authority model is powerful for delegation and custody workflows, it shatters the notion that one public key equals one owner. This case study dissects the lifecycle of stake accounts, spotlights edge‑cases, and outlines the indexing strategies NODE40 employs to rebuild owner‑centric ledgers from Solana’s raw history. Our focus is reproducible audit trails for investors, accountants, and regulators.
Examples Used in This Paper
The examples found throughout this paper are real examples taken from the Solana blockchain. The data can be independently verified and authenticated as correct using an explorer such as Solscan (https://solscan.io/). Since the examples are real, the state of the accounts on the blockchain will change over time, so screenshots are also provided to better illustrate the activities. None of the examples include accounts owned by or in any way controlled by the author of this paper or NODE40.
What is a Stake Account
A stake account is a dedicated, program‑owned account whose singular purpose is to hold SOL that will be delegated to a validator. The account data includes the active stake amount, delegation details, lock‑up, and two pubkeys that define who may act on it:
- Stake authority: may delegate, redelegate, deactivate, or split the stake.
- Withdraw authority: may withdraw SOL or transfer ownership (by changing the withdraw authority).
By separating these roles Solana allows, for example, an investment manager to control delegation while a custodian such as a CFO retains withdrawal rights. The trade‑off for analysts is that ownership becomes multidimensional and frustratingly difficult to audit.
Important: Solana stake accounts are not Program-Derived Addresses (PDAs). PDAs are created by programs inside a transaction and have no corresponding private key. Stake accounts, by contrast, always start from a client‑generated keypair, whether randomly or seed‑derived. The client (typically a wallet) simply discards the private key after allocation because subsequent operations are authorized through the embedded authority keys, not by signatures from the stake account itself. This fact makes listing all stake accounts that an authority ever controlled impossible without homegrown indexes or painstakingly crawling the blockchain for relevant transactions. Both options are described below.
How Stake Accounts Come to Life
There are three steps involved in the creation of a stake account. They are listed and described below. A screenshot of steps 2 and 3 of this process follows with a real example of an account owner interacting with Marinade Staking1.
- Generate a keypair (off-chain)
This step takes place entirely off‑chain. The wallet (solana‑keygen, Phantom, Ledger, etc.) produces a fresh Ed25519 keypair. This happens client‑side and the private key never enters the blockchain. - Create Account (on-chain)
Creating an account technically involves three steps that are almost always executed as a single instruction:
Create Account = Transfer +Allocate+Assign
Transfer SOL to the new account from step 1.
Allocate space on-chain to store the account data.
Assign ownership to the Stake program.
There are two instruction options here. One is the CreateAccount
instruction and the other is the CreateAccountWithSeed
instruction. Wallets choose CreateAccountWithSeed
when they need a predictable address. E.g., custodial services or scripts that must recreate the account ID later, but at the cost of anyone else being able to precompute that address. The difference between the two is important. With CreateAccount
, the keypair is randomly generated by the client, whereas CreateAccountWithSeed
uses a client-provided seed to deterministically generate the result2. This means that anyone who knows the instruction inputs can re-create the stake account pubkey (we built tooling at NODE40 that does this). When wallets use CreateAccountWithSeed
, a most interesting ability is exposed. See the section “What About CreateAccountWithSeed
?” below for details.
- Initialize via the Stake Program (on-chain)
Theinitialize
instruction converts the freshly allocated System account into a Stake‑program‑owned account. Under the hood, it configures the account with the chosen stake authority and withdraw authority.
Once the transaction is finalized, the account is fully initialized and the System program can no longer mutate or close it. All future operations (delegateStake, split, withdraw,
etc.) must be authorized by the embedded authority keys, not by the stake account’s own private key. In fact, the original keypair can be, and typically is, destroyed by the client at this point. If you view the account in Solscan, you will see FALSE for isOnCurve (indicating that the account has no private key that can sign). See below.

J4XaL61AMPxgwRArzNREHXNb1UifFFvTs6tcizPxLAZQ
has no private key that can sign.
CreateAccount
and Initialize
instructions for stake account AAqBoCnAx3bjGNoHQcoPtV44wRkQBaWQtCYM4nKmuDoL
.In Figure 2 above, instruction #3 shows the account being created by HYvFHTHcXQKqVTWpaV97Z5WA8TZLn4Mceo3BVF59hMLv
and seeded with 25 SOL. Instruction #4 shows HYvFHTHcXQKqVTWpaV97Z5WA8TZLn4Mceo3BVF59hMLv
assigned as the withdraw authority but stWirqFCf2Uts1JBL1Jsd3r6VBWhgnpdPxCTe1MFjrq
(Marinade Native Staking) assigned as the stake authority.
Discovering Stake Accounts for a Given Solana Account
The fastest way to list active stake accounts for a given Solana account is via an RPC endpoint by calling the getProgramAccounts
method on the stake program (ID: Stake11111111111111111111111111111111111111
). This returns all currently‑initialized stake account records.

HYvFHTHcXQKqVTWpaV97Z5WA8TZLn4Mceo3BVF59hMLv
3.The code in figure 3 lists a snapshot of the current accounts for HYvFHTHcXQKqVTWpaV97Z5WA8TZLn4Mceo3BVF59hMLv
but will not include closed accounts. Once all SOL has been transferred from an account, the account is garbage collected by validators and rendered closed4. It can be reopened and used for a different purpose later, which is awful, but something that must be understood by analytics platforms like NODE40.

getProgramAccounts
seen in figure 3.Figure 4 Breakdown
Figure 4 shows the single active stake account for HYvFHTHcXQKqVTWpaV97Z5WA8TZLn4Mceo3BVF59hMLv
. It is the JSON representation of what you would see in Solscan’s “Stake Accounts” tab5.
Line 26 is the pubkey of the stake account. Lines 10-11 show its stake and withdraw authority. Lines 14-19 give details of where the the delegation was made (Asymmetric Research Validator in this case), when the delation was made (epoch 784 – May 8, 2025 through May 10, 2025), and how much SOL was staked (41.5 SOL).

Finding Old Stake Accounts
The limitation of retrieving only current stake accounts makes auditing an account’s stake activity frustratingly difficult. There are two paths to finding all accounts.
Path 1) Crawl the Blockchain
From the account you are auditing, walk every confirmed signature that touches a stake account, then fetch the full transaction and parse the instructions and inner instructions. This is very expensive for long‑lived accounts and easy to miss context if the account changed ownership or was created by splitting from another stake account.
Path 2) Build a Custom Index
Walk the entire blockchain from the first transaction in the first block and parse every transaction, looking for any instruction that interacts with the stake program and recording stake account authority assignment and transfer details. When encountered, record key details of the operation in an index for fast retrieval of any stake account as it relates to any other account.
Which path is better depends on your needs, skill set, available resources, and access to data. Solana is a massive dataset and requires a specialized ability to manage the large volume of data. At NODE40, we maintain a number of indexes. We use them to identify all past and current stake accounts, then crawl the blockchain via getAccountSignatures
, but only for stake accounts. We never use getAccountSignatures
for standard on curve Solana accounts. This may sound strange but we’ve encountered situations where getAccountSingatures
fails to return certain types of transactions, so we get signatures using a different approach that we developed.
What About CreateAccountWithSeed
?
This section is a bit technical and is included to demonstrate the chaos that can emerge in Solana by reusing accounts. It can be skipped (but please don’t because it’s very cool).
In step 2 of the How Stake Accounts Come to Life section above, we explained that CreateAccountWithSeed
is deterministic. While true, it is only deterministic in that you can derive the pubkey if you know the inputs used. Let’s take a look at an example.

CreateAccountWithSeed
.In figure 6 above, stake account J4XaL61AMPxgwRArzNREHXNb1UifFFvTs6tcizPxLAZQ
is created by HYvFHTHcXQKqVTWpaV97Z5WA8TZLn4Mceo3BVF59hMLv and the same account makes itself the stake and withdraw authority6. The instruction used is CreateAccountWithSeed
and provides the seed “stake:0
.”
The inputs to the CreatAccountWithSeed
instruction are Base (HYvFHTHcXQKqVTWpaV97Z5WA8TZLn4Mceo3BVF59hMLv
), Seed (stake:0
), and Program Owner (Stake11111111111111111111111111111111111111
). You can derive the stake account J4XaL61AMPxgwRArzNREHXNb1UifFFvTs6tcizPxLAZQ
yourself from these inputs.

Figure 7 shows how to derive a stake account pubkey that was created with CreateAccountWithSeed
. The algorithm is simple and shows it is deterministic if you know the base and seed (owner for stake is the Stake Program). Does this mean I can do this for stake:1, stake:2, stake:3,
etc? Yes, it does. However, that pattern isn’t a standard. The seed is up to the client. It just so happens that this client used an obvious pattern7.
Conclusion: seeds are managed by clients and should never be assumed, making derivation impossible, or unreliable at best. You cannot rely on the code above to discover historical stake accounts, even if you think you’ve figured out the pattern because there is no way to know if a stake account was created any other way.
Reused Stake Accounts (a wild ride)
As awful as it is, closed accounts in Solana can be reused, even if they had a different owner in the past. A real example of this involves the stake account J4XaL61AMPxgwRArzNREHXNb1UifFFvTs6tcizPxLAZQ
. It was originally created on Jan 22, 2025 at this transaction: https://solscan.io/tx/4kWdi1pTAwhweTkEZMRVrco7p5nN5DE1HSpnVjYDMbjEtbXiAvby8TBVpFHmJMx6FsxghMktDAEm5HStBSs5fg4i.
The wallet that created the account, used CreateAccountWithSeed
and provided stake:0
as the seed.
Then about 25 days later, on Feb 14, 2025 the owner transferred ownership of the account to Marinade to take part in Marinade Liquid staking (getting mSOL in return). You can view the transaction here: https://solscan.io/tx/voJhTgNwPwQ69NYgnD8LJ5Xq4mrtES8yjYpEijXRn5bRT4jaAiVaL1AZh1KhQrMFWVfrTA4anYDbQmsY1kznnpW
Two days later (after the current epoch’s reward payout 😉), Marinade withdrew all the SOL from the stake account, along with that last reward. The account was cleaned up and closed.
Finally, on May 8, 2025, and completely unrelated to all other activity, the original owner staked 41 SOL. The process was exactly as described in the section above “How Stake Accounts Come to Life.” The wallet provided the same seed as before (stake:0
) because the account at that seed was available again after having been closed by the old owner. Even though the account was last owned by a different authority, the account was up for grabs. Auditors do not love this.
The original owner regained control over the account, but that account now has a blended history of transactions for multiple owners. Some from the original owner, some from when Marinade owned it, and now, transactions from the new new owner, which happens to be the original owner.
Summary
Tracking stake exposure on Solana is not a matter of listing balances, it is the art of reconstructing who controlled how many lamports when. Because the stake program stores only current state, analysts must look elsewhere for history. Two viable paths emerge: 1) RPC‑level crawling of every transaction that touches a candidate authority, and 2) maintaining a full, stake‑specific block index. Either way, accuracy hinges on three insights:
- Authority events are ownership events, even at zero lamports.
- A stake account’s identity is {account pubkey, withdraw authority}; either field can change.
- Deterministic seeds mean yesterday’s account can be tomorrow’s blank canvas.
NODE40’s ledger engine encodes these rules, letting us segregate activity, attribute rewards correctly, and surface blended histories that would otherwise be invisible. With that scaffolding, auditors gain a provable view of stake positions, even when Solana’s current state no longer remembers them.
——————
1 Marinade native staking allows a user to deposit SOL into a stake account, then hand off stake authority to the Marinade staking bot while retaining withdraw, and therefore ownership, over the stake account.
2 The term “randomly” here is meant only to refer to not externally deterministic. A wallet may invoke some type of deterministic algorithm, but that would be proprietary to the wallet manufacturer and not “deterministic” to third party observers.
3 There is another offset that can be used to get the stake accounts for which a given account has stake authority, but this paper focuses on withdraw (i.e., ownership) authority.
4 There is no transaction instruction that closes a cleaned up account. You need to check the post transaction balance in a transaction for zero. If it is zero, it can be assumed to be closed as a result of the transaction.
5 Some irrelevant fields have been removed to make the response more compact and less distracting.
6 Creating a stake account and making yourself the staker and withdrawer is incredibly common. It means you have full control and can perform all stake-related operations on the account.
7 The wallet used here was Solflare.