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.

payslips package

Overview

Package payslips publishes graduated withholding statements over the wage facts of a live r/payroll.

An employer sets a withholding schedule — a p/tiers bracket table — and publishes a payslip naming the employees it covers. For each one this realm reads payroll's own views, reconstructs the settled gross, prices it through the schedule, and records the result. Nothing is supplied by the caller except the schedule and the selection: every amount comes from payroll.

WHAT THIS REALM IS, AND WHAT IT CANNOT BE

It is a REPORTER. It holds no coins, has no withdrawal path, and moves nothing. It does not deduct the withholding it computes — payroll settles wages and this realm states what a graduated schedule would have withheld from them. The separation is deliberate and it is also forced: r/payroll is live and immutable, so a graduated rate cannot be charged inside it, and a realm that charged one here would be a second custody surface over coins payroll already holds.

The consequence is the realm's only real safety argument and it is a strong one: THIS REALM CANNOT WRITE A WRONG PAYMENT, BECAUSE IT CANNOT WRITE A PAYMENT. The failure mode it does have is a wrong or incomplete REPORT, and every mitigation below is aimed there.

SETTLED IS NOT EARNED

The gross on a payslip is the gross payroll has SETTLED for the position that is open right now:

Example
1gross = PaidPeriodsOf(employer, employee) * PerPeriodOf(employer, employee)

That identity is exact rather than approximate, and the reason is structural: payroll exports no setter for perPeriod, so a position's rate is fixed for its whole life by the Hire that created it, and paidPeriods only ever increases. The product is therefore the position's lifetime settled gross and not an estimate of it.

It is still not the employee's lifetime earnings under this employer, and the gap is not small. payroll's Release removes a settled position and frees its storage, and Hire refuses only while a position is OPEN — so Terminate → Release → Hire is a legal sequence that resets paidPeriods to zero and makes every period before it unreadable from any view. The Released event carries paidPeriods, but an event is not realm-readable. So:

Example
1A PAYSLIP IS SCOPED TO THE POSITION OPEN AT ITS OWN HEIGHT, AND MUST NEVER
2BE READ AS LIFETIME GROSS.

The failure mode is silent under-reporting, which is the one error a reader cannot detect from the number itself. The mitigations are that every payslip records its height, its own scope, and a coverage line; that Coverage and OmittedRoster say how much of the employer's roster the payslip left out; and that an employee named with no open position PANICS rather than being dropped — a silently-dropped line is exactly the under-report this realm cannot detect after the fact.

PERIODS THAT ARE DUE BUT NOT ACCRUED ARE EXCLUDED, AND DISCLOSED

payroll accrues on demand: anyone may call Accrue, and until someone does, completed periods are due and unsettled. A payslip published before an Accrue therefore reports a smaller gross than the employee is owed. Rather than guess, each line records dueGross — DuePeriods times perPeriod at the payslip's height — and UnsettledGrossOf totals it. A payslip whose UnsettledGrossOf is non-zero is complete about what was settled and explicit about what was not.

THE PLATFORM FEE IS AN INTERVAL, AND THAT IS A MEASUREMENT

payroll takes an accrual fee of AccrueFeeBps from each settlement, and the obvious reconstruction — a flat rate on the cumulative gross — is wrong. Its Accrue settles every due period in ONE deposit and floors the fee on that aggregate, so the fee actually taken depends on how the accruals were BATCHED, and payroll exports no view of the batching. What it does export is paidPeriods and perPeriod, and those bound the fee exactly:

Example
1feeLo = paidPeriods * floor(perPeriod * bps / RateScale)   one period per call
2feeHi = floor(gross * bps / RateScale)                     one call for all

Both endpoints are achievable, so the interval is tight and not a hedge: floor(x) + floor(y) <= floor(x+y) gives the upper bound, and floor(n*x) >= n*floor(x) gives the lower. They coincide whenever perPeriod * bps divides RateScale evenly, which is the common case.

So PlatformFeeRange and NetRange return pairs. A single platform-fee number here would have been plausible, derivable, and wrong for any employee whose accruals were batched — and nothing on the chain would have contradicted it.

