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.74 Kb · 243 lines
  1package pool
  2
  3import (
  4	"chain"
  5	"errors"
  6
  7	"gno.land/p/gnoswap/gnsmath"
  8	prabc "gno.land/p/gnoswap/rbac"
  9	u256 "gno.land/p/gnoswap/uint256"
 10	"gno.land/p/gnoswap/utils"
 11	ufmt "gno.land/p/nt/ufmt/v0"
 12
 13	"gno.land/r/gnoswap/access"
 14	"gno.land/r/gnoswap/common"
 15	"gno.land/r/gnoswap/halt"
 16	pf "gno.land/r/gnoswap/protocol_fee"
 17)
 18
 19const (
 20	MaxBpsValue = uint64(1000) // 10%
 21	ZeroBps     = uint64(0)
 22)
 23
 24// HandleWithdrawalFee settles collected fees from the pool and returns the amount after the fee.
 25// Only position contract can call this function
 26// Input:
 27// - token0Path: the path of the token0
 28// - amount0: the amount of token0
 29// - token1Path: the path of the token1
 30// - amount1: the amount of token1
 31// - positionCaller: the original caller of the position contract
 32// Output:
 33// - the fee amount of token0
 34// - the fee amount of token1
 35// - the amount of token0 after the fee
 36// - the amount of token1 after the fee
 37func (i *poolV1) HandleWithdrawalFee(
 38	_ int,
 39	rlm realm,
 40	token0Path string,
 41	amount0 string, // uint256
 42	token1Path string,
 43	amount1 string, // uint256
 44	positionCaller address,
 45) (string, string, string, string) {
 46	if !rlm.IsCurrent() {
 47		panic(errors.New(errSpoofedRealm))
 48	}
 49
 50	i.assertPoolUnlocked()
 51	halt.AssertIsNotHaltedWithdraw()
 52
 53	// only position contract can call this function
 54	previousRealm := rlm.Previous()
 55	caller := previousRealm.Address()
 56	access.AssertIsPosition(caller)
 57
 58	common.MustRegistered(token0Path, token1Path)
 59
 60	i.lockPool(0, rlm)
 61	defer i.unlockPool(0, rlm)
 62
 63	fee := u256.NewUint(i.store.GetWithdrawalFeeBPS())
 64	fee0, amount0WithoutFee := calculateAmountWithFee(u256.MustFromDecimal(amount0), fee)
 65	fee1, amount1WithoutFee := calculateAmountWithFee(u256.MustFromDecimal(amount1), fee)
 66
 67	fee0Int64 := gnsmath.SafeConvertToInt64(fee0)
 68	fee1Int64 := gnsmath.SafeConvertToInt64(fee1)
 69
 70	i.addPendingProtocolFee(0, rlm, token0Path, fee0Int64)
 71	i.addPendingProtocolFee(0, rlm, token1Path, fee1Int64)
 72	i.addToProtocolFee(0, rlm)
 73	common.SafeGRC20Transfer(cross(rlm), token0Path, positionCaller, gnsmath.SafeConvertToInt64(amount0WithoutFee))
 74	common.SafeGRC20Transfer(cross(rlm), token1Path, positionCaller, gnsmath.SafeConvertToInt64(amount1WithoutFee))
 75
 76	return fee0.ToString(), fee1.ToString(), amount0WithoutFee.ToString(), amount1WithoutFee.ToString()
 77}
 78
 79func (i *poolV1) addPendingProtocolFee(_ int, rlm realm, tokenPath string, amount int64) {
 80	if amount <= 0 {
 81		return
 82	}
 83
 84	existingProtocolFees := i.store.GetPendingProtocolFees()
 85	pendingProtocolFees := make(map[string]int64)
 86	for pendingTokenPath, pendingAmount := range existingProtocolFees {
 87		pendingProtocolFees[pendingTokenPath] = pendingAmount
 88	}
 89	pendingProtocolFees[tokenPath] = gnsmath.SafeAddInt64(pendingProtocolFees[tokenPath], amount)
 90	if err := i.store.SetPendingProtocolFees(0, rlm, pendingProtocolFees); err != nil {
 91		panic(err)
 92	}
 93}
 94
 95// addToProtocolFee sends pending pool protocol fees to the protocol fee realm.
 96// It is best-effort: when protocol fee collection is halted, it keeps pending
 97// storage unchanged; otherwise it rebuilds pending storage with only entries
 98// that could not be added, removing successful and non-positive entries.
 99func (i *poolV1) addToProtocolFee(_ int, rlm realm) {
100	pendingProtocolFees := i.store.GetPendingProtocolFees()
101	if len(pendingProtocolFees) == 0 || halt.IsHaltedProtocolFee() {
102		return
103	}
104
105	protocolFeeAddr := access.MustGetAddress(prabc.ROLE_PROTOCOL_FEE.String())
106	remainingProtocolFees := make(map[string]int64)
107	for tokenPath, amount := range pendingProtocolFees {
108		if amount <= 0 {
109			continue
110		}
111
112		common.SafeGRC20Approve(cross(rlm), tokenPath, protocolFeeAddr, amount)
113		if err := pf.AddToProtocolFee(cross(rlm), tokenPath, amount); err != nil {
114			remainingProtocolFees[tokenPath] = amount
115			continue
116		}
117	}
118
119	if err := i.store.SetPendingProtocolFees(0, rlm, remainingProtocolFees); err != nil {
120		panic(err)
121	}
122}
123
124// SetPoolCreationFee sets the poolCreationFee.
125// Only admin or governance can call this function.
126func (i *poolV1) SetPoolCreationFee(_ int, rlm realm, fee int64) {
127	if !rlm.IsCurrent() {
128		panic(errors.New(errSpoofedRealm))
129	}
130
131	i.assertPoolUnlocked()
132	halt.AssertIsNotHaltedPool()
133
134	previousRealm := rlm.Previous()
135	caller := previousRealm.Address()
136	access.AssertIsAdminOrGovernance(caller)
137
138	i.lockPool(0, rlm)
139	defer i.unlockPool(0, rlm)
140
141	prevPoolCreationFee := i.store.GetPoolCreationFee()
142	err := i.setPoolCreationFee(0, rlm, fee)
143	if err != nil {
144		panic(err)
145	}
146
147	chain.Emit(
148		"SetPoolCreationFee",
149		"prevAddr", caller.String(),
150		"prevRealm", previousRealm.PkgPath(),
151		"prevFee", utils.FormatInt(prevPoolCreationFee),
152		"newFee", utils.FormatInt(fee),
153	)
154}
155
156// SetWithdrawalFee sets the withdrawal fee.
157// Only admin or governance can call this function.
158func (i *poolV1) SetWithdrawalFee(_ int, rlm realm, fee uint64) {
159	if !rlm.IsCurrent() {
160		panic(errors.New(errSpoofedRealm))
161	}
162
163	i.assertPoolUnlocked()
164	halt.AssertIsNotHaltedPool()
165
166	previousRealm := rlm.Previous()
167	caller := previousRealm.Address()
168	access.AssertIsAdminOrGovernance(caller)
169
170	i.lockPool(0, rlm)
171	defer i.unlockPool(0, rlm)
172
173	prevWithdrawalFee := i.store.GetWithdrawalFeeBPS()
174
175	err := i.setWithdrawalFee(0, rlm, fee)
176	if err != nil {
177		panic(err)
178	}
179
180	chain.Emit(
181		"SetWithdrawalFee",
182		"prevAddr", caller.String(),
183		"prevRealm", previousRealm.PkgPath(),
184		"prevFee", utils.FormatUint(prevWithdrawalFee),
185		"newFee", utils.FormatUint(fee),
186	)
187}
188
189// calculateAmountWithFee calculates the fee amount and the amount after the fee
190//
191// Inputs:
192// - amount: the amount before the fee
193// - fee: the fee in BPS
194//
195// Outputs:
196// - the fee amount
197// - the amount after the fee applied
198func calculateAmountWithFee(amount, fee *u256.Uint) (feeAmount, afterAmount *u256.Uint) {
199	feeAmount, overflow := u256.Zero().MulOverflow(amount, fee)
200	if overflow {
201		panic(errors.New(errOverflow))
202	}
203	feeAmount = u256.Zero().Div(feeAmount, u256.NewUint(10_000))
204	afterAmount = u256.Zero().Sub(amount, feeAmount)
205	return feeAmount, afterAmount
206}
207
208// setPoolCreationFee this function is internal function called by SetPoolCreationFee
209// And SetPoolCreationFee
210func (i *poolV1) setPoolCreationFee(_ int, rlm realm, fee int64) error {
211	if fee < 0 {
212		return makeErrorWithDetails(
213			errInvalidInput,
214			"pool creation fee cannot be negative",
215		)
216	}
217
218	// update pool creation fee
219	err := i.store.SetPoolCreationFee(0, rlm, fee)
220	if err != nil {
221		return err
222	}
223
224	return nil
225}
226
227// setWithdrawalFee this function is internal function called by SetWithdrawalFee
228// function and SetWithdrawalFee function
229func (i *poolV1) setWithdrawalFee(_ int, rlm realm, fee uint64) error {
230	if fee > MaxBpsValue {
231		return makeErrorWithDetails(
232			errInvalidWithdrawalFeePct,
233			ufmt.Sprintf("fee(%d) must be in range 0 ~ %d", fee, MaxBpsValue),
234		)
235	}
236
237	err := i.store.SetWithdrawalFeeBPS(0, rlm, fee)
238	if err != nil {
239		return err
240	}
241
242	return nil
243}