protocol_fee.gno
6.03 Kb · 227 lines
1package v1
2
3import (
4 "chain"
5 "strconv"
6
7 ufmt "gno.land/p/nt/ufmt/v0"
8
9 prabc "gno.land/p/gnoswap/rbac"
10 "gno.land/r/gnoswap/access"
11 "gno.land/r/gnoswap/common"
12 "gno.land/r/gnoswap/halt"
13)
14
15// DistributeProtocolFee distributes collected protocol fees.
16//
17// Splits fees between devOps and gov/staker based on configured percentages.
18// This function processes all accumulated fees since last distribution.
19//
20// Only callable by admin or gov/staker contract.
21// Note: Default split is 0% devOps, 100% gov/staker.
22func (pf *protocolFeeV1) DistributeProtocolFee(_ int, rlm realm) {
23 if !rlm.IsCurrent() {
24 panic(errSpoofedRealm)
25 }
26
27 prev := rlm.Previous()
28 assertIsAdminOrGovStaker(prev.Address())
29
30 if halt.IsHaltedWithdraw() {
31 return
32 }
33
34 protocolFeeAddr := access.MustGetAddress(prabc.ROLE_PROTOCOL_FEE.String())
35 pfs := pf.getProtocolFeeState()
36
37 reservedTokens := pfs.ReservedTokens()
38
39 for _, token := range reservedTokens {
40 toDevOpsAmount := safeSubInt64(
41 pfs.GetAccuTransferToDevOpsByTokenPath(token),
42 pfs.GetActualDistributedToDevOpsByTokenPath(token),
43 )
44 toGovStakerAmount := safeSubInt64(
45 pfs.GetAccuTransferToGovStakerByTokenPath(token),
46 pfs.GetActualDistributedToGovStakerByTokenPath(token),
47 )
48
49 amount := safeAddInt64(toDevOpsAmount, toGovStakerAmount)
50 balance := common.BalanceOf(token, protocolFeeAddr)
51
52 // amount should be less than or equal to balance
53 if amount > balance {
54 panic(makeErrorWithDetail(
55 errInvalidAmount,
56 ufmt.Sprintf("amount: %d should be less than or equal to balance: %d", amount, balance),
57 ))
58 }
59
60 if amount <= 0 {
61 continue
62 }
63
64 // Distribute to DevOps
65 if err := pfs.distributeToDevOps(0, rlm, token, toDevOpsAmount); err != nil {
66 panic(err)
67 }
68
69 // Distribute to Gov/Staker
70 if err := pfs.distributeToGovStaker(0, rlm, token, toGovStakerAmount); err != nil {
71 panic(err)
72 }
73 }
74 if err := pfs.clearReservedTokens(0, rlm); err != nil {
75 panic(err)
76 }
77
78 chain.Emit(
79 "TransferProtocolFee",
80 "prevAddr", prev.Address().String(),
81 "prevRealm", prev.PkgPath(),
82 )
83}
84
85// SetDevOpsPct sets the devOpsPct.
86//
87// Parameters:
88// - pct: percentage for devOps (0-10000, where 10000 = 100%)
89//
90// Only callable by admin or governance.
91// Note: GovStaker percentage is automatically adjusted to (10000 - devOpsPct).
92func (pf *protocolFeeV1) SetDevOpsPct(_ int, rlm realm, pct int64) {
93 if !rlm.IsCurrent() {
94 panic(errSpoofedRealm)
95 }
96
97 halt.AssertIsNotHaltedProtocolFee()
98
99 prev := rlm.Previous()
100 access.AssertIsAdminOrGovernance(prev.Address())
101
102 assertIsValidPercent(pct)
103
104 prevDevOpsPct := pf.getProtocolFeeState().DevOpsPct()
105 prevGovStakerPct := pf.getProtocolFeeState().GovStakerPct()
106
107 newDevOpsPct, err := pf.getProtocolFeeState().setDevOpsPct(0, rlm, pct)
108 if err != nil {
109 panic(err)
110 }
111 newGovStakerPct := pf.getProtocolFeeState().GovStakerPct()
112
113 chain.Emit(
114 "SetDevOpsPct",
115 "prevAddr", prev.Address().String(),
116 "prevRealm", prev.PkgPath(),
117 "newDevOpsPct", strconv.FormatInt(newDevOpsPct, 10),
118 "prevDevOpsPct", strconv.FormatInt(prevDevOpsPct, 10),
119 "newGovStakerPct", strconv.FormatInt(newGovStakerPct, 10),
120 "prevGovStakerPct", strconv.FormatInt(prevGovStakerPct, 10),
121 )
122}
123
124// SetGovStakerPct sets the stakerPct.
125//
126// Parameters:
127// - pct: percentage for gov/staker (0-10000, where 10000 = 100%)
128//
129// Only callable by admin or governance.
130// Note: DevOps percentage is automatically adjusted to (10000 - govStakerPct).
131func (pf *protocolFeeV1) SetGovStakerPct(_ int, rlm realm, pct int64) {
132 if !rlm.IsCurrent() {
133 panic(errSpoofedRealm)
134 }
135
136 halt.AssertIsNotHaltedProtocolFee()
137
138 prev := rlm.Previous()
139 access.AssertIsAdminOrGovernance(prev.Address())
140
141 assertIsValidPercent(pct)
142
143 prevDevOpsPct := pf.getProtocolFeeState().DevOpsPct()
144 prevGovStakerPct := pf.getProtocolFeeState().GovStakerPct()
145
146 newGovStakerPct, err := pf.getProtocolFeeState().setGovStakerPct(0, rlm, pct)
147 if err != nil {
148 panic(err)
149 }
150 newDevOpsPct := pf.getProtocolFeeState().DevOpsPct()
151
152 chain.Emit(
153 "SetGovStakerPct",
154 "prevAddr", prev.Address().String(),
155 "prevRealm", prev.PkgPath(),
156 "newDevOpsPct", strconv.FormatInt(newDevOpsPct, 10),
157 "prevDevOpsPct", strconv.FormatInt(prevDevOpsPct, 10),
158 "newGovStakerPct", strconv.FormatInt(newGovStakerPct, 10),
159 "prevGovStakerPct", strconv.FormatInt(prevGovStakerPct, 10),
160 )
161}
162
163// AddToProtocolFee pulls the approved amount into protocol fee accounting.
164//
165// Parameters:
166// - tokenPath: token contract path
167// - amount: fee amount to add
168//
169// Only callable by pool, router or staker contracts.
170// Caller must approve the protocol fee realm for at least amount before calling.
171// Note: Accumulated fees are distributed when DistributeProtocolFee is called.
172func (pf *protocolFeeV1) AddToProtocolFee(_ int, rlm realm, tokenPath string, amount int64) error {
173 if !rlm.IsCurrent() {
174 return errSpoofedRealm
175 }
176
177 if halt.IsHaltedProtocolFee() {
178 return errProtocolFeeHalted
179 }
180
181 prev := rlm.Previous()
182 caller := prev.Address()
183 assertIsPoolOrPositionOrRouterOrStaker(caller)
184
185 if amount < 0 {
186 panic(makeErrorWithDetail(
187 errInvalidAmount,
188 ufmt.Sprintf("amount(%d) should not be negative", amount),
189 ))
190 }
191
192 if amount == 0 {
193 return nil
194 }
195
196 pf.reserveCollectedProtocolFee(0, rlm, tokenPath, amount)
197 protocolFeeAddr := access.MustGetAddress(prabc.ROLE_PROTOCOL_FEE.String())
198 common.SafeGRC20TransferFrom(cross(rlm), tokenPath, caller, protocolFeeAddr, amount)
199
200 chain.Emit(
201 "AddToProtocolFee",
202 "prevAddr", caller.String(),
203 "prevRealm", prev.PkgPath(),
204 "tokenPath", tokenPath,
205 "amount", strconv.FormatInt(amount, 10),
206 )
207
208 return nil
209}
210
211func (pf *protocolFeeV1) reserveCollectedProtocolFee(_ int, rlm realm, tokenPath string, amount int64) {
212 pfs := pf.getProtocolFeeState()
213
214 toDevOpsAmount := safeMulDiv(amount, pfs.DevOpsPct(), 10000)
215 toGovStakerAmount := safeSubInt64(amount, toDevOpsAmount)
216
217 if err := pfs.addAccuToDevOps(0, rlm, tokenPath, toDevOpsAmount); err != nil {
218 panic(err)
219 }
220 if err := pfs.addAccuToGovStaker(0, rlm, tokenPath, toGovStakerAmount); err != nil {
221 panic(err)
222 }
223
224 if err := pfs.store.AddReservedToken(0, rlm, tokenPath); err != nil {
225 panic(err)
226 }
227}