THE SCHEDULE PRICES GROSS, AND THE TWO DEDUCTIONS ARE NEVER COMBINED

The withholding schedule is applied to gross. payroll's platform fee is a separate deduction that payroll has already taken. They are reported side by side and never folded into one effective rate, because they have different payees, different arithmetic (graduated versus flat) and different certainty (exact versus an interval).

A schedule in p/tiers carries no identity and no effective date, and a falling rate array is legal there — it is a volume discount — so a transposed table is a silent authoring error that tiers.New cannot detect. Two consequences this realm acts on: every payslip SNAPSHOTS the schedule that priced it, so a later SetSchedule cannot retroactively re-price a published payslip; and OverWithheld names the one arithmetic outcome that is almost certainly an authoring error — a schedule withholding more than the employee actually received — rather than hiding it or refusing to publish it.

AUTHORITY

An employer publishes about their OWN roster: the employer of every payslip is cur.Previous().Address(), never an argument. This realm therefore cannot publish a payslip about a roster the caller does not hold, and a payslip's key is the employer that published it.

Functions

ChargeOn

func ChargeOn(employer address, gross int64) int64
Ask AI Claude ChatGPT

ChargeOn quotes an employer's current schedule against an arbitrary gross without publishing anything. The quote a caller needs before choosing a schedule, and the one view here that is not about a record.

Params

Command

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

Result

Count

func Count(employer address) int
Ask AI Claude ChatGPT

Count is how many payslips an employer has published. Sequence numbers run 1..Count with no gaps: a payslip is never removed.

Param

Command

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

Result

Coverage

func Coverage(employer address, seq int64) (listed, priced, zeroGross, withDue, ended, dupes, rosterAt, openAt int)
Ask AI Claude ChatGPT

Coverage is the payslip's coverage line, and it is the number to read before any total.

Example
1listed     employees the caller named, duplicates included
2priced     lines in the record
3zeroGross  priced lines whose settled gross is zero
4withDue    priced lines with gross due and not accrued
5ended      priced lines whose position's clock had stopped
6dupes      duplicate names dropped
7rosterAt   open positions the employer held at this height
8openAt     open positions across every employer at this height

listed == priced + dupes always, by construction: nothing else is dropped. rosterAt - priced is OmittedRoster.

Params

Command

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

Result

Employers

func Employers() int
Ask AI Claude ChatGPT

Employers is how many addresses have set a schedule here. Note the tense: a schedule survives, so this is not a count of employers with payslips.

Command

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

Result

FeeBpsAt

func FeeBpsAt(employer address, seq int64) int64
Ask AI Claude ChatGPT

FeeBpsAt is the platform fee rate this payslip was priced against.

Params

Command

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

Result

GrossOf

func GrossOf(employer address, seq int64) int64
Ask AI Claude ChatGPT

GrossOf is a payslip's total SETTLED gross. Read it beside UnsettledGrossOf and OmittedRoster: neither is an error, and both change what this number means.

Params

Command

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

Result

Has

func Has(employer address, seq int64) bool
Ask AI Claude ChatGPT

Has reports whether a payslip exists, so the record accessors can be pre-checked rather than recovered from. Every one of them panics on an absent record, which is deliberate — returning a zero figure for a payslip that was never published is the exact silent under-report this realm exists to refuse — but a panic is only a usable contract when the caller has a way not to hit it.

It is here before the first deploy rather than in a successor because a realm's bytes are frozen by addpkg: an accessor a consumer needs cannot be added afterwards.

Params

Command

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

Result

HasSchedule

func HasSchedule(employer address) bool
Ask AI Claude ChatGPT

HasSchedule reports whether an employer has set a schedule, without panicking on absence — every other schedule accessor panics, which makes them unusable as an existence probe.

Param

Command

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

Result

Height

func Height() int64
Ask AI Claude ChatGPT

Height is the current chain height, exposed so a caller can see the clock this realm reads payroll against.

Command

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

Result

LineEnded

func LineEnded(employer address, seq int64, i int) bool
Ask AI Claude ChatGPT

LineEnded reports whether this line's position had been terminated or had passed its end height when the payslip was published. An ended position can still carry unsettled gross: payroll keeps the obligation alive past Terminate on purpose.

