Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}