render_example_test.gno
2.73 Kb · 81 lines
1package cliffvestingdemo
2
3// ExampleRender pins the realm's Render output as a testable example.
4func ExampleRender() {
5 print(Render(""))
6 // Output:
7 // # Cliff Vesting
8 //
9 // A pure vesting calculator, demoing the [`p/moul/x/daily/cliffvesting`](/p/moul/x/daily/cliffvesting/v1) library.
10 //
11 // ## A 12-month grant with a 3-month cliff
12 //
13 // `1200` tokens, `Start = 0`, `Cliff = 3`, `End = 12`.
14 //
15 // | month | vested | unvested | % |
16 // |---|---|---|---|
17 // | 0 | 0 | 1200 | 0% |
18 // | 1 | 0 | 1200 | 0% |
19 // | 2 | 0 | 1200 | 0% ← nothing yet |
20 // | 3 | 300 | 900 | 25% ← **cliff** |
21 // | 4 | 400 | 800 | 33% |
22 // | 5 | 500 | 700 | 41% |
23 // | 6 | 600 | 600 | 50% |
24 // | 7 | 700 | 500 | 58% |
25 // | 8 | 800 | 400 | 66% |
26 // | 9 | 900 | 300 | 75% |
27 // | 10 | 1000 | 200 | 83% |
28 // | 11 | 1100 | 100 | 91% |
29 // | 12 | 1200 | 0 | 100% ← fully vested |
30 //
31 // The cliff is a **step, not a ramp**: at month 3 a quarter of the term has elapsed, so `300` tokens unlock at once. After that it accrues linearly.
32 //
33 // ## Claiming
34 //
35 // The library tracks no balances, so the caller supplies what has already been claimed:
36 //
37 // | at month | already claimed | claimable |
38 // |---|---|---|
39 // | 6 | 0 | 600 |
40 // | 6 | 300 | 300 |
41 // | 6 | 600 | 0 |
42 // | 12 | 600 | 600 |
43 //
44 // ## Integer rounding
45 //
46 // All arithmetic is integer, no float ever reaches consensus state. Rounding is **down**, so nobody is ever paid more than they earned, and the final instalment collects the remainder.
47 //
48 // `1000` over `3` periods:
49 //
50 // | t | vested |
51 // |---|---|
52 // | 0 | 0 |
53 // | 1 | 333 |
54 // | 2 | 666 |
55 // | 3 | 1000 |
56 //
57 // `333 + 333 + 334`, and the end is exactly `1000`, never `999`.
58 //
59 // ## Bug 1: a rate truncated to zero
60 //
61 // When the total is smaller than the duration, computing a per-tick rate first truncates it to zero and **nothing ever vests**. Multiplying before dividing keeps it honest. `7` tokens over `1000` ticks:
62 //
63 // | t | vested |
64 // |---|---|
65 // | 100 | 0 |
66 // | 150 | 1 |
67 // | 500 | 3 |
68 // | 1000 | 7 |
69 //
70 // ## Bug 2: a product that leaves int64
71 //
72 // Multiplying first is only safe if the product fits. Over a term measured in **seconds**, `total * elapsed` passes 2^63 for any grant above about `146,036` whole coins, and a wrapped int64 is still a valid int64: `v0` of this library returned a **negative** vested amount and nothing signalled it. `v1` routes the product through a 128-bit intermediate.
73 //
74 // A real two-year schedule, `1789225200` to `1852383600`, at the halfway mark:
75 //
76 // | grant | vested at halfway | expected |
77 // |---|---|---|
78 // | 100000000000 | 50000000000 | 50000000000 |
79 // | 146037000000 | 73018500000 | 73018500000 |
80 // | 318720000000000 | 159360000000000 | 159360000000000 |
81}