@anywalletkit/protocol-staking-native-monad
@anywalletkit/protocol-staking-native-monad
Section titled “@anywalletkit/protocol-staking-native-monad”Native MON staking for @anywalletkit/wallet-evm accounts on Monad (chain 143, testnet 10143):
delegations straight to the validators your app lists, through Monad’s staking precompile at
0x0000000000000000000000000000000000001000. No pool, no token, no extra contract.
npm install @anywalletkit/protocol-staking-native-monad @anywalletkit/wallet-evmimport { Wallet } from '@anywalletkit/kit'import { WalletManagerEvm } from '@anywalletkit/wallet-evm'import { NativeStakingProtocolMonad } from '@anywalletkit/protocol-staking-native-monad'
const wallet = new Wallet(seedPhrase)wallet.registerWallet('eip155:143', WalletManagerEvm, { provider: ['https://rpc.monad.xyz'], chainId: 143 })const account = await wallet.getAccount('eip155:143')
// Your own validator(s):const staking = new NativeStakingProtocolMonad(account, { validators: [{ id: 12n, name: 'Our validator' }] })await staking.stake({ amount: 10n ** 18n }) // delegate(12), 1 MONawait staking.requestWithdrawal({ amount: 10n ** 18n }) // undelegate into a free slot// … one epoch after the stake deactivates …await staking.claimWithdrawals() // withdraw every ready request
// Or let users pick from the active set:const open = new NativeStakingProtocolMonad(account, { validators: 'any' })const validators = await open.listValidators() // by stake: commission, active, apr, eligible, refusalsawait open.stake({ amount: 10n ** 18n, validator: validators[0].id })Amounts are wei (bigint). Without provider in the config, reads go to the account’s own RPC.
Config
Section titled “Config”| Option | Default | |
|---|---|---|
validators |
— (required) | Validator ids (bigint / number / { id, name }), or 'any'. No list, no module. |
defaultValidator |
the only listed id | Used when a call names no validator; with several listed and no default, calls must name one. |
maxCommission |
10 |
Percent. Checked against a fresh read of the validator (current and upcoming commission, compared in the precompile’s 1e18 scale) at every delegation and compound. |
allowInactive |
false |
Delegate to validators outside the consensus set or flagged by the precompile (their stake earns nothing). |
chainId |
the account’s | Pin 143 or 10143. The account and the read RPC must both be on it. Pin it when you list validator ids: ids differ between mainnet and testnet. |
provider |
the account’s RPC | URL, list (failover) or EIP-1193 provider, for reads and simulation. |
reserveBalance |
10 MON | Left on the wallet by stake; at least 10 MON, Monad’s reserve balance (value transfers that dip below it revert). |
maxWithdrawId |
255 |
Highest withdrawal slot used and scanned. |
withdrawScan |
'gap' |
'gap' reads withdrawal slots from 0 and stops 32 empty slots past the last used one; 'full' reads all 256 per validator. |
concurrency |
6 |
Reads in flight (public Monad RPCs cap requests per second; batching counts each call). |
enrich |
— | (ids) => ({ [id]: { apr, name, … } }): an explorer’s APR, names, logos. Decoration only. |
stakingAddress / timeoutMs |
0x…1000 / 15 s |
Methods
Section titled “Methods”Shared StakingProtocol |
What it does on Monad |
|---|---|
stake({ amount, validator? }) |
delegate(validatorId) with the amount as value |
requestWithdrawal({ amount, validator?, withdrawId? }) |
undelegate(validatorId, amount, withdrawId); the lowest free slot by default |
getWithdrawalRequests({ validator?, validators? }) |
pending withdrawals; id = validatorId << 8 | withdrawId |
claimWithdrawals({ ids?, validators? }) |
withdraw(validatorId, withdrawId) per ready request (one transaction each) |
getStakedBalance() |
balance = active stake, total = active + activating; unclaimedRewards apart |
getApr({ validator? }) |
enrich’s APR, or the estimate below |
Monad extras: compound / claimRewards (+ quotes), getEpoch() → { epoch, inEpochDelayPeriod, activatesAtEpoch }, listValidators, getValidator, listPositions, nextWithdrawId,
getNetworkStats, getMaxStakeable. Every write has a quote* twin that signs nothing.
Timing
Section titled “Timing”- A stake (or a compound) becomes active in epoch n+1, or n+2 when sent after the epoch’s boundary
block (
getEpoch().inEpochDelayPeriod). Epochs are about 4 hours; they are counted in rounds, not blocks, so the module always asksgetEpoch(). - Only active stake can be undelegated. Stake that is still activating shows in
activating/activatingNextwith its epoch. - An undelegated amount deactivates at n+1 (or n+2) and can be withdrawn one epoch later
(
withdrawableAtEpoch). Rewards keep accruing until then and are paid with the withdrawal. claimRewardspays at once;compoundfollows the stake timing.
apr is 18 MON per block × blocks per year ÷ the consensus set’s stake × (1 − commission). The block
time is measured over the last 50 000 blocks; the stake is summed over the consensus set (one read
per validator, cached). It assumes the validator proposes its share of blocks; pass enrich for a
measured figure.
Safety
Section titled “Safety”- Fail-closed before anything is signed: unlisted, unknown, inactive or over-commission validators; amounts under 1 gwei or above the active stake; a stake that would leave less than the reserve; withdrawal slots in use or all 256 taken; requests not yet open; an account or read RPC that is not on Monad (or on the pinned chain).
- Every write is simulated with
eth_callas the wallet first, and the precompile’s revert comes back in plain words (withdrawal not ready,insufficient stake,reserve balance violation, …). - The module never uses multicall, STATICCALL or EIP-7702 against the precompile (all revert there).
- Policies: the transactions go to
0x0000000000000000000000000000000000001000; anallowlistmust include it. - Full exits. Once the wallet’s stake with a validator reaches zero, the precompile drops that
validator from
getDelegations, while the withdrawal is still pending. The module still looks at the listed validators and those this instance undelegated from; in'any'mode keep theidthatrequestWithdrawalreturns (claimWithdrawals({ ids })always works) or pass the validator invalidators(the activity feed has it). - Reading withdrawals costs one
eth_callper slot: the gap scan reads about 36 slots for a validator without withdrawals;withdrawScan: 'full'reads all 256. Passvalidatorto read one position. - Validator data for display is cached for a minute; every write decision reads the validator again.
Testing
Section titled “Testing”pnpm test: selectors and topics against Monad’s published values, id packing, timing rules, every operation and refusal over an in-memory precompile, conformance with recorded mainnet transactions (delegate, undelegate, withdraw, compound, claimRewards).pnpm verify:live [rpcUrl] [chainId]: mainnet reads, then every write planned for real delegators found from recent events and simulated by the module and by the wallet. Nothing is signed. There is no forked testing: the precompile has no code to fork.
Classes
Section titled “Classes”Interfaces
Section titled “Interfaces”- DelegatorPosition
- Eip1193Provider
- MonadClaimOptions
- MonadClaimQuote
- MonadClaimResult
- MonadEpoch
- MonadNetworkStats
- MonadRewardsOptions
- MonadRewardsQuote
- MonadRewardsResult
- MonadStakeOptions
- MonadStakeQuote
- MonadStakeResult
- MonadUnstakeOptions
- MonadUnstakeQuote
- MonadUnstakeResult
- MonadWithdrawalRequest
- MonadWithdrawalScanOptions
- NativeStakingMonadConfig
- ValidatorConfig
- ValidatorExtra
- ValidatorInfo
Type Aliases
Section titled “Type Aliases”- MonadChainId
- MonadProvider
- MonadWithdrawalRequestsView
- NormalizedValidators
- ValidatorEnricher
- ValidatorRefusal
- ValidatorsOption
Variables
Section titled “Variables”- ACTIVE_VALIDATOR_STAKE
- ACTIVE_VALSET_SIZE
- BLOCK_REWARD
- BOUNDARY_BLOCK_PERIOD
- DEFAULT_CONCURRENCY
- DEFAULT_MAX_COMMISSION
- DEFAULT_TIMEOUT_MS
- DUST_THRESHOLD
- EPOCH_DELAY_ROUNDS
- MAX_COMMISSION
- MAX_WITHDRAW_ID
- MIN_AUTH_ADDRESS_STAKE
- MODULE_NAME
- MONAD_CHAIN_IDS
- MONAD_MAINNET
- MONAD_TESTNET
- PAGINATED_RESULTS_SIZE
- REVERTS
- SELECTORS
- STAKING_ABI
- STAKING_PRECOMPILE
- TOPICS
- USER_RESERVE_BALANCE
- WITHDRAW_SCAN_GAP
- WITHDRAWAL_DELAY
Functions
Section titled “Functions”- activatesAtEpoch
- commissionToPercent
- createReadClient
- describeMonadStakingActivity
- describeRevert
- normalizeValidators
- packWithdrawalId
- paginate
- percentToCommission
- pool
- toValidatorId
- toWithdrawId
- unpackWithdrawalId
- withdrawableAtEpoch
References
Section titled “References”default
Section titled “default”Renames and re-exports NativeStakingProtocolMonad