Assessment reports>Pye>Critical findings>Uninitialized transient stake account can be stolen
Category: Coding Mistakes

Uninitialized transient stake account can be stolen

Critical Impact
Critical Severity
Medium Likelihood

Description

When a solo validator bond has matured, a user can call the handle_maturity instruction to mark the bond as matured. The instruction also handles transient stake accounts, merging the transient stake account into the bond's stake account if it is fully active:

// ...

let transient_stake_status = get_stake_status(
    &ctx.accounts.transient_stake_account,
    ctx.accounts.clock.epoch,
    &ctx.accounts.stake_history,
)?;

// Handle existing transient account
match transient_stake_status {
    StakeStatus::FullyActive => {
        // merge it with the main stake account
        ctx.accounts.stake_merge(
            ctx.accounts.stake_account.to_account_info(),
            ctx.accounts.transient_stake_account.to_account_info(),
            ctx.accounts.bond.to_account_info(),
            &ctx.accounts.bond,
        )?;
    }
    StakeStatus::Empty => return Ok(()),
    _ => return Err(ErrorCode::UnsupportedStakeState.into()),
}

// ...

Merging the transient stake account sets its stake state to uninitialized and relocates all of its lamports to the bond's stake account.

Once the transaction is finished, since the transient stake account has zero lamports, the account will be closed as it does not have enough to cover rent.

However, an attacker can stop the transient stake account from being closed by sending enough lamports for rent in the same transaction. This leaves the transient stake account in the uninitialized state. An attacker could then reinitialize the transient stake account, stealing it by setting their own account as the authorized staker and withdrawer.

Impact

When redeeming yield tokens (YTs) for lamports or stake, the SoloValidatorBond::calculate_lamports_for_yt method is used to calculate the yield for YTs, which includes the lamports of the transient stake account in its calculations.

If an attacker owned the transient stake account, they could deposit lamports into the transient stake account, redeem their YT for lamports or stake, and then immediately withdraw their deposit from the transient stake account.

Since the deposited lamports are taken into account in the yield calculations, they get a larger return on their YT than intended at no cost.

This could be used to steal funds from the bond's stake account or drain the global counter party (GCP).

Recommendations

Rework the bond implementation to allow for multiple transient stake accounts and not depend on their balance for yield calculations.

Remediation

This issue has been acknowledged by Pye in the Sky Labs Ltd., and fixes were implemented in the following commits:

Zellic © 2025Back to top ↑