Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

assets package

Overview

Package assets is a per-owner fixed-asset register whose ledger postings are DERIVED from state rather than asserted by a caller.

What this realm is for

A general ledger takes a debit code, a credit code and an amount from whoever is posting. That is the right design for a ledger and it means gno.land/r/.../books structurally CANNOT know that an entry was a depreciation charge, or that the charge for that period had already been made. Nothing stops an owner from booking the same month twice, or from skipping one, and nothing in the book can tell afterwards.

This realm closes that gap for one class of entry. It holds each asset's depreciation schedule as state, computes the charge from the schedule and the chain height, and keeps a per-asset HIGH-WATER MARK of the periods already booked. The amount is never a parameter. A charge therefore cannot be double-booked (the mark does not move backwards) and cannot be skipped (the next call books the whole span, because charges telescope exactly).

The resulting claim is narrow and checkable by anyone, which is the point:

Example
1bookedAmount(asset) == asset.schedule.AccumulatedAt(bookedThrough(asset))

and, across a whole register,

Example
1sum of bookedAmount over assets == the credit balance of 1590

Both are exported as Invariant and CrossFoot and both are FREE reads, deliberately — see "Why Rederive is not owner-only" below.

The chart is two accounts and is fixed

Example
15500  Depreciation expense       (expense, debit-normal)
21590  Accumulated depreciation   (asset,   CONTRA — see below)

Every posting is debit 5500 / credit 1590 for an equal amount, so the accounting equation offsets at 0 = 0 on an empty register and after every charge. There is no OpenAccount entrypoint and the underlying book is created with a hard cap of two accounts, so the chart cannot grow.

The COST BASIS never reaches the book. Booking an asset's cost would force a credit leg, and the only honest candidates are a liability ("2000 Acquisition payable") or an equity plug ("3000 Contributed capital"). Both assert how the asset was funded, which this realm does not know and must not invent. The cost stays in register state, where it is visible as data rather than as an accounting claim.

1590 is kind ASSET, so journal treats it as debit-normal and Balance() returns Debits - Credits — which reads NEGATIVE for accumulated depreciation. That is arithmetically correct for a contra-asset and is confusing on first reading, so both figures are exported and each is named for what it is: AccumulatedCredit is the positive contra balance.

Periods are measured in BLOCKS

A period is periodBlocks chain heights long, anchored to the height the asset was placed in service. Period k closes at placedAt + k*periodBlocks, and a partial period in progress is not counted and is not lost.

Block height, not wall-clock time, because that is this portfolio's convention: payroll, budgets, reimbursements and subscriptions all carry a periodBlocks field, and height is monotone by construction where block time is validator-reported. p/depreciation's Daily/Monthly/Yearly constants are SECONDS and are therefore not used here; its arithmetic is unit-agnostic (Periods is an integer division), so this is a mismatch in that package's documentation rather than a divergence from its behaviour. It is recorded in DISCOVERY.md § 6 rather than silently worked around.

Retirement books first, then freezes

Retire performs the final booking for the periods completed up to the retirement height and only then stops the asset. The two are one transaction on purpose: retiring without booking would leave depreciation that was genuinely incurred permanently unbooked, and booking after retirement would need an exception to the "retired assets do not accrue" rule. Historical charges always stand — the depreciation really happened.

Disposal gain or loss is OUT OF SCOPE for v1, and the reason is structural: it needs the cost-basis account this realm deliberately does not open, plus a proceeds figure the realm cannot observe because it holds no funds.

What this realm does NOT do

  • No custody. It holds no coins, has no withdrawal path, and refuses a user call that arrives carrying any. An asset's cost is a number an owner typed.
  • No period close. p/journal can seal a book up to a HEIGHT, refusing entries dated at or before it. This realm's bookedThrough is a PERIOD INDEX — a different axis wearing the same English word — and sealing a derived book could only make the derivation unpostable. The capability exists in the engine and is deliberately not exposed.
  • No bulk booking. p/depreciation's MaxLife doc states the loop bound (1199 iterations per CarryingAt, 2398 per Charge, and a degenerate declining schedule that runs the full bound on every call forever) and explicitly refuses to price it. A loop over N assets multiplies an unpriced bound by N, so one asset is booked per call.
  • No truth. A register is a set of claims by its owner. With no custody there is nothing tying "cost 1000000" to a real holding. The invariants here prove that the postings follow from the stored schedules; they prove nothing about the world.

Why Rederive and the invariants are free reads

r/books exposes Rederive as an owner-only crossing function, to contain the cost of a full re-walk. This realm diverges, deliberately: the derivation IS the product, and a claim only its owner can check is not a claim worth making. Rederive, Invariant and CrossFoot are all free reads, so any third party can verify a register from vm/qeval without the owner's key.

The bound above is what makes that affordable, and a bound is not a price. Measured under the pinned onyx toolchain at the register cap — 64 assets on MaxLife declining schedules, the worst case this realm can hold:

Example
1Render   per call:    106,421,272 gas
2Rederive per call:  9,357,860,004 gas

So the design choice is stated with its cost rather than only its reason. Render is an ordinary rich-page figure; Rederive at cap is two orders of magnitude above it, falls on the queried node rather than the caller, and is reachable with no key and no fee. Whether a given RPC's query gas ceiling admits it AT THE CAP is a property of that node and is not measured here — if it refuses, the third-party-verification claim above fails exactly at the bound it was designed around. Invariant is per-asset and cheap; CrossFoot walks the assets only, not the postings.

Deploy order

This realm imports p/journal and p/depreciation. Neither is deployed yet, so the order is p/journal, p/depreciation, then this realm.

Function

IsBalanced

func IsBalanced(owner address) bool
Ask AI Claude ChatGPT

IsBalanced reports whether the book's debits equal its credits.

Param

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/assets.IsBalanced()"

Result