@anywalletkit/protocol-staking-tonstakers-ton
@anywalletkit/protocol-staking-tonstakers-ton
Section titled “@anywalletkit/protocol-staking-tonstakers-ton”A simple package that lets @anywalletkit/wallet-ton wallet accounts stake
through Tonstakers: stake GRAM → tsTON, exit
instant / standard / best-rate, track payout bills, read rates, APY and
earnings.
Install
Section titled “Install”npm install @anywalletkit/protocol-staking-tonstakers-ton @anywalletkit/core# or: pnpm add @anywalletkit/protocol-staking-tonstakers-ton @anywalletkit/core · yarn add @anywalletkit/protocol-staking-tonstakers-ton @anywalletkit/coreOn npm: @anywalletkit/protocol-staking-tonstakers-ton, published from the release workflow with provenance.
Built as the third implementation of the StakingProtocol type in
@anywalletkit/core — after Lido (ETH liquid staking) and SSV (token staking
with a cooldown) — and the first one off EVM. The base class in
src/staking-protocol.js is the same file, byte-identical; a TON liquid
staking protocol fitting it is the point: the type is chain-agnostic.
The mechanics are extracted from a production wallet (React Native/Hermes) where every flow was exercised with real funds.
What Tonstakers and tsTON are
Section titled “What Tonstakers and tsTON are”The Open Network’s native token is GRAM (Toncoin was renamed Gram on 2026-06-15; the chain keeps the TON name). Tonstakers is TON’s largest liquid-staking pool (~128M GRAM, ~140k stakers): the pool contract lends deposited GRAM to validators, and tsTON is the non-rebasing receipt jetton whose GRAM value grows with the pool’s rewards — the rate is the pool’s balance over the jetton supply and moves once per validation round (~18h). Exits burn tsTON in one of three modes: standard (paid now from the pool’s free liquidity when there is enough, otherwise a payout bill settled after the round), instant (paid now or the message bounces — never a bill), best rate (always a bill, settled at the round-end projected rate). Bills are NFTs the pool pays out by itself; there is nothing to claim.
import WalletManagerTon from '@anywalletkit/wallet-ton'import TonstakersProtocolTon from '@anywalletkit/protocol-staking-tonstakers-ton'
const wallet = new WalletManagerTon(seed, { tonClient: { url: 'https://toncenter.com/api/v2/jsonRPC' } })const account = await wallet.getAccount(0)
const tonstakers = new TonstakersProtocolTon(account, { partnerCode: 0x…n // optional: Tonstakers partner code for referral attribution})
// Stake — tsTON lands at the pool rate; the message carries +1 GRAM the pool refundsconst { hash, fee } = await tonstakers.stake({ amount: 10n ** 9n })
// Exit: standard (default), instant (fill-or-kill) or bestRate (round-end rate)await tonstakers.requestWithdrawal({ amount: 10n ** 9n, mode: 'instant' })
// Bills waiting for their round (settled automatically — nothing to claim)const { requests, pendingAmount } = await tonstakers.getWithdrawalRequests()
// Viewsawait tonstakers.getStakedBalance() // { balance: tsTON, rate: GRAM per tsTON, total: GRAM value }await tonstakers.getApr() // 12.86 (percent, tonapi)Tonstakers-specific extras beyond the interface:
await tonstakers.getPoolData() // halted, depositsOpen, balances, minter, payout collection, ratesawait tonstakers.getRates() // { rate, projectedRate } — GRAM per tsTON, 1e9 fixedawait tonstakers.getInstantLiquidity() // GRAM the pool can pay instant exits fromawait tonstakers.getJettonWallet() // the account's tsTON jetton wallet (derived, checked against the minter)await tonstakers.getTokenBalance() // tsTON heldawait tonstakers.getRewards() // earned GRAM = (rate − weighted entry rate) × tsTONHow it maps onto the interface
Section titled “How it maps onto the interface”IStakingProtocol |
Tonstakers |
|---|---|
stake({ amount }) |
one message to the pool: amount + 1 GRAM, body op 0x47d54391 · query_id 1 · partner code |
requestWithdrawal({ amount, mode }) |
burn on the account’s tsTON jetton wallet: 1.05 GRAM attached, body op 0x595f07bc · amount · owner · {wait_till_round_end, fill_or_kill} |
getWithdrawalRequests() |
the account’s payout-bill NFTs in the pool’s active payout collections (claimable always false) |
claimWithdrawals() |
rejects — bills are settled by the pool, not claimed |
getStakedBalance() |
tsTON balance, rate GRAM/tsTON, total = GRAM value |
getApr() |
tonapi’s staking-pool view (the number Tonstakers shows) |
Three honest deviations, documented on the methods rather than papered over:
the exit has modes (an extra mode option), the second phase is
automatic (so claimWithdrawals refuses instead of pretending), and
total is the position’s value in GRAM — for a non-rebasing receipt the
GRAM value is the figure that means anything.
Why it looks the way it does
Section titled “Why it looks the way it does”- The official SDK only speaks TonConnect (
tonstakers-sdkbuilds and sends through a connector; no payload export). Its message builders — op codes, fee reserves, payload layout — are ported verbatim and ride the account’s ownsendTransaction({ to, value, body }), which signs locally. - Friendly addresses on the wire: the module hands the account
EQ…forms, which carry the bounce flag; a raw0:…jetton-wallet address carries none. - Pool state is read on-chain (
get_pool_full_data, layout verified against mainnet), through the account’s TonClient with failover across configured clients. Public toncenter nodes intermittently answer exit code −13 on that getter, so each client gets two tries. - Instant exits are pre-checked against the pool’s free balance (the contract would bounce them anyway, but a bounce carries no readable reason).
- REST is decoration: bills and APY come from the indexes the official
SDK reads (tonapi, Tonstakers’ payout API), overridable or disable-able via
config.endpoints; on-chain flows never depend on them, andgetRewardsdegrades to null. - No
AbortSignal.timeout/.any, noprocess.env— Hermes and Bare friendly. Mainnet and testnet both ship pool and tsTON minter defaults. - The jetton wallet is derived, not trusted: the account’s tsTON jetton
wallet — the destination of every unstake’s 1.05 GRAM — is computed on the
device from the minter’s pinned wallet code (
TONSTAKERS_JETTON_WALLET_CODES,deriveTonstakersJettonWallet), and the minter’sget_wallet_addressmust agree or the call is refused. A minter with no pinned code (a customaddresses.jetton) needsjettonWalletCode, or the explicitallowUnverifiedJettonWallet: trueto trust the node’s answer.
Configuration
Section titled “Configuration”new TonstakersProtocolTon(account, { tonClient, // TonClient | { url, secretKey } | array of either; defaults to the account's network, // '-239' mainnet (default) or '-3' testnet addresses, // { pool, jetton } jettonWalletCode, // Cell | base64 BOC — wallet code of a minter not pinned in the package allowUnverifiedJettonWallet, // true: no known code, trust get_wallet_address (off by default) partnerCode, // referral attribution in stake payloads endpoints // { tonapi, withdrawalPayouts } — null disables the feature})Mainnet contracts (verified live): pool
EQCkWxfyhAkim3g2DjKQQg8T5P4g-Q1-K_jErGcDJZ4i-vqR, tsTON minter
EQC98_qAmNEptUtPc7W6xdHh_ZHrBUFpw5Ft_IzNU20QAJav.
npm test # offline unit tests (node:test, stubbed TonClient + account)npm run verify:live # read-only checks against mainnet (pool getter, jetton wallet, APY, bills, message builders)Activity
Section titled “Activity”describeActivity(tx, { own }) labels Tonstakers transactions for a wallet’s history, offline and synchronously, against the deployment the instance is configured with (pool + tsTON jetton master; addresses are compared in raw wc:hex form, so friendly and raw spellings both match). Three shapes: a message to the pool whose op is OP_STAKE 0x47d54391 (or undecoded) → stake “Staked GRAM” (net GRAM out — the fee reserve the pool returns is netted — tsTON minted in); a burn (op OP_UNSTAKE 0x595f07bc, read from call.data as hex / decimal or from call.method) with a tsTON jetton transfer leaving the wallet → unstake “Requested unstake” (the destination is the wallet’s own jetton wallet, which cannot be known statically, so the jetton movement is the proof); an incoming message from the pool carrying GRAM (op 0x0a77535c tonstake_pool_withdrawal when decoded, no call of ours) → claim “Received unstaked GRAM”. Subtitle "Tonstakers · TON", counterparty the pool. Anything else — plain GRAM or jetton transfers, other pools — is null.
Fixtures in fixtures/activity/ are the test wallet’s funded run (stake 3 GRAM, unstake 1 tsTON, the settled payout; 2026-09-04 … 10-07, amounts from tonapi events and raw transactions); negatives/ holds a GRAM transfer in and a USD₮ jetton transfer out.
Classes
Section titled “Classes”Interfaces
Section titled “Interfaces”- ClaimWithdrawalsOptions
- ClaimWithdrawalsResult
- PoolData
- Rewards
- StakedBalance
- StakeOptions
- StakeResult
- StakingWalletAccount
- TonClientConfig
- TonstakersBill
- TonstakersProtocolConfig
- WithdrawalRequest
- WithdrawalRequestOptions
- WithdrawalRequestResult
- WithdrawalRequestsView
Type Aliases
Section titled “Type Aliases”Variables
Section titled “Variables”- MIN_STAKE
- OP_STAKE
- OP_UNSTAKE
- OP_WITHDRAWAL_PAYOUT
- STAKE_FEE_RESERVE
- TONSTAKERS_ADDRESS_MAP
- TONSTAKERS_JETTON_WALLET_CODES
- UNSTAKE_FEE_RESERVE