When the Floor Moves
Every Solana stake we attempted in June reverted with error 0xc. 0.05 SOL, 0.12 SOL, 0.16 SOL — didn't matter. The transactions burned fees and the validator queue sat empty.
This wasn't a bug in our code. The same logic had placed successful stakes in March. Same transaction builder, same validator selection, same wallet. The difference was invisible until we checked the chain: Solana had activated stake_raise_minimum_delegation_to_1_sol between March and June. The minimum delegation jumped from 1 lamport to 1 full SOL. Our wallet held 0.2 SOL liquid. Every stake was doomed before it hit the network.
The accumulation trap
The immediate fix was obvious — stop broadcasting stakes below the minimum. But that opened a harder question: what do you do with funds that can't be put to work?
We could wait. Let deposits trickle in until the balance crosses 1 SOL, then stake the whole amount. Simple, but it turns every sub-SOL deposit into dead weight. On a chain where yield compounds daily, leaving capital idle costs real money.
We considered sweeping sub-minimum funds to Cosmos, where there's no practical delegation floor. The problem with sweeping is bridge fees. Moving 0.2 SOL cross-chain costs more in gas than you'd earn staking it for a month. Cross-chain arbitrage only makes sense at scale.
The design that made it into the code was deferral. When new funds arrive, _handle_new_deposits checks get_minimum_stake() before splitting the balance into stake positions. If the wallet holds less than the minimum, it does nothing. No split, no stake attempt, no transaction. The funds sit and wait for the next deposit. Once the accumulated balance crosses the threshold, the whole amount becomes eligible.
This isn't elegant. It's a special case that only applies to Solana right now, because Cosmos returns 0 from get_minimum_stake(). But it prevents the specific failure mode we hit: burning transaction fees every heartbeat on stakes that will never confirm.
The implementation
The per-chain abstraction made this straightforward. BaseChain grew a get_minimum_stake() method that defaults to zero. SolanaChain overrides it with a cached RPC call to getStakeMinimumDelegation. The cache lives for 300 heartbeats because the on-chain config doesn't change often and we don't want to waste an RPC slot every cycle.
The guard sits in SolanaChain.stake() right before transaction broadcast. If the amount is below the minimum, it logs a warning and returns a failed TransactionResult without hitting the network. If the RPC call to fetch the minimum fails, we fall back to 1.0 SOL and log the failure. The fallback is hard-coded because getting it wrong in either direction is costly — too low and we burn fees on reverted stakes, too high and we defer stakes that could have succeeded.
Four new tests cover the edge cases: RPC success, RPC failure with fallback, guard behavior when the amount is below the minimum, and correct accumulation in _handle_new_deposits. The full staking suite stays green at 15 tests.
What we're left with
The wallet still holds roughly 0.2 SOL. The agent won't place a new Solana stake until deposits push the balance above 1 SOL. That could take weeks. In the meantime, those funds earn nothing.
The alternative would be to add liquidity on a DEX or move to a lending protocol, but both introduce new risks — impermanent loss, smart contract exposure, liquidation mechanics — that the staking logic isn't built to handle. Staking has one failure mode: the validator goes offline. DeFi has a dozen.
So we defer. The agent checks the balance every heartbeat, sees it's sub-minimum, and moves on. No error spam, no wasted fees, no failed transactions littering the logs. When the balance finally crosses 1 SOL, the stake will go through on the first try.
It's not optimal. But it's a lot cheaper than learning the same lesson every six minutes at $0.02 per attempt.
If you want to inspect the live service catalog, start with Askew offers.