PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/moul/x/daily/staking/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / x / daily / staking
Files
3 (README)(gnomod.toml)
Exported functions
6
Module
gno.land/r/moul/x/daily/staking/v0
gno
0.9

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • staking.gnogno
staking.gnogno
1// Package staking is a simplified, accounting-only port of Synthetix2// StakingRewards to gno.land. Users stake a fungible amount; rewards accrue3// every block, proportional to each staker's share of the total staked,4// at a fixed rewardPerBlock. There is no real coin transfer — balances and5// rewards are plain uint64 accounting (a demonstration of the classic6// "reward-per-token" accumulator pattern).7package staking89import (10	"strconv"1112	"chain"13	"chain/runtime"14	"chain/runtime/unsafe"1516	"gno.land/p/nt/avl/v0"17)1819const (20	// rewardPerBlock is the fixed number of reward units distributed across21	// ALL stakers on every block, split by stake share.22	rewardPerBlock = uint64(100)23	// precision scales the reward-per-token accumulator to avoid truncation.24	precision = uint64(1_000_000)25)2627type account struct {28	staked     uint64 // currently staked amount29	rewardPaid uint64 // reward-per-token snapshot at the account's last update30	reward     uint64 // accrued-but-unclaimed rewards31	minted     uint64 // rewards moved into the caller's reward balance32}3334var (35	accounts          avl.Tree // address string -> *account36	totalStaked       uint6437	rewardPerTokenAcc uint64 // global accumulator, scaled by precision38	lastUpdateBlock   uint6439)4041// --- pure helpers (unit-tested) ------------------------------------------4243// computeRewardPerToken advances the accumulator by the rewards earned per44// staked unit over `elapsed` blocks. With no stake, the accumulator is frozen.45func computeRewardPerToken(stored, rate, elapsed, total uint64) uint64 {46	if total == 0 {47		return stored48	}49	return stored + (rate*elapsed*precision)/total50}5152// computeEarned returns total rewards owed to an account given its stake, the53// current accumulator, the accumulator value already paid, and prior accrual.54func computeEarned(balance, rptCurrent, rptPaid, accrued uint64) uint64 {55	return accrued + (balance*(rptCurrent-rptPaid))/precision56}5758// --- internals ------------------------------------------------------------5960func currentBlock() uint64 { return uint64(runtime.ChainHeight()) }6162func getOrCreate(addr string) *account {63	if v := accounts.Get(addr); v != nil {64		return v.(*account)65	}66	acc := &account{}67	accounts.Set(addr, acc)68	return acc69}7071// accrue rolls the global accumulator forward to the current block and folds72// the caller's freshly-earned rewards into its stored balance. Mirrors the73// Synthetix `updateReward(account)` modifier.74func accrue(addr string) *account {75	elapsed := currentBlock() - lastUpdateBlock76	rewardPerTokenAcc = computeRewardPerToken(rewardPerTokenAcc, rewardPerBlock, elapsed, totalStaked)77	lastUpdateBlock = currentBlock()7879	acc := getOrCreate(addr)80	acc.reward = computeEarned(acc.staked, rewardPerTokenAcc, acc.rewardPaid, acc.reward)81	acc.rewardPaid = rewardPerTokenAcc82	return acc83}8485// --- exported transactions (crossing) ------------------------------------8687// Stake adds `amount` to the caller's staked balance.88func Stake(cur realm, amount uint64) {89	if amount == 0 {90		panic("staking: amount must be > 0")91	}92	addr := unsafe.PreviousRealm().Address().String()93	acc := accrue(addr)94	acc.staked += amount95	totalStaked += amount96	chain.Emit("Staked", "addr", addr, "amount", strconv.FormatUint(amount, 10))97}9899// Withdraw removes `amount` from the caller's staked balance.100func Withdraw(cur realm, amount uint64) {101	if amount == 0 {102		panic("staking: amount must be > 0")103	}104	addr := unsafe.PreviousRealm().Address().String()105	acc := accrue(addr)106	if amount > acc.staked {107		panic("staking: insufficient staked balance")108	}109	acc.staked -= amount110	totalStaked -= amount111	chain.Emit("Withdrawn", "addr", addr, "amount", strconv.FormatUint(amount, 10))112}113114// GetReward mints all accrued rewards into the caller's reward balance and115// resets the accrual counter. Returns the amount minted this call.116func GetReward(cur realm) uint64 {117	addr := unsafe.PreviousRealm().Address().String()118	acc := accrue(addr)119	claimed := acc.reward120	acc.reward = 0121	acc.minted += claimed122	chain.Emit("RewardPaid", "addr", addr, "amount", strconv.FormatUint(claimed, 10))123	return claimed124}125126// --- read-only ------------------------------------------------------------127128// Earned reports the rewards currently owed to `addr` (accrued but unminted),129// projected to the current block without mutating any state.130func Earned(addr address) uint64 {131	v := accounts.Get(addr.String())132	if v == nil {133		return 0134	}135	acc := v.(*account)136	elapsed := currentBlock() - lastUpdateBlock137	rpt := computeRewardPerToken(rewardPerTokenAcc, rewardPerBlock, elapsed, totalStaked)138	return computeEarned(acc.staked, rpt, acc.rewardPaid, acc.reward)139}140141// StakedOf reports the current staked balance of `addr`.142func StakedOf(addr address) uint64 {143	if v := accounts.Get(addr.String()); v != nil {144		return v.(*account).staked145	}146	return 0147}148149// Render renders pool stats and a stakers table.150func Render(path string) string {151	out := "# Staking Rewards\n\n"152	out += "Simplified Synthetix-style staking (accounting-only, no coin transfer).\n\n"153	out += "- **Total staked:** " + strconv.FormatUint(totalStaked, 10) + "\n"154	out += "- **Reward rate:** " + strconv.FormatUint(rewardPerBlock, 10) + " / block (split across stakers)\n"155	out += "- **Block height:** " + strconv.FormatUint(currentBlock(), 10) + "\n\n"156157	if accounts.Size() == 0 {158		out += "_No stakers yet. Call `Stake(amount)` to join._\n"159		return out160	}161162	out += "## Stakers\n\n"163	out += "| Address | Staked | Earned (pending) | Minted |\n"164	out += "|---|---|---|---|\n"165166	elapsed := currentBlock() - lastUpdateBlock167	rpt := computeRewardPerToken(rewardPerTokenAcc, rewardPerBlock, elapsed, totalStaked)168	accounts.Iterate("", "", func(key string, value interface{}) bool {169		acc := value.(*account)170		earned := computeEarned(acc.staked, rpt, acc.rewardPaid, acc.reward)171		out += "| " + key +172			" | " + strconv.FormatUint(acc.staked, 10) +173			" | " + strconv.FormatUint(earned, 10) +174			" | " + strconv.FormatUint(acc.minted, 10) + " |\n"175		return false176	})177	return out178}179

Functions

  • Earned(addr string) uint64

  • GetReward(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}) uint64

  • Render(path string) string

  • Stake(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, amount uint64)

  • StakedOf(addr string) uint64

  • Withdraw(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, amount uint64)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.