profile.gno
3.04 Kb · 162 lines
1// Package profile is the Shoal sailor card. One address has one username.
2// This realm holds no ugnot. It does not mint FISH and it does not mint a fish NFT.
3package profile
4
5import (
6 "chain"
7 "chain/runtime/unsafe"
8)
9
10type card struct {
11 username string
12 country string
13 crest string
14}
15
16var (
17 byAddr map[address]*card
18 byName map[string]address
19)
20
21func SetProfile(cur realm, username, country, crest string) {
22 caller := requireUser(cur)
23 takeExact(0)
24 name := cleanUser(username)
25 if name == "" {
26 panic("profile: name")
27 }
28 if !knownCountry(country) {
29 panic("profile: country")
30 }
31 if !knownCrest(crest) {
32 panic("profile: crest")
33 }
34 ensure()
35 if owner, ok := byName[name]; ok && owner != caller {
36 panic("profile: taken")
37 }
38 prev := byAddr[caller]
39 if prev != nil && prev.username == name && prev.country == country && prev.crest == crest {
40 return
41 }
42 if prev != nil && prev.username != name {
43 delete(byName, prev.username)
44 }
45 byAddr[caller] = &card{username: name, country: country, crest: crest}
46 byName[name] = caller
47 chain.Emit("SetProfile", "who", caller.String(), "username", name, "country", country, "crest", crest)
48}
49
50func Has(cur realm, who address) bool {
51 takeExact(0)
52 return cardOf(who) != nil
53}
54
55func UsernameOf(cur realm, who address) string {
56 takeExact(0)
57 body := cardOf(who)
58 if body == nil {
59 return ""
60 }
61 return body.username
62}
63
64func CountryOf(cur realm, who address) string {
65 takeExact(0)
66 body := cardOf(who)
67 if body == nil {
68 return ""
69 }
70 return body.country
71}
72
73func CrestOf(cur realm, who address) string {
74 takeExact(0)
75 body := cardOf(who)
76 if body == nil {
77 return ""
78 }
79 return body.crest
80}
81
82func AddressOf(cur realm, username string) address {
83 takeExact(0)
84 name := cleanUser(username)
85 if name == "" || byName == nil {
86 return address("")
87 }
88 return byName[name]
89}
90
91func requireUser(cur realm) address {
92 if !cur.Previous().IsUserCall() {
93 panic("profile: eoa")
94 }
95 return cur.Previous().Address()
96}
97
98func takeExact(n int64) {
99 sent := unsafe.OriginSend()
100 if n == 0 {
101 if len(sent) != 0 {
102 panic("profile: payment")
103 }
104 return
105 }
106 panic("profile: payment")
107}
108
109func ensure() {
110 if byAddr == nil {
111 byAddr = make(map[address]*card)
112 }
113 if byName == nil {
114 byName = make(map[string]address)
115 }
116}
117
118func cardOf(who address) *card {
119 if byAddr == nil || !who.IsValid() {
120 return nil
121 }
122 return byAddr[who]
123}
124
125func cleanUser(name string) string {
126 for len(name) > 0 && name[0] == ' ' {
127 name = name[1:]
128 }
129 for len(name) > 0 && name[len(name)-1] == ' ' {
130 name = name[:len(name)-1]
131 }
132 if len(name) < 3 || len(name) > 16 {
133 return ""
134 }
135 out := ""
136 for _, c := range name {
137 if c >= 'A' && c <= 'Z' {
138 c = c - 'A' + 'a'
139 }
140 if (c < 'a' || c > 'z') && (c < '0' || c > '9') {
141 return ""
142 }
143 out += string(c)
144 }
145 return out
146}
147
148func knownCountry(s string) bool {
149 switch s {
150 case "Sea", "Vietnam", "Korea", "Japan", "Brazil", "France", "Nigeria", "Canada", "Australia":
151 return true
152 }
153 return false
154}
155
156func knownCrest(s string) bool {
157 switch s {
158 case "wave", "reef", "moon", "sun", "fang", "shell":
159 return true
160 }
161 return false
162}