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}