Params

Command

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

Result

LineNetRange

func LineNetRange(employer address, seq int64, i int) (lo, hi int64)
Ask AI Claude ChatGPT

LineNetRange bounds what this employee kept. Same interval as NetRange, one line.

Params

Command

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

Result

Lines

func Lines(employer address, seq int64) int
Ask AI Claude ChatGPT

Lines is how many employees a payslip prices.

Params

Command

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

Result

MarginalRateOf

func MarginalRateOf(employer address, gross int64) int64
Ask AI Claude ChatGPT

MarginalRateOf is the rate an employer's current schedule would charge on the next ugnot of gross past the given amount, of RateScale.

Params

Command

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

Result

NetRange

func NetRange(employer address, seq int64) (lo, hi int64)
Ask AI Claude ChatGPT

NetRange bounds what the employee kept: gross, less the schedule's withholding, less payroll's fee. The interval is inherited whole from PlatformFeeRange — the withholding itself is exact.

IT CAN BE NEGATIVE, and that is a finding rather than a bug. A schedule may legally charge up to 100%, and payroll's fee is taken on top of whatever this realm withholds, so a schedule at or near the ceiling withholds more than the employee actually received. OverWithheld names that case.

Params

Command

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

Result

NumTiersOf

func NumTiersOf(employer address) int
Ask AI Claude ChatGPT

NumTiersOf is how many brackets an employer's current schedule has.

Param

Command

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

Result

OmittedRoster

func OmittedRoster(employer address, seq int64) int
Ask AI Claude ChatGPT

OmittedRoster is how many of the employer's open positions this payslip did not price. It is the one number that says whether a payslip is complete, and a total read without it is being read wrongly.

It cannot be negative: every priced line has an open position under this employer at the payslip's height, duplicates are dropped before pricing, so priced is a count of distinct open positions and rosterAt is their total.

Params

Command

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

Result

OverWithheld

func OverWithheld(employer address, seq int64) bool
Ask AI Claude ChatGPT

OverWithheld reports whether this payslip's withholding plus payroll's largest possible fee exceeds the gross. True is almost always a transposed or mis-scaled rate array, which p/tiers cannot detect, so this realm surfaces it instead of refusing to publish it — a refusal would lose the evidence.

Params

Command

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

Result

Payslips

func Payslips() int
Ask AI Claude ChatGPT

Payslips is how many payslips exist across every employer.

Command

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

Result

PlatformFeeRange

func PlatformFeeRange(employer address, seq int64) (lo, hi int64)
Ask AI Claude ChatGPT

PlatformFeeRange bounds the accrual fee r/payroll already took on this gross. It is an interval because the fee depends on accrual batching, which payroll does not expose — see the package doc. lo == hi is common and means the batching could not have mattered.

Params

Command

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

Result

Publish

func Publish(cur realm, employees string) int64
Ask AI Claude ChatGPT

Publish prices the named employees under the caller's own roster and records a payslip. It returns the payslip's sequence number, which is per-employer and starts at 1.

employees is a comma-separated list of addresses, at most MaxEmployeesPerCall of them. Order is the caller's and is preserved in the record, so a payslip reads in the order it was requested.

THREE REFUSAL RULES, AND THE ASYMMETRY BETWEEN THEM IS THE POINT:

  • An employee with NO OPEN POSITION under the caller PANICS and fails the whole call. A silently-dropped line is an under-report, and under- reporting is the one error this product cannot detect after the fact. A caller that wants to skip an absent position must leave it out, which is a decision in the transaction rather than a silence in the record.
  • A DUPLICATE is dropped, counted in the coverage line, and does not fail the call. Dropping it cannot under-report: the employee is still priced once, and double-counting them would be worse.
  • An employee with ZERO SETTLED GROSS is PRICED, not filtered. A payslip for a position that has settled nothing yet is a correct payslip with a gross of zero, and filtering it would turn a true statement into a missing line.

An empty list is refused. Nothing here filters a list down to nothing, so after deduplication a call always prices at least one employee.

