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}