Transaction
1F4F0EB1FD4EA5…253425C18C7C
Block 25,938 · index 0 · indexed
Summary
- Hash
- 1F4F0EB1FD4EA52A2AB17262CEC2B4AABFFA1BCD8663CBF60767253425C18C7C
- Block
- 25,938
- Size
- 5949 bytes
- Gas used
- 10,321,291 / 12,385,609
- Fee
- 1000000ugnot
- Memo
- gnopublish
- Status
- success
Messages
Arguments · 9
- #1humanize
- #2README.md
- #3# `gno.land/p/moul/x/daily/humanize/v0` **Human-facing number formatting** — `Bytes`, `Comma`, `Ordinal`, `Plural`, `Blocks`, `Truncate`. ```go import "gno.land/p/moul/x/daily/humanize/v0" humanize.Bytes(1536) // "1.5 KiB" humanize.Comma(1234567) // "1,234,567" humanize.Ordinal(12) // "12th" — not "12nd" humanize.Plural(2, "mouse", "mice") // "2 mice" humanize.Blocks(1234) // "~1,200 blocks" humanize.Truncate("ééé", 2) // "é…" ``` **Integer-only, on purpose.** No floats anywhere: a rendered value that differed between nodes would be a consensus bug, so the single decimal place in `Bytes` comes from scaling by 10 and taking a remainder. **Durations are in blocks, never seconds.** There is no wall clock on chain, so "about 2 hours" is a lie dressed as precision. `Blocks` says `~1,200 blocks` and lets the caller decide what that means on their chain — and rounds deliberately, because false precision is worse than none. `Ordinal` handles the teens (`11th`/`12th`/`13th`, not `11st`/`12nd`/`13rd`) — the case naive implementations get wrong. `Truncate` counts **runes**, so multi-byte text is never sliced mid-character. **Live demo:** [`r/moul/x/daily/humanizedemo`](https://github.com/moul/gno-contracts/tree/main/r/moul/x/daily/humanizedemo/v0) · render it at [`/r/moul/x/daily/humanizedemo/v0`](https://gno.land/r/moul/x/daily/humanizedemo/v0). <!-- BEGIN GNOCONTRACTS FOOTER (generated by `make readmes`; do not edit below) --> --- Part of **[moul/gno-contracts](https://github.com/moul/gno-contracts)** — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage. > 🧪 **Highly experimental — potentially vibe-coded.** Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md). <!-- END GNOCONTRACTS FOOTER -->
- #4gnomod.toml
- #5module = "gno.land/p/moul/x/daily/humanize/v0" gno = "0.9"
- #6humanize.gno
- #7// Package humanize formats numbers for people rather than machines, as a pure, // reusable package: byte sizes, thousands separators, ordinals, pluralisation // and block-height "durations". // // Everything is integer-only. There are no floats here on purpose: gno has no // float determinism guarantees worth relying on for consensus output, and a // rendered value that differs between nodes would be a consensus bug. One // decimal place is produced by scaling by 10 and taking a remainder. // // Durations are expressed in BLOCKS, not seconds. There is no wall clock on // chain, so "about 2 hours" is a lie dressed as precision; this package says // "~1200 blocks" and lets the caller decide what that means on their chain. // // A live demo of this package is at // [r/moul/x/daily/humanizedemo](/r/moul/x/daily/humanizedemo/v0). package humanize import ( "strconv" "strings" ) // Bytes renders n bytes with SI-ish binary units and one decimal place. // Negative input is rendered with a leading minus rather than rejected. func Bytes(n int64) string { neg := n < 0 if neg { n = -n } const unit = 1024 if n < unit { s := strconv.FormatInt(n, 10) + " B" if neg { return "-" + s } return s } units := []string{"KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} div := int64(unit) i := 0 for n/div >= unit && i < len(units)-1 { div *= unit i++ } // one decimal place without floats: scale by 10, then split scaled := n * 10 / div whole, freq := scaled/10, scaled%10 s := strconv.FormatInt(whole, 10) if freq != 0 { s += "." + strconv.FormatInt(freq, 10) } s += " " + units[i] if neg { return "-" + s } return s } // Comma inserts thousands separators: 1234567 -> "1,234,567". func Comma(n int64) string { neg := n < 0 if neg { n = -n } s := strconv.FormatInt(n, 10) var b strings.Builder for i, c := range []byte(s) { if i > 0 && (len(s)-i)%3 == 0 { b.WriteByte(',') } b.WriteByte(c) } if neg { return "-" + b.String() } return b.String() } // Ordinal renders 1 -> "1st", 2 -> "2nd", 11 -> "11th". // // The teens are the trap: 11/12/13 take "th" despite ending in 1/2/3, so the // 11–13 case must be checked before the last digit. func Ordinal(n int64) string { s := strconv.FormatInt(n, 10) a := n if a < 0 { a = -a } if a%100 >= 11 && a%100 <= 13 { return s + "th" } switch a % 10 { case 1: return s + "st" case 2: return s + "nd" case 3: return s + "rd" } return s + "th" } // Plural returns "1 block" / "2 blocks", using plural when given, else word+"s". func Plural(n int64, word, plural string) string { if n == 1 || n == -1 { return strconv.FormatInt(n, 10) + " " + word } if plural == "" { plural = word + "s" } return strconv.FormatInt(n, 10) + " " + plural } // Blocks renders a block count at a coarse magnitude — deliberately vague, // because a block count is not a wall-clock duration. func Blocks(n int64) string { switch { case n < 0: return "in the past" case n == 0: return "now" case n < 10: return Plural(n, "block", "") case n < 1000: return "~" + Comma(n/10*10) + " blocks" default: return "~" + Comma(n/100*100) + " blocks" } } // Truncate shortens s to at most max runes, appending "…" when it cut. // Counts RUNES, not bytes, so a multi-byte string is not sliced mid-character. func Truncate(s string, max int) string { if max <= 0 { return "" } r := []rune(s) if len(r) <= max { return s } if max == 1 { return "…" } return string(r[:max-1]) + "…" }
- #8/gno.MemPackageType
- #9 MPUserProd
Result log
msg:0,success:true,log:,events:[]