MustNew
Ask AI
MustNew is New but panics on error.
Command
gnokey query vm/qeval -remote "https://rpc.gno.land" -data "gno.land/p/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/journal.MustNew(,)"
Result
Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.
Package journal is a pure double-entry accounting primitive for realms that need books which provably balance. It is to *bookkeeping* what feeledger is to *money* and duebook is to *time*: it holds no coins, performs no effects, imports no chain APIs, and owns no package-level state. The importing realm owns a *Book, supplies the clock, and performs its own effects.
Every financial primitive in the searched Gno ecosystem is ONE-SIDED. A treasury records what moved. A pull-payment ledger records who is owed what. A GRC20 ledger records who holds what. Each stores a balance and mutates it.
This package stores no balance. It stores ENTRIES, and derives balances from them:
1book.MustOpen("1000", "Cash", Asset)
2book.MustOpen("4000", "Revenue", Revenue)
3book.MustPost(now, "invoice 17 paid", []Posting{
4 {Account: "1000", Side: Debit, Amount: 5000},
5 {Account: "4000", Side: Credit, Amount: 5000},
6})
An entry that does not balance is refused, and the refusal says which side is short. So `sum(debits) == sum(credits)` is not a property the consumer must remember to maintain — it is the precondition for the state changing at all. A Book cannot be put into an unbalanced state by any sequence of calls to this package.
Post is the only way in, and there is no Edit and no Delete. A mistake is corrected the way real books correct one: with a REVERSING ENTRY that mirrors every posting of the original, leaving both visible.
1book.MustReverse(entryID, now, "wrong account")
This is why history is trustworthy here and would not be if entries were mutable. An auditor reading the journal sees the error AND the correction, which is the entire point of keeping a journal rather than a balance. Reverse refuses a second reversal of the same entry, so a correction cannot be looped.
Close(through) seals everything dated at or before `through`. It only ever moves forward, and after it Post refuses any entry dated into the sealed range — the backdating that makes a closed period meaningless. Reverse is refused there too: correcting a sealed period means opening a new entry in the current one, which is the consumer's call to make explicitly.
Read this before quoting IsBalanced as a guarantee.
IsBalanced compares TWO INDEPENDENTLY MAINTAINED ACCUMULATORS: the per-account debit/credit totals, summed across the chart, against the Book's own running totals. They are written by different code paths, so a drift between them is a real defect and IsBalanced finds it. What it does NOT do is re-read the journal, so a defect that corrupted both accumulators identically would survive it.
RederiveFromJournal is the strong check: it replays every posting of every entry and compares the result against both accumulators. It costs O(all postings ever) and exists to be run deliberately, not on every read.
AccountingEquation is WEAKER THAN IT LOOKS and is documented as such rather than left for a reader to assume otherwise: under this package's sign conventions it is implied by IsBalanced, and its boolean proves EXACTLY ONE of IsBalanced's three conjuncts. It is NOT an independent accounting fact. Three checks that are really two, presented as three, would be the defect this package exists to avoid.
This doc said "it is a cross-check of this package's own normal-balance handling" until 2026-10-07, and that sentence was FALSE — the one place the careful disclaimer around it still overclaimed. The boolean is the sum of every account's RawDelta, bucketed by Kind and then added back together, so the bucketing cannot change it: offsets is INVARIANT under every possible Kind misclassification, which is the one thing "normal-balance handling" means. See obligation 7 below, and AccountingEquation's own doc for what it does prove.
And the ceiling on all three: a Book can be internally perfect and describe nothing. See obligation 1 below.
Entries are never removed, because an append-only journal whose entries could vanish is not a journal. Storage therefore grows with every Post and is bounded only by maxEntries, set at construction. A Book that reaches that cap refuses further entries rather than silently dropping history — a long-lived consumer must plan for this, and the plan is a new Book with opening balances carried forward, not a pruned one.
All failures are returned as errors and leave the Book COMPLETELY UNCHANGED. Must* wrappers are the only functions here that panic.
The Book is address-agnostic: account codes are non-empty strings. Realms normally use a chart-of-accounts code, not an address.
MustNew is New but panics on error.
gnokey query vm/qeval -remote "https://rpc.gno.land" -data "gno.land/p/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/journal.MustNew(,)"
New returns an empty Book.
maxAccounts caps the chart and must be in [1, MaxAccountsLimit]. maxEntries caps the journal and must be in [1, MaxEntriesLimit]; because entries are never removed, it is a lifetime cap rather than a concurrent one, and a Book that reaches it is finished.
gnokey query vm/qeval -remote "https://rpc.gno.land" -data "gno.land/p/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/journal.New(,)"