Dollar cost average into yield-earniung staked ETH. Stream USDC on Base Mainnet and get cbETH. Powered by Superfluid Subscriptions and Uniswap v3, Built on Base.
- Goto to https://streamstaker.finance and connect your wallet (You need some
USDCon Base Mainnet) - Hit the [Setup] button to get started (approve transaction)
- Hit the [Stream] button to use Superfluid Subscriptions to start streaming.
- Watch
cbETHaccumulate in your wallet over time.
Setup: Stream Staker deploys a (minimal clone) contract for each user. This StreamStaker.sol contract is deployed by the user when they hit the [Setup] button. By using a minimal clone on the Base L2, these contracts are deployed using only 122,000 gas and cost approx $0.02 to deploy (example: https://basescan.org/tx/0x909c3a013349c14ea151c195705252d48ad53ee96b560fe61f812e771e83b7e7). The user is the owner of the deployed Staker contract.
Stream: After hitting the [Stream] button, the user can use SuperFluid Subscriptions to start streaming to their Staker contract. The user should have USDC or USDCx in their Base wallet. The Superfluid Subscriptions widget walks them through the required transactions to start the stream. After the stream has started, no further action is required by the user -- Dollar cost averaging into yield-earning staked ETH has never been so easy.
Stake: Periodically calling the stake() function will swap the USDC accumulated in the contract for cbETH (Coinbase Staked ETH) using Uniswap v3. The cbETH goes directly to user's wallet -- it is not held by the Staker contract. While the user can call stake() themself, a server-based automation is automatically setup to do this at least once every 24 hours, as long as the acculated balance is greater than 100 USDCx (these parameters may change over time, in response to gas prices). A fee of 0.5% is deducted for this automation service.
Two contracts were created for Stream Staker:
StreamStakerFactory.solis a factory contract that is used to deploy minimal clones of the StreamStaker contract described below. Deployed on Base Mainnet at0xa7320C8f9a80009Eb2461eA4d7175F8E5bFF546c#StreamStaker.solis designed to be a narrowly focused contract, with one deployed for each user, that receives the stream ofUSDCx, which periodically is converted tocbETHvia thestake()function, which sends thecbETHto user's wallet. Thestake()function first unwraps theUSDSCxtoUSDCand then uses Uniswap v3 on Base to swap first toWETH, and then tocbETH(the lowest cost route, which each pool having a 0.05% fee). Uniswap sends the resultingcbETHto the user's wallet -- the Staker contract never holds any token apart from the accumulatingUSDCx. An example contract that was deployed by the factory can be found at0xB228aa76b32b5DF03DA7680559e9f0833d0342C4#
On Ethereum Mainnet, the gas cost to deploy a dedicated contract for each user would be prohibitive. But on an L2 like Base, the gas cost is (currently) 2 cents! As such, the low deployment cost on Base makes this an option. Aside from cost, why? The same end goal could have been achieved using a single contract shared by all users, with each user sending their own stream to the contract. But this would require signifcantly more complexity, as the contract would need to keep track of each user's share of the accumulating USDCx -- requiring that contract be a Super App and likely requiring that swapped cbETH remain in the contract until a user claimed their share via a withraw() function. Yes, there would be gas savings by doing batch swaps, especially for users with very small streams. It is a trade-off between complexity, convenience, and transaction costs. Considering these in the context of the low gas context of Base L2, I decided to create a per-user narrowly focused contract (73 lines of Solidity code) that provided the convenience of starting a stream and automatically getting staked ETH in your wallet, with no further intervention required, with no need to claim/withdraw/unwrap. You can set it and forget it.
Since Base Mainnet is not yet supported by Gelato Automate, a server-based automation was created, powered by Google Firebase. Three serverless functions were created:
indexer: the indexer function indexes new staker contracts that are deployed from the factory contract.automate: the automate function checks each staker contract and executes astake()transaction when certain conditions are met. These conditions -- which may change in future -- are 1) at least 24 hours have past since the previous automatedstake()call, and 2) the contract has a balance of at least 100USDCx.api: a simple API with two endpoints.GET /api/stakerwill return the staker address for a givenowneraddress (if the owner has deployed one).POST /api/widgetgenerates a hosted Superfluid Subscriptions widget that can be used to start or modify a stream to the staker contract.
Possible next steps include:
- enhanced front-end that displays
stake()transaction history - create subgraph for indexing
- documentation!
- support for other LSD tokens, perhaps on multiple L2 networks

