Building Eight Yield Strategies for Flashstake V3
Drafted February 2024. Finished and published August 2026 as part of the migration away from WordPress.
By the end of 2023, I had created eight yield-source integrations for Flashstake V3. Four reached production. Four did not. They all implemented the same interface, but almost nothing behind that interface behaved the same way.
I created Flashstake V3 for Blockzero Labs as a general version of the upfront-yield protocol. The audited core contracts went live on Ethereum in July 2022 and the protocol contract was deployed at the same address across Ethereum, Arbitrum and Optimism. The premise was straightforward: lock a yield-bearing asset for a fixed term and receive the expected yield immediately rather than waiting for it to accrue.
Underneath that experience were four parts:
FlashProtocolrecorded the position.- A strategy moved the principal into an external yield source.
- The protocol minted a fungible fToken representing the future yield.
- Flashstake converted the fToken into immediate yield by burning it against accrued strategy yield, swapping it through the Uniswap V3 market we integrated, or combining both routes.
The Uniswap pool was external to the core contracts, but it was part of the Flashstake product. The Chronos upgrade introduced a user-facing proxy contract that handled the route selection. It compared the strategy’s accrued-yield pool with the Uniswap liquidity pool and routed the transaction through whichever combination returned more to the user.
The principal moved directly from the user to the strategy. At maturity, the user withdrew the principal and kept the yield they had already received.
The architecture depended on IFlashStrategy, a ten-function interface that made every yield source look the same to the core protocol. A strategy quoted the fTokens to mint, reported how much principal arrived, and later had to return exactly the amount the protocol requested. That compact boundary was enough to connect eight very different systems.
The four that shipped
Aave: the reference strategy grew into an L2 integration
Aave was the reference implementation. The strategy deposited principal, held the resulting aTokens and treated the difference between its balance and registered principal as yield. Adding USDC exposed an eighteen-decimal assumption in the original mint calculation, so the strategy began reading decimals from the principal token and gained a separate 556-line USDC integration suite. On Optimism, Aave V3 added packed L2 calldata and OP rewards that had to be claimed explicitly. The Flashstake interface stayed the same across both versions.
Lido: an integration reduced to a subtraction
The Lido strategy made no staking calls to Lido. stETH rebases, so yield was simply the contract’s current balance minus registered principal. The complication was that share-based rounding can leave one wei behind during a full transfer. The strategy deliberately registered one wei less so a mature position could return the amount recorded against it. Its mainnet-fork tests impersonated Lido’s oracle executor and submitted a real beacon report to exercise the rebase.
Rocket Pool: the same invariant, opposite rounding
Rocket Pool required the opposite design. rETH appreciates against ETH through an exchange rate, while the Flashstake principal was WETH and Rocket Pool’s deposit pool spoke native ETH. The strategy unwrapped WETH, deposited ETH, measured the rETH received and resolved Rocket Pool’s contracts dynamically through its storage registry.
When withdrawing, integer division could calculate slightly too little rETH and leave the strategy short of the ETH it owed. So this strategy rounded up:
uint256 rETHtoBurn =
((_tokenAmount * 10**18) / rocketTokenRETH.getExchangeRate()) + 1;
Lido subtracts one before registering principal. Rocket Pool adds one before redeeming it. Both preserve the same promise: internal accounting must never claim more principal than the strategy can return. Testing this required impersonating ten real Oracle DAO members on a mainnet fork and submitting balances to move Rocket Pool’s exchange rate forward.
GMX: a strategy with an operational system attached
The GMX strategy held staked GLP, which earned WETH without reinvesting it. I built an AWS Lambda keeper to claim the rewards, price them through the 0x API, simulate the conversion and send the transaction. It waited until at least $250 was available so processing did not cost more than it earned. The Solidity strategy and its keeper together supplied the compounding behaviour the product needed.
That made the strategy semi-centralised. If the chain and strategy continue indefinitely, the keeper will eventually go offline; it is only a question of when. Unless somebody replaces it, GLP will continue accumulating WETH rewards but the strategy will stop converting them into additional principal. From a Flashstake user’s perspective, its yield pool will stop growing and the strategy will become dormant. This was an accepted business decision after weighing that operational risk against the additional contract logic required to make routing, fees and slippage safe for permissionless callers.
The four that did not ship
These four strategies were intended to extend Flashstake beyond lending and liquid staking into liquidity positions, vaults and reward-based protocols. They were built or explored far enough to test the strategy interface, but were not deployed as public Flashstake strategies.
Uniswap V3: make the NFT somebody else’s problem
This strategy would have paid upfront yield from the fees earned by a concentrated-liquidity position. Uniswap V3 positions are NFTs, while IFlashStrategy expected an ERC20. Rather than add token IDs, price ranges and position management to the protocol, I built FlashLP to wrap an xToken Terminal position and issue fungible shares. The strategy saw an ordinary ERC20 and the core accounting did not change. The wrapper and strategy were in place, but yield processing and reinvestment were not yet connected end to end, so this remained an architectural prototype.
Beefy: calculate the withdrawal backwards
This strategy would have paid upfront yield against an auto-compounding Beefy vault position. To return an exact principal amount, it calculated the required vault shares and redeemed one additional unit so integer division could not leave the withdrawal short. It derived its principal token from the vault, read its decimals dynamically and deposited leftover dust back into the vault. Beefy came closest to production: its deposit, withdrawal and yield paths were exercised against a live Optimism vault on a fork, but it was not taken through deployment.
Aura: separate collecting rewards from pricing yield
This strategy would have turned the multiple rewards earned by an auraBAL position into usable Flashstake yield. It staked auraBAL and exposed public reward collection, keeping that separate from deposits and withdrawals. Claiming proved that value had accrued, but conversion, slippage and reinvestment still needed their own policy. The work stopped at that early integration stage before the conversion and full test path were completed.
Convex: one strategy crossing three protocols
Convex would have let users deposit Curve LP tokens and receive upfront yield from their future CRV and CVX rewards. It required the most machinery of the eight. The strategy deposited the LP tokens into the Convex Booster and staked the resulting position in a reward pool. Reinvestment then crossed three protocols:
- Claim CRV and CVX from Convex.
- Swap CVX through WETH into CRV.
- Add the CRV back into the Curve pool on one side.
- Stake the new LP tokens back into Convex.
The strategy included a minimum swap threshold so small reward balances could wait rather than spending more on gas than they earned. It also included a soft-shutdown control that could stop new yield generation and allow positions to migrate without changing the core protocol.
The core still saw the same deposits, withdrawals and yield balance as every other strategy. Behind those calls was a multi-protocol pipeline. This was an advanced prototype with its deposit, staking, reward and reinvestment paths represented, but it still needed final production validation.
Where an operator was required
Calling one strategy decentralised and another centralised would hide the dependencies inherited from their underlying protocols. Lido relied on oracle reports, Rocket Pool relied on its Oracle DAO, and Beefy relied on its own compounding infrastructure. The more useful distinction was whether Flashstake introduced an additional operator.
| Strategy | Flashstake-side yield processing |
|---|---|
| Aave | Interest accrued without a Flashstake keeper. |
| Lido | stETH rebased without a Flashstake keeper. |
| Rocket Pool | The rETH exchange rate updated without a Flashstake keeper. |
| GMX | A designated Flashstake keeper had to convert WETH rewards into additional principal. |
| Uniswap V3 | The prototype required a privileged reinvestment call. |
| Beefy | No Flashstake keeper was needed; compounding depended on the Beefy vault. |
| Aura | Reward claiming was public, but the conversion and reinvestment model was not completed. |
| Convex | Yield processing was public and paid the caller an incentive, so it did not require a fixed keeper. |
What I would do differently
I would keep the small strategy interface and the decision to send principal directly to isolated strategy contracts. Eight integrations used it without forcing protocol-specific logic into the core.
I would put more enforcement around that interface.
First, I would ship every strategy from a shared base contract. Principal accounting, access control, maximum-duration handling, token recovery and the common burn calculation were repeated across repositories. A base contract would leave each strategy responsible only for depositing, withdrawing and processing yield from its underlying protocol.
Second, I would make balance-delta accounting the default. The core transferred principal directly to a strategy and then trusted the amount returned by depositPrincipal. I would have the core measure the strategy’s token balance before and after that transfer, then use shared helpers to measure the shares received when the strategy deposits into the underlying protocol. Requested amounts would no longer be treated as amounts received.
Third, I would express withdrawal solvency as an invariant and run it against every connector. For any supported decimal count, exchange rate and withdrawal size, the strategy must never record more principal than it can return. The Lido and Rocket Pool corrections would then be two implementations of a property tested across the entire strategy suite.
Finally, I would separate passive yield from processed rewards. Aave and Lido accrue value in the held asset. GMX, Aura and Convex require rewards to be claimed, converted and sometimes reinvested by an external caller. I would define a second interface for those strategies, with a standard harvest function and an explicit keeper incentive. Their operational requirements would then be visible before they launched.
What eight strategies proved
The same core protocol worked with aTokens, rebasing stETH, exchange-rate-based rETH, staked GLP, vault shares, concentrated-liquidity positions and rewards spread across several contracts. The interface successfully isolated their structure. Each strategy still had to model what “principal” and “yield” meant for its underlying protocol.
Four strategies went live and four stayed as engineering work. Together they tested Flashstake V3 far more thoroughly than the reference implementation could have done alone.