vault.gno
6.29 Kb · 231 lines
1package game_vault
2
3import (
4 "chain/runtime/unsafe"
5 "strconv"
6
7 "gno.land/p/akkadia/beta/accesscontrol"
8 "gno.land/r/akkadia/beta/admin"
9 gameworld "gno.land/r/akkadia/beta/game_world"
10)
11
12const (
13 maxInt64 = int64(9223372036854775807)
14 maxUint64 = uint64(18446744073709551615)
15 gameVaultList = 100
16)
17
18type RewardPoolStatus int
19
20const (
21 RewardPoolStatusInvalid RewardPoolStatus = iota
22 RewardPoolStatusOpen
23 RewardPoolStatusLocked
24 RewardPoolStatusSettled
25 RewardPoolStatusCanceled
26)
27
28type RewardPool struct {
29 ID uint64
30 PoolKey string
31 GameWorldID uint64
32 Status RewardPoolStatus
33 ParticipationCommittedTotal int64
34 SystemFundedTotal int64
35 SystemRefundedTotal int64
36 DistributedTotal int64
37 Balance int64
38 DistributionCount uint64
39}
40
41type RewardPoolView struct {
42 PoolKey string
43 GameWorldID uint64
44 Status RewardPoolStatus
45 ParticipationCommittedTotal int64
46 DistributedTotal int64
47 Balance int64
48 DistributionCount uint64
49}
50
51func CreateRewardPool(cur realm, poolKey string, gameWorldID uint64) uint64 {
52 accesscontrol.MustGetAdminOrOperatorCaller(0, cur, admin.IsAdmin, admin.IsOperator)
53 assertNoAttachedCoins()
54 assertKeyLength(poolKey, "pool key")
55 if rewardPoolIDs.Has(poolKey) {
56 panic("reward pool key already exists")
57 }
58 assertGameWorldExists(gameWorldID)
59 if latestRewardPoolID == maxUint64 {
60 panic("reward pool id exhausted")
61 }
62
63 latestRewardPoolID++
64 poolID := latestRewardPoolID
65 treeSet(rewardPools, poolID, RewardPool{
66 ID: poolID,
67 PoolKey: poolKey,
68 GameWorldID: gameWorldID,
69 Status: RewardPoolStatusOpen,
70 })
71 rewardPoolIDs.Set(poolKey, poolID)
72 getOrCreateIDSet(gameWorldRewardPoolIDs, gameWorldID).Set(uint64Key(poolID), true)
73 return poolID
74}
75
76func LockRewardPool(cur realm, poolKey string) {
77 accesscontrol.MustGetAdminOrOperatorCaller(0, cur, admin.IsAdmin, admin.IsOperator)
78 assertNoAttachedCoins()
79 poolID, pool := mustGetRewardPoolByKey(poolKey)
80 if pool.Status == RewardPoolStatusLocked {
81 return
82 }
83 if pool.Status != RewardPoolStatusOpen {
84 panic("reward pool cannot be locked")
85 }
86 pool.Status = RewardPoolStatusLocked
87 treeSet(rewardPools, poolID, pool)
88}
89
90func CancelRewardPool(cur realm, poolKey string) {
91 accesscontrol.MustGetAdminOrOperatorCaller(0, cur, admin.IsAdmin, admin.IsOperator)
92 assertNoAttachedCoins()
93 poolID, pool := mustGetRewardPoolByKey(poolKey)
94 if pool.Status == RewardPoolStatusCanceled {
95 return
96 }
97 if pool.Status != RewardPoolStatusOpen {
98 panic("reward pool cannot be canceled")
99 }
100 if pool.ParticipationCommittedTotal != 0 || pool.DistributedTotal != 0 {
101 panic("reward pool with committed funds cannot be canceled")
102 }
103 pool.Status = RewardPoolStatusCanceled
104 treeSet(rewardPools, poolID, pool)
105}
106
107func SettleRewardPool(cur realm, poolKey string) {
108 accesscontrol.MustGetAdminOrOperatorCaller(0, cur, admin.IsAdmin, admin.IsOperator)
109 assertNoAttachedCoins()
110 poolID, pool := mustGetRewardPoolByKey(poolKey)
111 if pool.Status == RewardPoolStatusSettled {
112 return
113 }
114 if pool.Status != RewardPoolStatusLocked {
115 panic("reward pool must be locked before settlement")
116 }
117 if pool.Balance != 0 {
118 panic("reward pool balance must be zero before settlement")
119 }
120 pool.Status = RewardPoolStatusSettled
121 treeSet(rewardPools, poolID, pool)
122}
123
124func GetRewardPoolID(poolKey string) uint64 {
125 assertKeyLength(poolKey, "pool key")
126 value, found := stringGet(rewardPoolIDs, poolKey)
127 if !found {
128 return 0
129 }
130 return value.(uint64)
131}
132
133func GetRewardPool(poolID uint64) RewardPool {
134 value, found := treeGet(rewardPools, poolID)
135 if !found {
136 panic("reward pool not found: " + strconv.FormatUint(poolID, 10))
137 }
138 return value.(RewardPool)
139}
140
141func GetRewardPoolByKey(poolKey string) RewardPoolView {
142 _, pool := mustGetRewardPoolByKey(poolKey)
143 return RewardPoolView{
144 PoolKey: pool.PoolKey,
145 GameWorldID: pool.GameWorldID,
146 Status: pool.Status,
147 ParticipationCommittedTotal: pool.ParticipationCommittedTotal,
148 DistributedTotal: pool.DistributedTotal,
149 Balance: pool.Balance,
150 DistributionCount: pool.DistributionCount,
151 }
152}
153
154func mustGetRewardPoolByKey(poolKey string) (uint64, RewardPool) {
155 poolID := GetRewardPoolID(poolKey)
156 if poolID == 0 {
157 panic("reward pool not found: " + poolKey)
158 }
159 return poolID, GetRewardPool(poolID)
160}
161
162func GetLatestRewardPoolID() uint64 { return latestRewardPoolID }
163
164func GetRewardPoolCountByGameWorld(gameWorldID uint64) int {
165 ids, found := getIDSet(gameWorldRewardPoolIDs, gameWorldID)
166 if !found {
167 return 0
168 }
169 return ids.Size()
170}
171
172func ListRewardPoolIDsByGameWorld(gameWorldID uint64, page, count int) []uint64 {
173 ids, _ := getIDSet(gameWorldRewardPoolIDs, gameWorldID)
174 return listIDs(ids, page, count)
175}
176
177func assertGameWorldExists(gameWorldID uint64) {
178 if !gameworld.HasGameWorld(gameWorldID) {
179 panic("game world not found: " + strconv.FormatUint(gameWorldID, 10))
180 }
181}
182
183func assertNoAttachedCoins() {
184 if len(unsafe.OriginSend()) != 0 {
185 panic("attached coins not allowed")
186 }
187}
188
189func assertExactUgnotPayment(expected int64) {
190 coins := unsafe.OriginSend()
191 if len(coins) != 1 || coins[0].Denom != "ugnot" || coins[0].Amount != expected {
192 panic("exact payment required: expected " + strconv.FormatInt(expected, 10) + "ugnot, got " + coins.String())
193 }
194}
195
196func getAttachedUgnotDeposit() int64 {
197 coins := unsafe.OriginSend()
198 if len(coins) != 1 || coins[0].Denom != "ugnot" || coins[0].Amount <= 0 {
199 panic("positive ugnot deposit required")
200 }
201 return coins[0].Amount
202}
203
204func assertKeyLength(value, name string) {
205 if len(value) < 1 || len(value) > 128 {
206 panic(name + " length must be between 1 and 128")
207 }
208}
209
210func assertPageCount(page, count int) {
211 if page < 1 {
212 panic("page must be at least 1")
213 }
214 if count < 1 {
215 panic("count must be at least 1")
216 }
217 if count > gameVaultList {
218 panic("count exceeds game vault list limit")
219 }
220}
221
222func assertNewOperationKey(operationKey string) {
223 assertKeyLength(operationKey, "operation key")
224 if processedOperationKeys.Has(operationKey) {
225 panic("operation key already processed")
226 }
227}
228
229func HasProcessedOperationKey(operationKey string) bool {
230 return processedOperationKeys.Has(operationKey)
231}