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/p/moul/x/storagecost/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / storagecost
Files
4 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/storagecost/v0
gno
0.9

Files (4)

  • README.mdmarkdown
  • gnomod.tomltoml
  • storagecost.gnogno
  • storagecost_test.gnogno
storagecost.gnogno
1// Package storagecost answers one question: is it worth paying gas to delete2// on-chain state?3//4// On gno.land every byte of realm state locks GNOT, and the lock is refunded5// to whoever signs the transaction that frees the byte. Deleting state is6// therefore paid work, and whether a given deletion pays depends on two prices7// that move independently: the storage price, a chain parameter, and the gas8// price of the day. A contract cannot know the second one, so it cannot decide9// on its own behalf whether to compact, reindex or reap. It can only publish10// the size of the prize and let a caller do the arithmetic.11//12// This package is that arithmetic. It is pure integer maths with no chain13// imports, so a realm can call it inside a Render and an off-chain bot can14// reuse the identical formula.15//16// The single fact worth remembering is the ratio. One byte freed refunds 10017// ugnot, and at the lowest gas price mainnet has actually accepted one ugnot18// buys 1000 gas. So a byte is worth 100,000 gas, and any deletion costing less19// than that per byte pays for itself.20//21// Demo realm: gno.land/r/moul/x/reaper/v0.22package storagecost2324import "gno.land/p/nt/ufmt/v0"2526const (27	// DefaultStoragePrice is the ugnot locked per byte of realm state, the28	// default of the chain's vm:p:storage_price parameter. It is governance29	// settable, so read it from the chain rather than trusting this constant30	// when real money depends on the answer.31	DefaultStoragePrice int64 = 1003233	// GasPerUgnotFloor is how much gas one ugnot buys at the lowest gas price34	// mainnet has been observed to accept, 0.001 ugnot per gas. It is a floor,35	// not a promise: the fee a node requires tracks gas_wanted, so asking for36	// more headroom raises the fee proportionally.37	GasPerUgnotFloor int64 = 10003839	// payloadOverheadNum/payloadOverheadDen approximate what a payload really40	// costs once the realm has wrapped it in an object. Measured at 1.85x: ten41	// 1,024-byte strings in a realm slice cost 18,984 bytes of state, 1,89842	// each. It is an estimate and nothing more. The authoritative number is43	// the chain's own, from the vm/qstorage query or a StorageDepositEvent.44	payloadOverheadNum int64 = 18545	payloadOverheadDen int64 = 1004647	ugnotPerGNOT int64 = 1_000_00048)4950// Refund is the deposit returned for freeing bytes at the given price per51// byte. Returns 0 for non-positive inputs rather than panicking, so a Render52// on a realm with no state still works.53func Refund(bytes, pricePerByte int64) int64 {54	if bytes <= 0 || pricePerByte <= 0 {55		return 056	}57	return bytes * pricePerByte58}5960// BreakEvenBytes is the fewest bytes whose refund covers a fee: the point61// where a cleanup stops costing money and starts making it. Below this many62// bytes the transaction is charity.63func BreakEvenBytes(feeUgnot, pricePerByte int64) int64 {64	if pricePerByte <= 0 {65		return 066	}67	if feeUgnot <= 0 {68		return 069	}70	// Round up: freeing exactly feeUgnot/pricePerByte bytes must cover the fee.71	return (feeUgnot + pricePerByte - 1) / pricePerByte72}7374// GasFee is the fee a transaction asking for gasWanted must pay at a gas price75// of num/den ugnot per gas, rounded up.76//77// The fee tracks gas_wanted rather than gas_used, so unused headroom is paid78// for. Pass the ceiling you will actually put in the transaction, not what you79// expect to burn.80func GasFee(gasWanted, num, den int64) int64 {81	if gasWanted <= 0 || num <= 0 || den <= 0 {82		return 083	}84	return (gasWanted*num + den - 1) / den85}8687// FloorGasFee is GasFee at the lowest gas price mainnet has accepted.88func FloorGasFee(gasWanted int64) int64 {89	return GasFee(gasWanted, 1, GasPerUgnotFloor)90}9192// EstimateBytes guesses the realm state a payload of this many bytes will93// occupy, applying the measured object overhead.94//95// It is for sizing a bounty in a Render, where being within a factor of two96// beats reporting the raw payload length. Never settle an accounting question97// with it.98func EstimateBytes(payloadBytes int64) int64 {99	if payloadBytes <= 0 {100		return 0101	}102	return payloadBytes * payloadOverheadNum / payloadOverheadDen103}104105// Quote is a complete answer for one candidate cleanup.106type Quote struct {107	Bytes     int64 // bytes the cleanup would free108	Refund    int64 // ugnot returned for them109	Fee       int64 // ugnot the transaction will cost110	Net       int64 // Refund - Fee; negative means it costs more than it pays111	BreakEven int64 // bytes needed to cover Fee112}113114// Worth reports whether the cleanup pays for itself.115func (q Quote) Worth() bool { return q.Net > 0 }116117// String renders the quote as one line of markdown-safe text, for a Render.118func (q Quote) String() string {119	verdict := "not worth it yet"120	if q.Worth() {121		verdict = "worth " + FormatGNOT(q.Net)122	}123	return ufmt.Sprintf(124		"%d bytes, refunds %s against %s of gas, break-even %d bytes: %s",125		q.Bytes, FormatGNOT(q.Refund), FormatGNOT(q.Fee), q.BreakEven, verdict,126	)127}128129// Evaluate prices one cleanup: freeing bytes in a transaction asking for130// gasWanted, at a storage price of pricePerByte and a gas price of num/den131// ugnot per gas.132func Evaluate(bytes, pricePerByte, gasWanted, num, den int64) Quote {133	fee := GasFee(gasWanted, num, den)134	refund := Refund(bytes, pricePerByte)135	return Quote{136		Bytes:     bytes,137		Refund:    refund,138		Fee:       fee,139		Net:       refund - fee,140		BreakEven: BreakEvenBytes(fee, pricePerByte),141	}142}143144// EvaluateAtFloor is Evaluate at the default storage price and the floor gas145// price: the best case, and the one to quote when advertising a bounty.146func EvaluateAtFloor(bytes, gasWanted int64) Quote {147	return Evaluate(bytes, DefaultStoragePrice, gasWanted, 1, GasPerUgnotFloor)148}149150// FormatGNOT renders ugnot as GNOT with six decimal places and trailing zeros151// trimmed, because a bounty shown in ugnot is unreadable and gno has no152// floats.153func FormatGNOT(amount int64) string {154	neg := amount < 0155	if neg {156		amount = -amount157	}158	whole := amount / ugnotPerGNOT159	frac := amount % ugnotPerGNOT160161	out := ufmt.Sprintf("%d", whole)162	if frac != 0 {163		// Left-pad the fraction to six digits, then trim trailing zeros.164		digits := ufmt.Sprintf("%d", frac)165		for len(digits) < 6 {166			digits = "0" + digits167		}168		for len(digits) > 1 && digits[len(digits)-1] == '0' {169			digits = digits[:len(digits)-1]170		}171		out += "." + digits172	}173	if neg {174		out = "-" + out175	}176	return out + " GNOT"177}178

Functions

not supported for pure packages by the node (vm/qfuncs)

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