Polygon Staking
Reports an account's Polygon PoS delegation — staked principal and unclaimed rewards — for any configured validator.
One immutable adapter that reports an account's Polygon PoS delegation for any configured validator: the staked principal and the unclaimed rewards, both denominated in POL. The set of validators to query is governed on the NAV Calculator (per-adapter, MANAGER-gated) and passed in as bytes32 instance coordinates — ValidatorShare addresses widened to bytes32 — so adding a validator is a config transaction, not a deploy.
Type: Meta-adapter
protocolSubId:
keccak256("polygon-staking")Instance key: the
ValidatorShareaddress, widened to bytes32positionId:
abi.encode(address(validatorShare))protocolName():
"Polygon Staking"— the PRODUCT, not the chain. A bare "Polygon" reads as a network row in a portfolio, and the position is a delegation settled on Ethereum L1.
This is an Ethereum mainnet adapter, despite the protocol's name. Polygon PoS staking is settled on Ethereum L1 by the StakeManager at 0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908, which mints one ValidatorShare contract per validator; delegators hold share balances there. Nothing about this adapter runs on the Polygon chain.
Positions returned
Staking
Staking
false
The account's delegated principal, in POL
Rewards
Rewards
false
Unclaimed staking rewards, in POL
Both legs carry the same positionId — the validator share address — and are told apart by positionKind, not by id. Both come from a single getStakeAndRewards(account) call, which returns the principal already denominated in POL rather than in shares, so no exchange-rate conversion applies. Zero legs are dropped, so a configured validator the account has not delegated to contributes nothing rather than zero-value rows.
Unbonding positions are not reported
A delegation that is mid-unstake reads as smaller than it is. When a delegator starts an unstake, the shares leave getStakeAndRewards and land in a per-account, nonce-indexed withdrawal queue (unbondNonces + unbonds_new, valued through withdrawExchangeRate()). This adapter does not read that queue.
The omission is deliberate. Walking it means iterating an unbounded nonce range per instance per account, on a read path that is already O(instances). The resulting error is an under-report, which is the safe direction under this framework's conventions, and DeBank does not surface those positions either — so a covered account mid-unbond reads low against the same reference NAV is reconciled with. Adding it later needs its own isLocked = true leg and a bound on the nonce walk.
Pricing POL
POL is a registered asset priced by a plain Chainlink primary — no custom feed contract. The feed is 0x7bAC85A8a13A4BcD8abb3eB7d6b4d632c5a57676, whose description() reads MATIC / USD.
That is not a mis-wiring. Chainlink's own mainnet reference data lists this proxy with baseAsset: POL; the display name is legacy and the tracked asset is POL. It is corroborated economically: POL is the 1:1 MATIC successor, and PolygonMigration still reports unmigrationLocked() == false while holding ~9.6 B MATIC, so the pair is arbitrage-enforced in both directions rather than by convention.
Registered at heartbeat 86400 — exactly the cadence the feed declares. A heartbeat above the declared value would leave a window in which a dead feed still reads fresh, which is the one error this system treats as critical; see Choosing a heartbeat.
Labels
The breadcrumb is ["Validator", "#<validatorId>"], e.g. ["Polygon Staking", "Validator", "#192"].
The leaf comes from validatorId() rather than the ERC-20 name() every other address-keyed meta-adapter uses, because the live share contracts return something opaque — "Delegated POL #c0" for validator 192 — with nothing tying the suffix to the validator id a user would recognise. The read is gas-capped and fails open to "<POL symbol> <share address>", so a hostile or non-conforming instance degrades the label instead of reverting a display-only view.
Failure behaviour
A configured instance that is not a readable ValidatorShare reverts at getStakeAndRewards and is dropped by the meta-adapter's per-instance catch — the account's other positions are unaffected. An instance that runs out of gas is not absorbed: the fan-out raises InstanceGasExhausted, because recording a starved instance as an empty leg list would under-report behind a complete-looking answer.
If POL is ever unregistered on the NAV Calculator, the adapter returns no legs rather than reverting — an unregistered asset is an ordinary outcome, not an error.
Last updated