@@ -8,83 +8,131 @@ import (
8
8
"gno.land/p/demo/ufmt"
9
9
)
10
10
11
- var (
12
- totalDelegated = uint64(0)
11
+ // stakerState encapsulates all staking-related data.
12
+ type stakerState struct {
13
+ totalDelegated uint64
14
+ delegatorAmount *avl.Tree // caller -> amount.
15
+ delegatedFromTo *avl.Tree // caller -> (inner tree: to -> amount).
16
+ delegatedTo *avl.Tree // to -> amount.
17
+ delegationHistory *avl.Tree // caller -> []DelegationHistory.
18
+ delegationSnapShotHistory *avl.Tree // to -> []DelegationSnapShotHistory.
19
+ }
13
20
14
- delegatorAmount = avl.NewTree() // caller => amount
15
- delegatedFromTo = avl.NewTree() // caller => to => amount
16
- delegatedTo = avl.NewTree() // to => amount
17
- )
18
-
19
- func delegate(to std.Address, amount uint64) {
20
- caller := std.PrevRealm().Addr().String()
21
- toStr := to.String()
21
+ func (s stakerState) TotalDelegated() uint64 { return s.totalDelegated }
22
+ func (s stakerState) DelegatorAmount() *avl.Tree { return s.delegatorAmount }
23
+ func (s stakerState) DelegatedFromTo() *avl.Tree { return s.delegatedFromTo }
24
+ func (s stakerState) DelegatedTo() *avl.Tree { return s.delegatedTo }
25
+ func (s stakerState) DelegationHistory() *avl.Tree { return s.delegationHistory }
26
+ func (s stakerState) DelegationSnapShotHistory() *avl.Tree { return s.delegationSnapShotHistory }
27
+
28
+ // Global state instance.
29
+ var state = newStakerState()
30
+
31
+ // newStakerState creates and returns a new StakerState.
32
+ func newStakerState() *stakerState {
33
+ return &stakerState{
34
+ totalDelegated: 0,
35
+ delegatorAmount: avl.NewTree(),
36
+ delegatedFromTo: avl.NewTree(),
37
+ delegatedTo: avl.NewTree(),
38
+ delegationHistory: avl.NewTree(),
39
+ delegationSnapShotHistory: avl.NewTree(),
40
+ }
41
+ }
22
42
23
- // initialize the internal tree for callers to `delegatedFromTo`
24
- innerTree := getOrCreateInnerTree(delegatedFromTo, caller)
43
+ // getOrCreateInnerTree returns the inner tree for the given caller,
44
+ // creating it if it does not exist.
45
+ func (s *stakerState) getOrCreateInnerTree(caller string) *avl.Tree {
46
+ if inner, exists := s.delegatedFromTo.Get(caller); exists {
47
+ return inner.(*avl.Tree)
48
+ }
49
+ innerTree := avl.NewTree()
50
+ s.delegatedFromTo.Set(caller, innerTree)
51
+ return innerTree
52
+ }
53
+
54
+ // updateUint64 updates the value at key in the given tree by delta.
55
+ // If add is true, it increments the value; otherwise, it decrements.
56
+ func (s *stakerState) updateUint64(tree *avl.Tree, key string, delta uint64, add bool) {
57
+ var current uint64 = 0
58
+ if value, exists := tree.Get(key); exists {
59
+ current = value.(uint64)
60
+ }
61
+ if add {
62
+ current += delta
63
+ } else {
64
+ if current < delta {
65
+ panic(ufmt.Sprintf("not enough value for key %s", key))
66
+ }
67
+ current -= delta
68
+ }
69
+ tree.Set(key, current)
70
+ }
25
71
26
- totalDelegated += amount
72
+ // Delegate processes a delegation from the caller to the given address.
73
+ func (s *stakerState) Delegate(to std.Address, amount uint64) {
74
+ caller := std.PrevRealm().Addr().String()
75
+ toStr := to.String()
27
76
28
- // update delegator amount
29
- updateUint64InTree(delegatorAmount, caller, amount, true)
77
+ innerTree := s.getOrCreateInnerTree(caller)
30
78
31
- // update delegatedFromTo's inner tree
32
- updateUint64InTree(innerTree, toStr, amount, true)
79
+ s.totalDelegated += amount
33
80
34
- // update delegatedTo
35
- updateUint64InTree(delegatedTo, toStr, amount, true)
81
+ s.updateUint64(s.delegatorAmount, caller, amount, true)
82
+ s.updateUint64(innerTree, toStr, amount, true)
83
+ s.updateUint64(s.delegatedTo, toStr, amount, true)
36
84
37
85
timeStamp := uint64(time.Now().Unix())
38
- // update delegation history
39
- delegation := DelegationHistory{
40
- to: to,
41
- amount: amount,
42
- timestamp: timeStamp,
43
- height: uint64(std.GetHeight()),
44
- add: true, // if true, delegation
45
- }
46
86
47
- history := make([]DelegationHistory, 0)
48
- if value, exists := delegationHistory.Get(caller); exists {
87
+ // Update delegation history.
88
+ history := []DelegationHistory{}
89
+ if value, exists := s.delegationHistory.Get(caller); exists {
49
90
history = value.([]DelegationHistory)
50
91
}
92
+ delegation := newDelegationHistory(to, amount, timeStamp, uint64(std.GetHeight()), true)
51
93
history = append(history, delegation)
52
- delegationHistory.Set(caller, history)
94
+ s. delegationHistory.Set(caller, history)
53
95
54
- // update delegation stat history
55
- updateAmount := uint64(0)
56
- snapShotHistory := make( []DelegationSnapShotHistory, 0)
57
- if value, exists := delegationSnapShotHistory .Get(toStr); exists {
96
+ // Update snapshot history.
97
+ updateAmount := amount
98
+ snapShotHistory := []DelegationSnapShotHistory{}
99
+ if value, exists := s.DelegationSnapShotHistory() .Get(toStr); exists {
58
100
snapShotHistory = value.([]DelegationSnapShotHistory)
59
101
lastStat := snapShotHistory[len(snapShotHistory)-1]
60
102
updateAmount = lastStat.amount + amount
61
- } else {
62
- updateAmount = amount
63
103
}
104
+ snapShot := newDelegationSnapShotHistory(to, updateAmount, uint64(std.GetHeight()), timeStamp)
105
+ snapShotHistory = append(snapShotHistory, snapShot)
106
+ s.delegationSnapShotHistory.Set(toStr, snapShotHistory)
107
+ }
64
108
65
- snapShotHistory = append(snapShotHistory, DelegationSnapShotHistory{
66
- to: to,
67
- amount: updateAmount,
68
- updatedBlock: uint64(std.GetHeight()),
69
- updatedAt: timeStamp,
70
- })
71
- delegationSnapShotHistory.Set(toStr, snapShotHistory)
109
+ // getUint64OrPanic retrieves a uint64 value from the tree by key,
110
+ // panicking with an error message if the key does not exist.
111
+ func (s *stakerState) getUint64OrPanic(tree *avl.Tree, key, errMsg string) uint64 {
112
+ if value, exists := tree.Get(key); exists {
113
+ return value.(uint64)
114
+ }
115
+ panic(addDetailToError(
116
+ errDataNotFound,
117
+ ufmt.Sprintf("%s: %s", key, errMsg),
118
+ ))
72
119
}
73
120
74
- func undelegate(to std.Address, amount uint64) {
121
+ // Undelegate processes an undelegation from the caller for the given address.
122
+ func (s *stakerState) Undelegate(to std.Address, amount uint64) {
75
123
caller := std.PrevRealm().Addr().String()
76
124
toStr := to.String()
77
125
78
- // check caller's delegatedFromTo
79
- innerTree, exists := delegatedFromTo.Get(caller)
126
+ innerTreeValue, exists := s.DelegatedFromTo().Get(caller)
80
127
if !exists {
81
128
panic(addDetailToError(
82
129
errNoDelegatedAmount,
83
130
ufmt.Sprintf("caller(%s) has no delegated amount", caller),
84
131
))
85
132
}
86
- // check caller's delegatedFromTo's inner tree
87
- delegatedAmountValue, exists := innerTree.(*avl.Tree).Get(toStr)
133
+ innerTree := innerTreeValue.(*avl.Tree)
134
+
135
+ delegatedAmountValue, exists := innerTree.Get(toStr)
88
136
if !exists {
89
137
panic(addDetailToError(
90
138
errNoDelegatedTarget,
@@ -95,86 +143,74 @@ func undelegate(to std.Address, amount uint64) {
95
143
if delegatedAmount < amount {
96
144
panic(addDetailToError(
97
145
errNotEnoughDelegated,
98
- ufmt.Sprintf("caller(%s) has only %d delegated amount(request : %d) to %s", caller, delegatedAmount, amount, to),
146
+ ufmt.Sprintf("caller(%s) has only %d delegated amount (requested : %d) to %s", caller, delegatedAmount, amount, to),
99
147
))
100
148
}
101
- innerTree.(*avl.Tree). Set(toStr, delegatedAmount-amount)
102
- delegatedFromTo .Set(caller, innerTree)
149
+ innerTree.Set(toStr, delegatedAmount-amount)
150
+ s.DelegatedFromTo() .Set(caller, innerTree)
103
151
104
- // update total delegated amount
105
- totalDelegated -= amount
152
+ s.totalDelegated -= amount
106
153
107
- currentAmount := uint64(0)
108
- if value, exists := delegatorAmount.Get(caller); exists {
109
- currentAmount = value.(uint64)
110
- } else {
111
- panic(addDetailToError(
112
- errDataNotFound,
113
- ufmt.Sprintf("caller(%s) has no delegated amount", caller),
114
- ))
115
- }
116
- if currentAmount < amount {
154
+ currentDelegatorAmount := s.getUint64OrPanic(s.delegatorAmount, caller, "has no delegated amount")
155
+ if currentDelegatorAmount < amount {
117
156
panic(addDetailToError(
118
157
errNotEnoughDelegated,
119
- ufmt.Sprintf("caller(%s) has only %d delegated amount(request : %d)", caller, currentAmount , amount),
158
+ ufmt.Sprintf("caller(%s) has only %d delegated amount (requested : %d)", caller, currentDelegatorAmount , amount),
120
159
))
121
160
}
122
- delegatorAmount.Set(caller, currentAmount -amount)
161
+ s. delegatorAmount.Set(caller, currentDelegatorAmount -amount)
123
162
124
- currentToAmount := uint64(0)
125
- if value, exists := delegatedTo.Get(toStr); exists {
126
- currentToAmount = value.(uint64)
127
- } else {
128
- panic(addDetailToError(
129
- errDataNotFound,
130
- ufmt.Sprintf("to(%s) has no delegated amount", toStr),
131
- ))
132
- }
133
- if currentToAmount < amount {
163
+ currentDelegatedTo := s.getUint64OrPanic(s.delegatedTo, toStr, "target has no delegated amount")
164
+ if currentDelegatedTo < amount {
134
165
panic(addDetailToError(
135
166
errNotEnoughDelegated,
136
- ufmt.Sprintf("to(%s) has only %d delegated amount(request : %d)", toStr, currentToAmount , amount),
167
+ ufmt.Sprintf("to(%s) has only %d delegated amount (requested : %d)", toStr, currentDelegatedTo , amount),
137
168
))
138
169
}
139
- delegatedTo.Set(toStr, currentToAmount-amount)
140
-
141
- // update delegation history
142
- delegation := DelegationHistory{
143
- to: to,
144
- amount: amount,
145
- timestamp: uint64(time.Now().Unix()),
146
- height: uint64(std.GetHeight()),
147
- add: false,
148
- }
149
- var history []DelegationHistory
150
- if value, exists := delegationHistory.Get(caller); exists {
170
+ s.delegatedTo.Set(toStr, currentDelegatedTo-amount)
171
+
172
+ // Update delegation history.
173
+ history := []DelegationHistory{}
174
+ if value, exists := s.delegationHistory.Get(caller); exists {
151
175
history = value.([]DelegationHistory)
152
176
}
177
+ delegation := newDelegationHistory(to, amount, uint64(time.Now().Unix()), uint64(std.GetHeight()), false)
153
178
history = append(history, delegation)
154
- delegationHistory.Set(caller, history)
179
+ s. delegationHistory.Set(caller, history)
155
180
156
- // update delegation stat history
157
- statValue, exists := delegationSnapShotHistory .Get(toStr)
181
+ // Update snapshot history.
182
+ statValue, exists := s.DelegationSnapShotHistory() .Get(toStr)
158
183
if !exists {
159
184
panic(addDetailToError(
160
185
errDataNotFound,
161
186
ufmt.Sprintf("caller(%s) has no delegation stat history", caller),
162
187
))
163
188
}
164
-
165
- stat := statValue.([]DelegationSnapShotHistory)
189
+ stats := statValue.([]DelegationSnapShotHistory)
166
190
remainingAmount := amount
167
- for i := 0; i < len(stat); i++ {
168
- if stat[i].amount > 0 {
169
- if stat[i].amount < remainingAmount {
170
- remainingAmount -= stat[i].amount
171
- stat = append(stat[:i], stat[i+1:]...)
191
+ for i := 0; i < len(stats); i++ {
192
+ if stats[i].amount > 0 {
193
+ if stats[i].amount < remainingAmount {
194
+ remainingAmount -= stats[i].amount
195
+ stats = append(stats[:i], stats[i+1:]...)
196
+ i--
172
197
} else {
173
- stat[i].amount -= remainingAmount
174
- stat[i].updatedAt = uint64(time.Now().Unix())
198
+ stats[i].amount -= remainingAmount
199
+ stats[i].updatedAt = uint64(time.Now().Unix())
200
+ remainingAmount = 0
175
201
break
176
202
}
177
203
}
178
204
}
179
- delegationSnapShotHistory.Set(to.String(), stat)
205
+ s.delegationSnapShotHistory.Set(toStr, stats)
206
+ }
207
+
208
+ // delegate is the external API for delegation.
209
+ func delegate(to std.Address, amount uint64) {
210
+ state.Delegate(to, amount)
211
+ }
212
+
213
+ // undelegate is the external API for undelegation.
214
+ func undelegate(to std.Address, amount uint64) {
215
+ state.Undelegate(to, amount)
180
216
}
0 commit comments