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

6.42 Kb · 234 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		toDevOpsAmount := gnsmath.SafeSubInt64(
 43			pfs.GetAccuTransferToDevOpsByTokenPath(token),
 44			pfs.GetActualDistributedToDevOpsByTokenPath(token),
 45		)
 46		toGovStakerAmount := gnsmath.SafeSubInt64(
 47			pfs.GetAccuTransferToGovStakerByTokenPath(token),
 48			pfs.GetActualDistributedToGovStakerByTokenPath(token),
 49		)
 50
 51		amount := gnsmath.SafeAddInt64(toDevOpsAmount, toGovStakerAmount)
 52		balance := common.BalanceOf(token, protocolFeeAddr)
 53
 54		// amount should be less than or equal to balance
 55		if amount > balance {
 56			panic(makeErrorWithDetail(
 57				errInvalidAmount,
 58				ufmt.Sprintf("amount: %d should be less than or equal to balance: %d", amount, balance),
 59			))
 60		}
 61
 62		if amount <= 0 {
 63			continue
 64		}
 65
 66		// Distribute to DevOps
 67		if err := pfs.distributeToDevOps(0, rlm, token, toDevOpsAmount); err != nil {
 68			panic(err)
 69		}
 70
 71		// Distribute to Gov/Staker
 72		if err := pfs.distributeToGovStaker(0, rlm, token, toGovStakerAmount); err != nil {
 73			panic(err)
 74		}
 75	}
 76	if err := pfs.clearReservedTokens(0, rlm); err != nil {
 77		panic(err)
 78	}
 79
 80	chain.Emit(
 81		"TransferProtocolFee",
 82		"prevAddr", prev.Address().String(),
 83		"prevRealm", prev.PkgPath(),
 84	)
 85}
 86
 87// DistributeProtocolFeeByTokenPath is retained for ABI compatibility while token-path distribution is deferred.
 88func (pf *protocolFeeV1) DistributeProtocolFeeByTokenPath(_ int, rlm realm, tokenPath string) {
 89	assertNotImplementYet()
 90}
 91
 92// SetDevOpsPct sets the devOpsPct.
 93//
 94// Parameters:
 95//   - pct: percentage for devOps (0-10000, where 10000 = 100%)
 96//
 97// Only callable by admin or governance.
 98// Note: GovStaker percentage is automatically adjusted to (10000 - devOpsPct).
 99func (pf *protocolFeeV1) SetDevOpsPct(_ int, rlm realm, pct int64) {
100	if !rlm.IsCurrent() {
101		panic(errors.New(errSpoofedRealm))
102	}
103
104	halt.AssertIsNotHaltedProtocolFee()
105
106	prev := rlm.Previous()
107	access.AssertIsAdminOrGovernance(prev.Address())
108
109	assertIsValidPercent(pct)
110
111	prevDevOpsPct := pf.getProtocolFeeState().DevOpsPct()
112	prevGovStakerPct := pf.getProtocolFeeState().GovStakerPct()
113
114	newDevOpsPct, err := pf.getProtocolFeeState().setDevOpsPct(0, rlm, pct)
115	if err != nil {
116		panic(err)
117	}
118	newGovStakerPct := pf.getProtocolFeeState().GovStakerPct()
119
120	chain.Emit(
121		"SetDevOpsPct",
122		"prevAddr", prev.Address().String(),
123		"prevRealm", prev.PkgPath(),
124		"newDevOpsPct", strconv.FormatInt(newDevOpsPct, 10),
125		"prevDevOpsPct", strconv.FormatInt(prevDevOpsPct, 10),
126		"newGovStakerPct", strconv.FormatInt(newGovStakerPct, 10),
127		"prevGovStakerPct", strconv.FormatInt(prevGovStakerPct, 10),
128	)
129}
130
131// SetGovStakerPct sets the stakerPct.
132//
133// Parameters:
134//   - pct: percentage for gov/staker (0-10000, where 10000 = 100%)
135//
136// Only callable by admin or governance.
137// Note: DevOps percentage is automatically adjusted to (10000 - govStakerPct).
138func (pf *protocolFeeV1) SetGovStakerPct(_ int, rlm realm, pct int64) {
139	if !rlm.IsCurrent() {
140		panic(errors.New(errSpoofedRealm))
141	}
142
143	halt.AssertIsNotHaltedProtocolFee()
144
145	prev := rlm.Previous()
146	access.AssertIsAdminOrGovernance(prev.Address())
147
148	assertIsValidPercent(pct)
149
150	prevDevOpsPct := pf.getProtocolFeeState().DevOpsPct()
151	prevGovStakerPct := pf.getProtocolFeeState().GovStakerPct()
152
153	newGovStakerPct, err := pf.getProtocolFeeState().setGovStakerPct(0, rlm, pct)
154	if err != nil {
155		panic(err)
156	}
157	newDevOpsPct := pf.getProtocolFeeState().DevOpsPct()
158
159	chain.Emit(
160		"SetGovStakerPct",
161		"prevAddr", prev.Address().String(),
162		"prevRealm", prev.PkgPath(),
163		"newDevOpsPct", strconv.FormatInt(newDevOpsPct, 10),
164		"prevDevOpsPct", strconv.FormatInt(prevDevOpsPct, 10),
165		"newGovStakerPct", strconv.FormatInt(newGovStakerPct, 10),
166		"prevGovStakerPct", strconv.FormatInt(prevGovStakerPct, 10),
167	)
168}
169
170// AddToProtocolFee pulls the approved amount into protocol fee accounting.
171//
172// Parameters:
173//   - tokenPath: token contract path
174//   - amount: fee amount to add
175//
176// Only callable by pool, router or staker contracts.
177// Caller must approve the protocol fee realm for at least amount before calling.
178// Note: Accumulated fees are distributed when DistributeProtocolFee is called.
179func (pf *protocolFeeV1) AddToProtocolFee(_ int, rlm realm, tokenPath string, amount int64) error {
180	if !rlm.IsCurrent() {
181		return errors.New(errSpoofedRealm)
182	}
183
184	if halt.IsHaltedProtocolFee() {
185		return errors.New(errProtocolFeeHalted)
186	}
187
188	prev := rlm.Previous()
189	caller := prev.Address()
190	assertIsPoolOrPositionOrRouterOrStaker(caller)
191
192	if amount < 0 {
193		panic(makeErrorWithDetail(
194			errInvalidAmount,
195			ufmt.Sprintf("amount(%d) should not be negative", amount),
196		))
197	}
198
199	if amount == 0 {
200		return nil
201	}
202
203	pf.reserveCollectedProtocolFee(0, rlm, tokenPath, amount)
204	protocolFeeAddr := access.MustGetAddress(prabc.ROLE_PROTOCOL_FEE.String())
205	common.SafeGRC20TransferFrom(cross(rlm), tokenPath, caller, protocolFeeAddr, amount)
206
207	chain.Emit(
208		"AddToProtocolFee",
209		"prevAddr", caller.String(),
210		"prevRealm", prev.PkgPath(),
211		"tokenPath", tokenPath,
212		"amount", strconv.FormatInt(amount, 10),
213	)
214
215	return nil
216}
217
218func (pf *protocolFeeV1) reserveCollectedProtocolFee(_ int, rlm realm, tokenPath string, amount int64) {
219	pfs := pf.getProtocolFeeState()
220
221	toDevOpsAmount := gnsmath.SafeMulDivInt64(amount, pfs.DevOpsPct(), 10000)
222	toGovStakerAmount := gnsmath.SafeSubInt64(amount, toDevOpsAmount)
223
224	if err := pfs.addAccuToDevOps(0, rlm, tokenPath, toDevOpsAmount); err != nil {
225		panic(err)
226	}
227	if err := pfs.addAccuToGovStaker(0, rlm, tokenPath, toGovStakerAmount); err != nil {
228		panic(err)
229	}
230
231	if err := pfs.store.AddReservedToken(0, rlm, tokenPath); err != nil {
232		panic(err)
233	}
234}