Search Apps Documentation Source Content File Folder Download Copy Actions Download

protocol_fee.gno

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