npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@anywalletkit/protocol-staking-lido-evm

v0.3.10

Published

A simple package that lets @anywalletkit/wallet-evm wallet accounts stake through the Lido protocol: stake, two-phase queue withdrawals (permit-first), wrap/unwrap, rewards.

Readme

@anywalletkit/protocol-staking-lido-evm

npm

A simple package that lets @anywalletkit/wallet-evm wallet accounts stake through the Lido protocol: stake ETH → stETH, exit through the two-phase withdrawal queue, wrap/unwrap the non-rebasing wstETH form, and read APR and reward history.

Install

npm install @anywalletkit/protocol-staking-lido-evm @anywalletkit/core
# or: pnpm add @anywalletkit/protocol-staking-lido-evm @anywalletkit/core  ·  yarn add @anywalletkit/protocol-staking-lido-evm @anywalletkit/core

On npm: @anywalletkit/protocol-staking-lido-evm, published from the release workflow with provenance.

Built as the first implementation of the StakingProtocol type in @anywalletkit/core. The base class lives in @anywalletkit/core; src/staking-protocol.ts keeps the runtime IStakingProtocol marker and the option / result types this module documents. See "The interface" below for why staking is not lending.

The mechanics are extracted from a production wallet (web + React Native/Hermes) where every flow is exercised with real funds.

Written in TypeScript on viem (the only runtime dependency) and built with tsup to dist/ (ESM + CJS + .d.ts).

Usage

import WalletManagerEvm from '@anywalletkit/wallet-evm'
import LidoProtocolEvm from '@anywalletkit/protocol-staking-lido-evm'

const wallet = new WalletManagerEvm(seed, { provider: 'https://rpc.example.org' })
const account = await wallet.getAccount(0)

const lido = new LidoProtocolEvm(account, {
  referral: '0x…' // optional: Lido Rewards-Share referral, defaults to zero address
})

// Stake — stETH lands on the account 1:1
const { hash, fee } = await lido.stake({ amount: 10n ** 18n })

// Exit, phase 1: queue the withdrawal (permit-first — ONE transaction)
await lido.requestWithdrawal({ amount: 10n ** 18n, token: 'stETH' })

// …days later — exit, phase 2: claim finalized requests as ETH
const { claimableIds } = await lido.getWithdrawalRequests()
if (claimableIds.length) await lido.claimWithdrawals({ ids: claimableIds })

// Views
await lido.getStakedBalance() // { balance, wrappedBalance, rate, total }
await lido.getApr()           // 2.72 (percent, 7d SMA)

Lido-specific extras beyond the interface:

await lido.wrap({ amount })                 // stETH → wstETH (approve awaited + wrap)
await lido.unwrap({ amount })               // wstETH → stETH (single tx)
await lido.getWithdrawalEta(ids)            // Map<id, seconds> from Lido's queue API
await lido.getRewards()                     // lifetime rewards + average APR
await lido.getRewardHistory({ limit: 14 })  // daily rebase events, newest first

Why the withdrawal path looks the way it does

Every one of these choices was paid for in production:

  • Permit-first: requestWithdrawal signs one EIP-2612 permit and rides it inside requestWithdrawalsWithPermit — a single transaction (and on mobile, a single biometric prompt) instead of approve + request.
  • Proven before broadcast: the permit path is simulated with eth_call first. A bad signature must fall back, not burn gas.
  • The fallback awaits its approval: in the approve + request path the approval is waited to a mined receipt, because the request's gas estimate reverts against an unmined allowance.
  • Verified permit domains: stETH signs as version "2", wstETH as version "1" — both checked against the tokens' on-chain DOMAIN_SEPARATOR. A wrong version recovers to a different signer and reverts with no readable reason.
  • Per-request caps: the queue takes at most 1000 stETH per request; over-cap amounts split into several requests inside the same transaction (wstETH capped at 800 per request — 1000 is stETH-equivalent and the rate drifts upward).
  • Up-front guards: stake checks isStakingPaused and getCurrentStakeLimit before estimating gas, because the raw revert carries no readable reason for either.
  • Structural account checks: writability is typeof account.sendTransaction === 'function', not instanceof — a duplicated copy of the account package fails instanceof while the account works fine.
  • No AbortSignal.timeout / AbortSignal.any: Hermes (React Native) ships AbortController but not those statics; nothing here depends on them.
  • REST endpoints are configuration: APR, queue ETAs and reward history come from Lido's public frontend APIs (eth-api.lido.fi, wq-api.lido.fi, reward-history-backend.lido.fi), all overridable via config.endpoints. On-chain flows never depend on them: ETA and rewards are decoration and degrade to empty results, never throws.

Configuration

new LidoProtocolEvm(account, {
  provider,       // RPC url or EIP-1193; defaults to the account's provider
  chainId,        // default 1; other chains must supply the fields below
  referral,       // forwarded to submit() — Lido Rewards-Share integrators earn on stake flow
  addresses,      // { stEth, wstEth, withdrawalQueue }
  permitDomains,  // { stEth: { name, version }, wstEth: { name, version } }
  endpoints       // { apr, withdrawalQueueApi, rewardHistory }
})

Only mainnet ships as a default deployment — deliberately. Testnets (e.g. Hoodi) must be configured explicitly so nobody stakes against an unverified address a default quietly supplied.

The interface

IStakingProtocol: stake / quoteStake, requestWithdrawal / quoteRequestWithdrawal, getWithdrawalRequests, claimWithdrawals / quoteClaimWithdrawals, getStakedBalance, getApr.

Staking is not lending: a lending withdraw is synchronous, while unstaking is a two-phase exchange — a request enters a queue, finalizes out of band (usually days), and only then can be claimed for the native token. Modelling that as ILendingProtocol.withdraw would produce a method that returns with no money received, and would stub out borrow/repay as unsupported. The honest interface names the queue. Wrap/unwrap, ETAs and reward history stay provider-specific extras so the core stays implementable by other liquid staking protocols.

Tests

pnpm build            # tsup → dist/
pnpm test             # offline unit + conformance tests (vitest, stubbed EIP-1193 provider)
pnpm verify:live      # read-only checks against mainnet (views, REST, eth_call simulation; needs dist/)

Activity

describeActivity(tx, { own }) labels Lido transactions for a wallet's history, offline and synchronously. A transaction is Lido's when it calls one of the three contracts the instance is configured with (stEth, wstEth, withdrawalQueue — mainnet by default) on that chain, and the selector is: stETH submit → stake "Staked ETH"; wstETH wrap → wrap "Wrapped stETH", unwrap → unwrap "Unwrapped wstETH"; WithdrawalQueue requestWithdrawals / …WithPermit / …WstETH / …WstETHWithPermit → unstake "Requested withdrawal", claimWithdrawals / claimWithdrawalsTo / claimWithdrawal → claim "Claimed ETH". Selectors are computed from the signatures at module load (LIDO_ACTIONS). approve / transfer on stETH or wstETH are not Lido operations and yield null, as does everything else. Subtitle "Lido · Ethereum", counterparty the contract called; assetsIn / assetsOut are the wallet's net movements from the receipt with stETH / wstETH / unstETH labelled by the module.

Fixtures in fixtures/activity/ are the test wallet's funded run (submit, wrap, unwrap, requestWithdrawalsWithPermit, claimWithdrawals; 2026-08-31 … 09-04); negatives/ holds an SSV stake and a USDT approve.