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

v0 source pure

Package vesting computes a gno.land account's vesting curve, as a PURE calculator: no state, no balances, no transfers.

Readme View source

p/moul/vesting

The vesting curve the gno.land chain itself enforces, as a pure calculator.

This is not a vesting scheme of its own. It is a faithful reimplementation of tm2's std.VestingSchedule, so a realm can answer "how much of this balance can actually move right now" with the same arithmetic the ante handler uses. Divergence here would be worse than useless, so every rule is copied from tm2/pkg/std/vesting.go rather than designed.

1s, err := vesting.New(106560000000, 1789225200, 1852383600, vesting.Continuous)
2s.Vested(now)              // how much has unlocked
3s.Locked(now)              // what the chain still refuses to move
4s.Spendable(balance, now)  // what can actually leave the account
5s.PermilleVested(now)      // tenths of a percent, for display

The rules, all of them from tm2

Continuous vests linearly between Start and End
Delayed a cliff: nothing before End, everything at or after it, and Start is ignored
rounding down, always
a zero Original means no schedule, which locks nothing
times unix seconds, never compared against the chain's clock

Rounding down is the direction that matters. Reporting one ugnot more than the chain will move turns a page into a lie somebody acts on.

Spendable caps the locked part at the balance: an account that spent while its coins were free can owe the schedule more than it now holds, and the honest answer there is that nothing moves, not a negative number.

Why this is not p/moul/x/daily/cliffvesting

That package is the employee-grant shape (start, cliff, end) for amounts a person is granted. This one is the chain's shape, for amounts a chain holds, and the difference is not only the curve:

cliffvesting v0 multiplied in plain int64. Over the real mainnet term of 63,158,400 seconds that wraps for any grant above 146,036 GNOT, silently: a 318,720,000 GNOT grant returned -6,264,395,224, a negative vested amount that every caller would have treated as real. tm2 reaches for math/big at exactly this point. gno has no math/big, so Vested goes through a 128-bit intermediate via math/bits. cliffvesting v1 now does the same.

The largest schedule this has to survive is the whole genesis allocation, 1,332,999,998 GNOT, and the tests take it there.

What this package cannot do

Find out an address's schedule. Realm code cannot read one. The VM's whole view of an account is banker.GetCoins, which returns the total balance with the locked part included, and no native exposes std.VestingSchedule. The schedule has to come from the caller.

r/moul/vesting is what that constraint looks like in a page.


Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.

⚠️ Disclaimer: provided as-is, without warranty; not security-audited. Full disclaimer: DISCLAIMER.

Overview

Package vesting computes a gno.land account's vesting curve, as a PURE calculator: no state, no balances, no transfers.

It is not a vesting scheme of its own. It is a faithful reimplementation of the one the CHAIN enforces, tm2's std.VestingSchedule, so that a realm can answer "how much of this balance can actually move right now" with the same arithmetic the ante handler uses. Divergence here is worse than useless, so every rule below is copied from tm2/pkg/std/vesting.go rather than designed:

  • Times are unix seconds, and nothing compares them against the chain's clock. A schedule already over when the chain starts is valid and vests everything at once.
  • Continuous vests linearly between Start and End. Delayed is a cliff: nothing before End, everything at or after it, and Start is ignored.
  • Rounding is DOWN, always, so the account never counts as spendable a ugnot the chain would refuse to move.
  • A zero Original is "no schedule", which locks nothing.

Why this is not p/moul/x/daily/cliffvesting

That package is the employee-grant shape (start, cliff, end) and multiplies in plain int64, which silently wraps for the amounts a chain actually holds: over the 63,158,400 second mainnet term, any grant above 146,036 GNOT overflows, and a 318,720,000 GNOT grant reports a NEGATIVE vested amount. tm2 reaches for math/big at exactly this point. gno has no math/big, so Schedule.Vested goes through a 128-bit intermediate instead.

What this package cannot do

Find out an address's schedule. Realm code cannot read one: the VM's whole view of an account is banker.GetCoins, which returns the TOTAL balance with the locked part included, and no native exposes std.VestingSchedule. The schedule has to come from the caller. See gno.land/r/moul/vesting for what that means for a page that wants to show real numbers.

Constants 1

const Continuous, Delayed

1const (
2	// Continuous vests linearly from Start to End. The tm2 default.
3	Continuous Type = iota
4	// Delayed is a cliff: nothing vests until End, then all of it.
5	Delayed
6)
source

Variables 1

Functions 1

func New

1func New(original, start, end int64, typ Type) (Schedule, error)
source

New validates a schedule, applying tm2's own rules in tm2's own order.

The Start >= 0 check is not cosmetic and is the reason Vested can stay in int64 for its subtractions: with 0 <= Start < End, neither End-Start nor now-Start can overflow. tm2 rejects a negative start for exactly this.

Types 2

type Schedule

struct
1type Schedule struct {
2	Original int64 // the granted amount, in the smallest unit
3	Start    int64 // unix seconds; ignored by Delayed
4	End      int64 // unix seconds
5	Type     Type
6}
source

Schedule is one account's vesting plan, as the chain stores it.

Methods on Schedule

func IsZero

method on Schedule
1func (s Schedule) IsZero() bool
source

IsZero reports whether there is no schedule at all, which locks nothing.

func Locked

method on Schedule
1func (s Schedule) Locked(now int64) int64
source

Locked returns the part of Original that has not vested at unix time now. This is what the chain refuses to let leave the account.

func PermilleVested

method on Schedule
1func (s Schedule) PermilleVested(now int64) int64
source

PermilleVested returns the vested share at now in tenths of a percent, rounded down, so a page can show one decimal without a float. Integer only: no float ever reaches consensus state.

func RemainingSeconds

method on Schedule
1func (s Schedule) RemainingSeconds(now int64) int64
source

RemainingSeconds returns how long until the schedule completes, zero once it has. Reported rather than formatted: the caller owns how a duration reads.

func Spendable

method on Schedule
1func (s Schedule) Spendable(balance, now int64) int64
source

Spendable returns how much of balance can actually move at unix time now.

balance is the account's TOTAL, which is what banker.GetCoins reports. The locked part is capped at the balance: an account that has already spent down to less than it still owes to the schedule has nothing spendable, not a negative amount. tm2 reaches the same answer by subtracting locked coins from the balance and refusing the transfer if the result does not cover it.

func Vested

method on Schedule
1func (s Schedule) Vested(now int64) int64
source

Vested returns how much of Original has vested at unix time now.

type Type

ident
1type Type uint8
source

Type selects the curve.

Methods on Type

func String

method on Type
1func (t Type) String() string
source

Imports 2

  • errors stdlib
  • math/bits stdlib

Source Files 4