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