Skip to content

Commit 091fc0a

Browse files
committed
refine monad staking lens scan
1 parent 5d07712 commit 091fc0a

3 files changed

Lines changed: 573 additions & 84 deletions

File tree

src/monad/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Monad Staking Lens
2+
3+
`StakingLens.sol` is a read-only helper around the Monad staking precompile. It is designed for Gem Wallet's RPC-only flow, so the main goal is to make `getDelegations(address)` useful without requiring an indexer or transaction history service.
4+
5+
## Trade-off
6+
7+
Monad exposes active delegations and validator lists, but withdrawals are only available through point lookups:
8+
9+
- `getDelegations(delegator, startValId)` can enumerate validators with current delegation state.
10+
- `getWithdrawalRequest(validatorId, delegator, withdrawId)` requires the caller to already know both the validator and the withdraw id.
11+
12+
Because withdraw ids are scoped per `(validator, delegator)` and can use the full `0..255` range, a fully exact on-chain scan would mean checking up to 256 withdraw ids for every validator. That is too expensive for the default lens path.
13+
14+
## Current policy
15+
16+
`getDelegations(address)` uses a bounded hybrid scan:
17+
18+
- Prioritize Gem Wallet's curated validators:
19+
- `16` MonadVision
20+
- `5` Alchemy
21+
- `10` Stakin
22+
- `9` Everstake
23+
- Then prioritize validators returned by `getDelegations(...)`.
24+
- Full scan `0..255` for up to `MAX_FULL_SCAN_VALIDATORS` prioritized validators.
25+
- Shallow scan `0..7` for remaining active validators.
26+
- Shallow scan `0..7` for up to `MAX_FALLBACK_SCAN_VALIDATORS` other validators discovered from the validator set fallback.
27+
28+
Curated validators are processed first inside the full-scan set, so they are not squeezed out when the lens hits the `MAX_DELEGATIONS` cap.
29+
30+
This keeps the common Gem Wallet path accurate while avoiding a worst-case `all validators x 256 withdraw ids` sweep on every call.
31+
32+
`getBalance(address)` uses the same active, curated, and fallback validator sources, but processes active validators first because balance calculation has no withdrawal scan tier. That prevents curated discovery from crowding out active stake if the validator cap is ever reached.
33+
34+
## Accepted blind spot
35+
36+
The main case we still may miss is:
37+
38+
- a user fully undelegated from an unknown validator
39+
- the only remaining state is a withdrawal
40+
- that withdrawal lives at `withdrawId > 7`
41+
- the validator is outside the bounded fallback scan window
42+
43+
The same can happen for an active non-curated validator after the full-scan validator cap is reached, but active stake and rewards are still returned because those do not depend on withdraw-id discovery.
44+
45+
We accept that trade-off for now because this lens is optimized for our wallet and our supported validators. If we later need exact recovery for all unknown validators, we will need either a heavier RPC fallback or an off-chain indexer.

0 commit comments

Comments
 (0)