Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

protocol_fee.gno

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