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

allocations.gno

2.43 Kb · 92 lines
 1package sales
 2
 3import (
 4	"strconv"
 5	"strings"
 6
 7	"chain"
 8	"gno.land/p/samcrew/launchpad/meta/v1"
 9	sm "gno.land/p/samcrew/launchpad/safemath/v1"
10	"gno.land/p/samcrew/launchpad/vesting/v1"
11)
12
13const (
14	maxAllocationBytes = 8192
15	maxAllocations     = 50
16)
17
18type allocation struct {
19	beneficiary address
20	amount      int64
21	start       int64
22	cliff       int64
23	duration    int64
24	revocable   bool
25}
26
27func decimal(s string) int64 {
28	if s == "" || (len(s) > 1 && s[0] == '0') {
29		panic("launchpad/sales: noncanonical allocation decimal")
30	}
31	for i := 0; i < len(s); i++ {
32		if s[i] < '0' || s[i] > '9' {
33			panic("launchpad/sales: noncanonical allocation decimal")
34		}
35	}
36	v, err := strconv.ParseInt(s, 10, 64)
37	if err != nil {
38		panic("launchpad/sales: allocation decimal out of range")
39	}
40	return v
41}
42
43// parseAllocations accepts a single bounded, canonical MsgCall argument.
44// Each record is address:amount:start:cliff:duration:revocable, with the
45// address order providing both a stable encoding and duplicate rejection.
46func parseAllocations(encoded string) ([]allocation, int64) {
47	if len(encoded) > maxAllocationBytes {
48		panic("launchpad/sales: allocations too large")
49	}
50	if encoded == "" {
51		return nil, 0
52	}
53	records := strings.Split(encoded, ";")
54	if len(records) > maxAllocations {
55		panic("launchpad/sales: too many allocations")
56	}
57	out := make([]allocation, 0, len(records))
58	var total int64
59	previous := ""
60	for _, record := range records {
61		fields := strings.Split(record, ":")
62		if len(fields) != 6 || fields[0] <= previous {
63			panic("launchpad/sales: invalid allocation order or fields")
64		}
65		who := address(fields[0])
66		if !meta.ValidAddress(who) || who == chain.PackageAddress(salesPath) {
67			panic("launchpad/sales: invalid allocation address")
68		}
69		a := allocation{
70			beneficiary: who,
71			amount:      decimal(fields[1]),
72			start:       decimal(fields[2]),
73			cliff:       decimal(fields[3]),
74			duration:    decimal(fields[4]),
75		}
76		if a.amount == 0 || (fields[5] != "0" && fields[5] != "1") {
77			panic("launchpad/sales: invalid allocation amount or revocability")
78		}
79		a.revocable = fields[5] == "1"
80		if a.duration == 0 {
81			if a.start != 0 || a.cliff != 0 || a.revocable {
82				panic("launchpad/sales: invalid immediate allocation")
83			}
84		} else {
85			vesting.Schedule{Total: a.amount, Start: a.start, Cliff: a.cliff, Duration: a.duration}.Validate()
86		}
87		total = sm.Add(total, a.amount)
88		out = append(out, a)
89		previous = fields[0]
90	}
91	return out, total
92}