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.

invoices package

Overview

Package invoices is on-chain accounts receivable: an issuer records an obligation, and the named payer discharges it later, in parts.

The one idea

Every payment realm in this portfolio, and every escrow and vesting package the discovery gate found on either live chain, is PREFUNDED. service_market escrows the exact price at purchase. subscriptions prepays a whole period. payroll, vesting, fee_split and revshare push money that already arrived. In all of them the coins exist before the claim on them does.

An invoice inverts that. It is UNFUNDED AT CREATION: the obligation is recorded first, and the money may never arrive at all.

That one inversion is the whole realm, and it is visible in the arithmetic rather than only in the prose. payroll's invariant carries a pool term because an employer funds first:

Example
1payroll:  Held() == TotalPooled() + Liabilities() + Surplus()
2invoices: Held() ==                 Liabilities() + Surplus()

There is no pool here, and no reserve, because at the moment an invoice is issued this realm holds nothing on its behalf and promises nothing. Issuing costs the issuer a storage deposit and nothing else.

What being unfunded forces

Two features that would be decoration on a prefunded rail are load- bearing here, and they are the reason this is a realm rather than a field on an existing one:

  • PARTIAL SETTLEMENT. When the money is already in escrow, "pay" is a release of a known amount and partiality is meaningless. When it is not, a payer who can send 60% today is the ordinary case. Pay accepts any amount up to the outstanding balance, repeatedly.
  • AN OVERDUE STATE. A prefunded claim cannot be late. An unfunded one can, so lateness is a state other realms can read and act on. That readable state is the integration surface this realm exists to provide, in the same way subscriptions exists to provide Entitled.

No escrow, no acceptance, no arbitration

An invoice is billed for work already done, so there is nothing for this realm to hold back. Money paid is credited to the issuer's ledger balance in the same transaction. There is no acceptance window and no release step — those belong to service_market, whose model is the opposite one.

Consequently this realm DOES NOT arbitrate. A payer who disputes a bill simply does not pay it, and the invoice goes overdue. That is the mechanism off chain too, and inventing a dispute path here would make this a worse copy of service_market rather than a complement to it.

Payment is pull, in the direction that cannot fail

Pay credits the issuer's ledger balance; the issuer later calls Claim. The push alternative — paying the issuer inside the payer's own transaction — would let the issuer's address decide whether the PAYER's settlement succeeds. An issuer that is a realm which panics on receipt, or is simply unable to accept, would make its own invoices unpayable and the failure would land on the payer. Pull puts every failure on the party who owns it.

Anyone may issue an invoice naming anyone. Read this before trusting a total.

There is no permission to bill someone, because requiring one would mean a registry of who may invoice whom, which is a different application. The honest consequence is stated here rather than discovered later:

Example
1AN UNPAID INVOICE IS AN UNVERIFIED CLAIM BY ITS ISSUER.

So this realm deliberately exposes NO global "what does this address owe" aggregate. Such a number would be the sum of every stranger's self-reported assertion, and reading it as a credit signal is the error the getter's existence would invite. What it exposes instead is ISSUER-SCOPED: OutstandingFrom and OverdueFrom answer "what does this payer owe THIS issuer", which is the question a consumer realm can actually act on, because a consumer gating its own service on its own unpaid bills is trusting only itself.

Two bounds back that up mechanically. Issuing costs a storage deposit paid by the ISSUER, so volume is not free to the party who benefits from it; and MaxRecordsPerIssuer caps one issuer's STORED records, which is an availability bound on that issuer's own namespace and no one else's. Neither bound makes an unpaid claim true, and they are not presented as doing so.

Both bounds are PER ISSUER, and the global total is not bounded. Sybil issuers multiply it: N funded addresses yield N*MaxRecordsPerIssuer records, and the per-record deposit paid by each is the only thing making that expensive. Two consequences follow, stated here rather than discovered:

  • A payer named on an invoice CANNOT remove it. Void is issuer-only, because forgiving a debt is the issuer's to do, and Release refuses an open record; so a stranger's open invoice naming you persists until that stranger voids it. What absorbs this is the design above rather than a bound: no global aggregate reads such a record, and the issuer-scoped getters a consumer realm actually calls stay gas-bounded at MaxRecordsPerIssuer, so a victim's reads are never charged for an attacker's volume. The residual harm is an off-chain reader's, and the unverified-claim warning above is what it is for.
  • An issuer record is never reclaimed. Release decrements its count but does not delete the issuers entry, because `issued` and `invoiced` are LIFETIME figures that IssuedOf reports, and deleting the entry would reset them to zero and make that getter lie. Global state therefore grows monotonically. Keeping an honest counter is worth more than reclaiming one AVL node.

The cap counts STORED records rather than OPEN ones, and the difference is load-bearing rather than a detail. OutstandingFrom, OverdueFrom and HasOverdueFrom — the getters a consumer realm calls, and therefore the ones whose gas cost is someone else's problem — iterate an issuer's whole prefix, settled and voided records included, because a settled record is not removed until Release. A cap on open invoices alone would bound the obligations and leave that iteration unbounded: an issuer could issue and void repeatedly, keeping `open` at zero while their prefix grew without limit, until a consumer's read ran out of gas. Capping what is stored bounds both, since open can never exceed stored.

Finding "every invoice naming me" is therefore an off-chain indexer's job: on chain it would be an unbounded iteration over every issuer that has ever billed anyone, and the answer would be untrustworthy anyway.

STORAGE

An issuer record is paid for by that issuer's first Issue; an invoice record by the Issue that creates it. Release removes a finished invoice and the chain refunds the freed deposit to the transaction that frees it, which is the standing incentive to tidy up a record that is no longer anyone's business.

What this realm does NOT do

  • It does not verify that work happened, value it, or arbitrate.
  • It holds ugnot and only ugnot.
  • It has no admin. The deploying namespace receives protocol fees and swept surplus and has no other privilege: no setter, no pause, no ability to void an invoice or touch a balance.

Function

NextID

func NextID() int64
Ask AI Claude ChatGPT

NextID is the id the next Issue will allocate. Exposed so a caller can tell a never-issued id from a released one.

Command

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

Result