Param

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/payslips" -func "Publish" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "onyx-1" -remote "https://rpc.onyx.testnets.gno.land" ADDRESSgnokey query -remote "https://rpc.onyx.testnets.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/payslips" -func "Publish" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "onyx-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.onyx.testnets.gno.land" call.tx
  

PublishedAt

func PublishedAt(employer address, seq int64) int64
Ask AI Claude ChatGPT

PublishedAt is the height a payslip was published at, which is the height every figure in it was read at.

Params

Command

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

Result

Render

func Render(path string) string
Ask AI Claude ChatGPT

Render serves the index at "", an employer's page at "<address>", and one payslip at "<address>/<seq>".

Every caller-supplied string reaching the output goes through the sanitizer. This realm stores no free text, so the untrusted bytes are the path itself and the addresses in a record — and that is still the one injection-shaped surface a realm with no custody has.

Param

Command

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

Result

ScheduleAt

func ScheduleAt(employer address, seq int64) (edges, rates string)
Ask AI Claude ChatGPT

ScheduleAt is the schedule that priced this payslip, as comma-separated lists that round-trip exactly through SetSchedule. The snapshot, not the employer's current schedule — see ScheduleOf for that.

Params

Command

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

Result

ScheduleOf

func ScheduleOf(employer address) (edges, rates string)
Ask AI Claude ChatGPT

ScheduleOf is an employer's CURRENT schedule, as lists that round-trip through SetSchedule. It is not what priced any published payslip — see ScheduleAt.

Param

Command

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

Result

SetSchedule

func SetSchedule(cur realm, edges, rates string) int
Ask AI Claude ChatGPT

SetSchedule installs the caller's own withholding schedule and returns its tier count.

edges and rates are comma-separated int64 lists in p/tiers's units: rates are of RateScale, edges are gross amounts in ugnot. There is one more rate than there are edges, because the last tier is unbounded. An EMPTY edges string is legal and means a one-tier schedule — a flat rate — which is the shape to use for a flat withholding and the shape this realm uses internally for the platform fee.

Example
1SetSchedule(cross(cur), "1000000,5000000", "500,1500,2500")

is 5% on the first 1,000,000 ugnot of gross, 15% on the next 4,000,000, and 25% above 5,000,000.

REPLACING A SCHEDULE DOES NOT RE-PRICE ANYTHING. Every published payslip carries the schedule that priced it, so this only affects payslips published after it. That is a design requirement rather than a convenience: a published statement whose numbers change under it is not a record.

A FALLING RATE ARRAY IS ACCEPTED, because p/tiers accepts one — it is a volume discount, which is a real schedule. The consequence is that a transposed array is a silent authoring error here, and neither this realm nor tiers can detect it. Read back ScheduleOf after setting one.

Params

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/payslips" -func "SetSchedule" -args $'' -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "onyx-1" -remote "https://rpc.onyx.testnets.gno.land" ADDRESSgnokey query -remote "https://rpc.onyx.testnets.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/payslips" -func "SetSchedule" -args $'' -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "onyx-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.onyx.testnets.gno.land" call.tx
  

TierBreakdown

func TierBreakdown(employer address, seq int64, i int) string
Ask AI Claude ChatGPT

TierBreakdown is the withholding on one line split by bracket, as a comma-separated list in tier order — the column a payslip reader actually wants, because it shows which brackets the gross reached.

It is recomputed from the payslip's own schedule snapshot rather than stored, so it is exact for the schedule that priced the line and costs no storage. It cross-foots the column against the stored total before returning it: p/tiers DOCUMENTS that Charge is defined as the sum of ChargeByTier, and a documented identity across a package boundary is worth one loop to assert.

Params

Command

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

Result

UnsettledGrossOf

func UnsettledGrossOf(employer address, seq int64) int64
Ask AI Claude ChatGPT

UnsettledGrossOf is gross that had completed and not been accrued at the payslip's height, and is therefore EXCLUDED from GrossOf. Non-zero is normal: payroll accrues on demand.

Params

Command

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

Result

WithheldOf

func WithheldOf(employer address, seq int64) int64
Ask AI Claude ChatGPT

WithheldOf is the schedule's total charge on the gross.

Params

Command

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

Result