protocol_fee_unstaking.gno
3.66 Kb · 134 lines
1package staker
2
3import (
4 "chain"
5 "errors"
6
7 "gno.land/p/gnoswap/gnsmath"
8 prbac "gno.land/p/gnoswap/rbac"
9 "gno.land/p/gnoswap/utils"
10 "gno.land/r/gnoswap/access"
11 "gno.land/r/gnoswap/common"
12 "gno.land/r/gnoswap/halt"
13
14 pf "gno.land/r/gnoswap/protocol_fee"
15)
16
17// GetUnstakingFee returns the current unstaking fee rate in basis points.
18func (s *stakerV1) GetUnstakingFee() uint64 { return s.store.GetUnstakingFee() }
19
20// handleStakingRewardFee calculates and applies the unstaking fee.
21func (s *stakerV1) handleStakingRewardFee(
22 _ int,
23 rlm realm,
24 tokenPath string,
25 amount int64,
26 internal bool,
27) (int64, int64, error) {
28 unstakingFee := s.GetUnstakingFee()
29 if unstakingFee == 0 {
30 s.addToProtocolFee(0, rlm)
31 return amount, 0, nil
32 }
33
34 // Do not change the order of the operation.
35 feeAmount := gnsmath.SafeMulDivInt64(amount, gnsmath.SafeUint64ToInt64(unstakingFee), 10000)
36 if feeAmount < 0 {
37 return 0, 0, errors.New("fee amount cannot be negative")
38 }
39
40 if feeAmount == 0 {
41 s.addToProtocolFee(0, rlm)
42 return amount, 0, nil
43 }
44
45 if internal {
46 tokenPath = GNS_TOKEN_KEY
47 }
48
49 s.addPendingProtocolFee(0, rlm, tokenPath, feeAmount)
50 s.addToProtocolFee(0, rlm)
51
52 return gnsmath.SafeSubInt64(amount, feeAmount), feeAmount, nil
53}
54
55func (s *stakerV1) addPendingProtocolFee(_ int, rlm realm, tokenPath string, amount int64) {
56 if amount <= 0 {
57 return
58 }
59
60 existingProtocolFees := s.store.GetPendingProtocolFees()
61 pendingProtocolFees := make(map[string]int64)
62 for pendingTokenPath, pendingAmount := range existingProtocolFees {
63 pendingProtocolFees[pendingTokenPath] = pendingAmount
64 }
65 pendingProtocolFees[tokenPath] = gnsmath.SafeAddInt64(pendingProtocolFees[tokenPath], amount)
66 if err := s.store.SetPendingProtocolFees(0, rlm, pendingProtocolFees); err != nil {
67 panic(err)
68 }
69}
70
71// addToProtocolFee sends pending staker protocol fees to the protocol fee realm.
72// It is best-effort: when protocol fee collection is halted, it keeps pending
73// storage unchanged; otherwise it rebuilds pending storage with only entries
74// that could not be added, removing successful and non-positive entries.
75func (s *stakerV1) addToProtocolFee(_ int, rlm realm) {
76 pendingProtocolFees := s.store.GetPendingProtocolFees()
77 if len(pendingProtocolFees) == 0 || halt.IsHaltedProtocolFee() {
78 return
79 }
80
81 protocolFeeAddr := access.MustGetAddress(prbac.ROLE_PROTOCOL_FEE.String())
82 remainingProtocolFees := make(map[string]int64)
83 for tokenPath, amount := range pendingProtocolFees {
84 if amount <= 0 {
85 continue
86 }
87
88 common.SafeGRC20Approve(cross(rlm), tokenPath, protocolFeeAddr, amount)
89 if err := pf.AddToProtocolFee(cross(rlm), tokenPath, amount); err != nil {
90 remainingProtocolFees[tokenPath] = amount
91 continue
92 }
93 }
94
95 if err := s.store.SetPendingProtocolFees(0, rlm, remainingProtocolFees); err != nil {
96 panic(err)
97 }
98}
99
100// SetUnStakingFee sets the unstaking fee rate in basis points.
101// Only admin or governance can call this function.
102func (s *stakerV1) SetUnStakingFee(_ int, rlm realm, fee uint64) {
103 if !rlm.IsCurrent() {
104 panic(errors.New(errSpoofedRealm))
105 }
106
107 halt.AssertIsNotHaltedStaker()
108
109 previousRealm := rlm.Previous()
110 caller := previousRealm.Address()
111 access.AssertIsAdminOrGovernance(caller)
112
113 assertIsValidFeeRate(fee)
114
115 prevUnStakingFee := s.GetUnstakingFee()
116
117 err := s.setUnStakingFee(0, rlm, fee)
118 if err != nil {
119 panic(err)
120 }
121
122 chain.Emit(
123 "SetUnStakingFee",
124 "prevAddr", caller.String(),
125 "prevRealm", previousRealm.PkgPath(),
126 "prevFee", utils.FormatUint(prevUnStakingFee),
127 "newFee", utils.FormatUint(fee),
128 )
129}
130
131// setUnStakingFee internally updates the unstaking fee.
132func (s *stakerV1) setUnStakingFee(_ int, rlm realm, fee uint64) error {
133 return s.store.SetUnstakingFee(0, rlm, fee)
134}