Built for the UHI8 Hookathon.
LongETH is a Uniswap v4 hook vault for a USDC/USDT pool.
It accepts stablecoin deposits, mints transferable receipt shares, routes idle assets through YieldHub adapters, and captures protocol fees from:
- swaps (stable-only) via Uniswap v4
afterSwapreturn deltas - realized stable yield on withdrawals/claims
Captured stable fees are routed to ETHEngine, which buys ETH in batches, wraps to WETH, and deposits through the configured WETH yield source.
- Keep LP capital productive between AMM activity and lending yield.
- Maintain share-based accounting for USDC/USDT deposits and withdrawals.
- Convert part of stablecoin yield into protocol-owned ETH.
- Distribute ETH yield (not ETH principal) to LPs over time.
flowchart LR
User([User]) --> LongETH[LongETH Vault]
LongETH --> ActiveOrIdle{Active or Idle}
ActiveOrIdle -->|Active| AMM[Active liquidity funds AMM]
ActiveOrIdle -->|Idle| YieldHub((YieldHub))
YieldHub --> Morpho[Morpho Adapter]
YieldHub --> Aave[Aave Adapter]
YieldHub --> Restaking[ETH Restaking Adapter]
AMM -->|Protocol swap fee captured| BuyETH[Stable fees used to buy ETH]
BuyETH -->|Buy WETH / route ETH side| YieldHub
YieldHub -->|Stables/ETH yield| User
- LP deposits USDC and USDT and receives receipt shares.
- Stablecoins are deposited into configured
YieldHubsources. - On swaps, hook liquidity is added/removed just-in-time in the Uniswap v4 pool.
- On swap, a 0.005% protocol fee is captured only when unspecified output currency is USDC/USDT.
- On stable-yield realization (withdraw/claim), a 0.005% fee on the yield portion is captured.
- Captured stable fees are forwarded to
ETHEngine(pendingStableYield) and allocated by dynamic split policy. - ETH buy flow is handled by
ETHEngine, then wrapped to WETH and deposited viaYieldHub. - ETH yield is checkpointed and distributed per share via an accumulator (paid out in WETH).
- Deploy
LongETHwith:- Uniswap v4
PoolManager - owner address
- USDC and USDT token addresses
- Uniswap v4
- Initialize exactly one pool key through
PoolManager.initialize(...).
The hook stores that pool key and reverts on re-initialize.
The owner must set external integrations with:
setYieldHub(yieldHub)setEthEngine(engine)(optional, for ETH fee/yield routing)
Expected wiring:
yieldHub: hub that manages active sources for USDC/USDT0/WETH routingengine: deployedETHEnginecontroller if ETH fee conversion/distribution is enabled
Deposit flow:
- Call
previewDeposit(amountUSDC, amountUSDT)to estimate shares. - Approve USDC/USDT to the hook.
- Call
addLiquidity(shares). - Hook internally uses
previewMint(shares)and pulls only required token amounts.
Withdraw flow:
- Call
previewRedeem(shares)to estimate returned stablecoin amounts. - Call
removeLiquidity(shares). - Hook withdraws from yield sources and transfers net stablecoin amounts after stable-yield fee (fee applies only to yield portion).
- If ETH yield is accrued, claim is tracked in
ETHEngineand paid in WETH.
- Receipt shares are transferable.
- ETH reward debt is updated on mint/transfer/burn paths so transferred shares carry the correct reward state.
pendingEthYield(user)reports claimable ETH yield derived from the global accumulator.
- If
totalSupply == 0: shares/amounts are derived from Uniswap price and the configured tick range. - Otherwise:
- deposit shares are proportional to current per-asset vault balances
- minted shares are constrained by the limiting asset (
min(shares0, shares1)) - rounding favors existing LPs (
previewMintrounds up costs;previewRedeemrounds down outputs)
- Owner can reconfigure the yield hub via
setYieldHub.
In production, owner controls should be secured (multisig + timelock recommended). - Swap-fee extraction is economically observable and potentially MEV-targeted, but ETH buy execution is bounded by ETHEngine controls.
- Integration risk remains for external protocols (YieldHub adapter config, Morpho/Uniswap dependencies, and controller permissions).
forge buildforge testforge fmtUse the numbered scripts in script/unichain/steps. Each script does one thing so delegated-account rate limits do not break a multi-step deployment.
Current deployed addresses on Unichain:
USDC:0x078D782b760474a361dDA0AF3839290b0EF57AD6USDT0:0x9151434b16b9763660705744891fA906F660EcC5WETH:0x4200000000000000000000000000000000000006SwapRouter02:0x73855d06DE49d0fe4A9c42636Ba96c62da12FF9C
Deployment contracts:
LongETH:0x1112f5873547f6BA9cc8D287159742B54f3D28C4ETHEngine:0xA7Ea1243d067685b35B7dCb979504C9022842BEa
Ordered deployment flow:
01_DeployLongETH.s.sol02_InitializeLongETHPool.s.sol03_DeployOnlyEthEngine.s.sol04_WireOnlyEthEngine.s.sol05_DeployYieldHub.s.sol06_DeployMorphoAdapter.s.solthree times forUSDC,USDT0, andWETH07_SetYieldHubAssetController.s.solthree times08_AddYieldHubSource.s.solthree times09_SetYieldHubMigrationTolerance.s.solthree times10_QueueYieldHubActivation.s.solthree times with a safe future timestamp11_SetLongETHYieldHub.s.sol12_SetEthEngineYieldHub.s.sol13_SetEthEngineSwapRouter.s.sol14_SetEthEngineSwapFeeTier.s.soltwice forUSDCandUSDT0
Example commands:
forge script script/unichain/steps/05_DeployYieldHub.s.sol:DeployYieldHub --rpc-url "$RPC_URL" --broadcast -vvvv
forge script script/unichain/steps/06_DeployMorphoAdapter.s.sol:DeployMorphoAdapter --rpc-url "$RPC_URL" --broadcast -vvvv
forge script script/unichain/steps/07_SetYieldHubAssetController.s.sol:SetYieldHubAssetController --rpc-url "$RPC_URL" --broadcast -vvvv
forge script script/unichain/steps/08_AddYieldHubSource.s.sol:AddYieldHubSource --rpc-url "$RPC_URL" --broadcast -vvvv
forge script script/unichain/steps/09_SetYieldHubMigrationTolerance.s.sol:SetYieldHubMigrationTolerance --rpc-url "$RPC_URL" --broadcast -vvvv
forge script script/unichain/steps/10_QueueYieldHubActivation.s.sol:QueueYieldHubActivation --rpc-url "$RPC_URL" --broadcast -vvvv
forge script script/unichain/steps/11_SetLongETHYieldHub.s.sol:SetLongETHYieldHub --rpc-url "$RPC_URL" --broadcast -vvvv
forge script script/unichain/steps/12_SetEthEngineYieldHub.s.sol:SetEthEngineYieldHub --rpc-url "$RPC_URL" --broadcast -vvvv
forge script script/unichain/steps/13_SetEthEngineSwapRouter.s.sol:SetEthEngineSwapRouter --rpc-url "$RPC_URL" --broadcast -vvvv
forge script script/unichain/steps/14_SetEthEngineSwapFeeTier.s.sol:SetEthEngineSwapFeeTier --rpc-url "$RPC_URL" --broadcast -vvvv