KeeperHub / KeeperHub/keeperhub

feat(protocols): add ether.fi liquid restaking (stake, wrap/unwrap, reads)

Open
#2,391 3 comments 0 reactions 0 assignees View on GitHub
accepted confirmed
Dominant language
TypeScript
Stars
24
Forks
93
Avg merge
1d 4h
Merged PRs (30d)
253

Description

### Before filing

- [x] I searched open and closed issues for this proposal.
- [x] I checked the docs and the current behaviour on `staging`.
- [x] This is one change, not several.

### Reason: what you cannot do today

A workflow cannot stake ETH into ether.fi. It cannot wrap or unwrap the receipt. It cannot read the pool's accounting or exchange rate. ether.fi is the largest liquid restaking protocol on Ethereum. The registry already covers the sibling liquid-staking protocols (Lido, Rocket Pool, Frax Ether V2), so a builder who wants to route value through eETH or weETH has no first-class action. It falls back to a raw `web3/write-contract` node with a hand-pasted ABI and address.

### Reason: what the workaround costs

The raw-node workaround makes the builder re-supply the contract address, the ABI fragment, the correct payable/value handling for every action, with no labels, help text, decimals, no read/write typing. That is the same gap the existing staking integrations were added to close. It also loses the on-chain test coverage the protocol harness gives every registered protocol.

### Scope: what this touches, and what it does not

Adds one new protocol definition, `protocols/ether-fi.ts`, its minimal ABIs under `protocols/abis/`, a unit test, plus the regenerated `protocols/index.ts` and `lib/types/integration.ts` that `pnpm discover-plugins` produces. No existing protocol, node, route, schema, nor dependency changes.

Deliberately left alone, correct with these reverted:

- Mainnet only (chain 1). Minting settles on the beacon chain; the L2 weETH variants are LayerZero-bridged and not mintable here.
- No withdrawal/unstake action. ether.fi unstaking is a request-queue NFT (`EtherFiWithdrawRequestNFT`), a distinct surface that can ship separately later.

Actions: `stake` (payable, `LiquidityPool.deposit`), `wrap` plus `unwrap` (weETH), then reads for pool total, share/amount conversions, the weETH rate with its conversions, plus eETH/weETH balances and supply/shares.

### Plan: what you propose

Follow the existing `defineAbiProtocol` pattern (closest precedent: `protocols/frax-ether-v2.ts` and `protocols/rocket-pool.ts`). Three contracts:

- `liquidityPool` `0x308861A430be4cce5502d0A12724771Fc6DaF216`: `deposit()` payable as the `stake` action, plus reads `getTotalPooledEther`, `amountForShare`, `sharesForAmount`.
- `weeth` `0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee`: `wrap`, `unwrap`, plus reads `getRate`, `getWeETHByeETH`, `getEETHByWeETH`, `balanceOf`, `totalSupply`.
- `eeth` `0x35fA164735182de50811E8e2E824cFb9B6118ac2`: reads `balanceOf`, `totalShares`.

Every address and function was verified against mainnet on 2026-09-10 over a public RPC before filing. The set is coherent on-chain: `weETH.eETH()` returns the eETH address above. `weETH.getRate()` equals `LiquidityPool.amountForShare(1e18)` (about 1.1033 ETH per weETH), so the wrapper rate and the pool share price are the same number. `LiquidityPool.getTotalPooledEther()` reads about 2.21M ETH.

Test data mirrors the staking siblings: reads carry `nonZero` chain-invariant expectations. `stake` carries a write-expectation oracle that re-reads the eETH balance after the deposit so a mined-but-uncredited deposit fails the suite. `wrap`/`unwrap` are marked skipped in the fixture with a reason, since they need an eETH balance and an approval the setup block does not model.

I have this built and green locally against `staging` (discover-plugins registers it, tsgo and biome clean, an 18-case unit test modeled on `protocol-frax-ether-v2.test.ts` passes, the seed-workflow baseline unchanged). I will open the PR once this is accepted.

### Plan: alternatives considered

- Doing nothing: leaves the largest LRT reachable only through raw nodes while its three staking siblings are first-class.
- Adding the withdrawal queue in the same change: rejected. It is a separate contract and a separate UX (request then claim after a delay). It can ship and be correct with the mint/wrap surface reverted, so by the one-change test it is a separate issue.
- Adding the L2 weETH reads: rejected for this change. The bridged L2 tokens are not mintable and belong with a broader multi-chain pass.

Contributor guide

Open the contributing guide

Research direction

Start with protocols/frax-ether-v2.ts and protocols/rocket-pool.ts, then inspect the defineAbiProtocol pattern and protocols/abis/. Run the 18-case test modeled on protocol-frax-ether-v2.test.ts, followed by pnpm discover-plugins, tsgo, and biome. Done means the generated index and integration types are updated, the protocol reads and actions pass their fixtures, and the seed-workflow baseline remains unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
blockchain
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.