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

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}