Kind Realm (renderable)
Name v1
Namespace gnoswap / pool
Exported functions 3
Module gno.land/r/gnoswap/pool/v1
gno 0.9 transfer.gno gno
⧉
1 package pool 2 3 import ( 4 ufmt "gno.land/p/nt/ufmt/v0" 5 6 prabc "gno.land/p/gnoswap/rbac/v1" 7 8 GetPoolPath (token0Path string, token1Path string, fee uint32) string
NewPoolV1 (store interface {GetFeeAmountTickSpacing func() map[uint32]int32; GetObservations func() *gno.land/p/nt/bptree/v0.BPTree; GetPendingProtocolFee func(string) int64; GetPendingProtocolFees func() map[string]int64; GetPoolCreationFee func() int64; GetPools func() *gno.land/p/nt/bptree/v0.BPTree; GetSlot0FeeProtocol func() uint8; GetSwapEndHook func() func(.uverse.realm, string) .uverse.error; GetSwapStartHook func() func(.uverse.realm, string, int64); GetTickCrossHook func() func(.uverse.realm, string, int32, bool, int64); GetUnlocked func() bool; GetWithdrawalFeeBPS func() uint64; HasFeeAmountTickSpacing func() bool; HasObservations func() bool; HasPendingProtocolFees func() bool; HasPoolCreationFee func() bool; HasPools func() bool; HasSlot0FeeProtocol func() bool; HasSwapEndHook func() bool; HasSwapStartHook func() bool; HasTickCrossHook func() bool; HasUnlocked func() bool; HasWithdrawalFeeBPS func() bool; RemovePendingProtocolFee func(int, .uverse.realm, string) .uverse.error; SetFeeAmountTickSpacing func(int, .uverse.realm, map[uint32]int32) .uverse.error; SetObservations func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error; SetPendingProtocolFee func(int, .uverse.realm, string, int64) .uverse.error; SetPendingProtocolFees func(int, .uverse.realm, map[string]int64) .uverse.error; SetPoolCreationFee func(int, .uverse.realm, int64) .uverse.error; SetPools func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error; SetSlot0FeeProtocol func(int, .uverse.realm, uint8) .uverse.error; SetSwapEndHook func(int, .uverse.realm, func(.uverse.realm, string) .uverse.error) .uverse.error; SetSwapStartHook func(int, .uverse.realm, func(.uverse.realm, string, int64)) .uverse.error; SetTickCrossHook func(int, .uverse.realm, func(.uverse.realm, string, int32, bool, int64)) .uverse.error; SetUnlocked func(int, .uverse.realm, bool) .uverse.error; SetWithdrawalFeeBPS func(int, .uverse.realm, uint64) .uverse.error}) interface {Burn func(int, .uverse.realm, string, string, uint32, int32, int32, string, .uverse.address) (string, string); Collect func(int, .uverse.realm, string, string, uint32, .uverse.address, int32, int32, string, string) (string, string); CollectProtocol func(int, .uverse.realm, string, string, uint32, .uverse.address, string, string) (string, string); CollectSwapFee func(int, .uverse.realm, string, string, uint32, .uverse.address, int32, int32, string, string) (string, string, string, string); CreatePool func(int, .uverse.realm, string, string, uint32, string); DrySwap func(string, string, uint32, bool, string, string) (string, string, .uverse.error); ExistsPoolPath func(string) bool; GetBalanceToken0 func(string) (int64, .uverse.error); GetBalanceToken1 func(string) (int64, .uverse.error); GetFee func(string) (uint32, .uverse.error); GetFeeAmountTickSpacing func(uint32) (int32, .uverse.error); GetFeeAmountTickSpacings func() map[uint32]int32; GetFeeGrowthGlobal0X128 func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetFeeGrowthGlobal1X128 func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetFeeGrowthGlobalX128 func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, *gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetInitializedTicksInRange func(string, int32, int32) ([]int32, .uverse.error); GetLiquidity func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetObservationAt func(string, uint16) (gno.land/r/gnoswap/pool.Observation, .uverse.error); GetPendingProtocolFees func() map[string]int64; GetPoolCreationFee func() int64; GetPoolPositions func(string) *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetPools func() *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetPositionFeeGrowthInside0LastX128 func(string, string) (string, .uverse.error); GetPositionFeeGrowthInside1LastX128 func(string, string) (string, .uverse.error); GetPositionFeeGrowthInsideLastX128 func(string, string) (string, string, .uverse.error); GetPositionLiquidity func(string, string) (string, .uverse.error); GetPositionTokensOwed0 func(string, string) (int64, .uverse.error); GetPositionTokensOwed1 func(string, string) (int64, .uverse.error); GetProtocolFeesToken0 func(string) (int64, .uverse.error); GetProtocolFeesToken1 func(string) (int64, .uverse.error); GetSlot0 func(string) gno.land/r/gnoswap/pool.Slot0; GetSlot0FeeProtocol func(string) (uint8, .uverse.error); GetSlot0SqrtPriceX96 func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetSlot0Tick func(string) (int32, .uverse.error); GetSlot0Unlocked func(string) (bool, .uverse.error); GetTickBitmaps func(string, int16) (string, .uverse.error); GetTickCumulativeOutside func(string, int32) (int64, .uverse.error); GetTickFeeGrowthOutside0X128 func(string, int32) (string, .uverse.error); GetTickFeeGrowthOutside1X128 func(string, int32) (string, .uverse.error); GetTickFeeGrowthOutsideX128 func(string, int32) (string, string, .uverse.error); GetTickInfo func(string, int32) (gno.land/r/gnoswap/pool.TickInfo, .uverse.error); GetTickInitialized func(string, int32) (bool, .uverse.error); GetTickLiquidityGross func(string, int32) (string, .uverse.error); GetTickLiquidityNet func(string, int32) (string, .uverse.error); GetTickSecondsOutside func(string, int32) (uint32, .uverse.error); GetTickSecondsPerLiquidityOutsideX128 func(string, int32) (string, .uverse.error); GetTickSpacing func(string) (int32, .uverse.error); GetToken0Path func(string) (string, .uverse.error); GetToken1Path func(string) (string, .uverse.error); GetWithdrawalFee func() uint64; IncreaseObservationCardinalityNext func(int, .uverse.realm, string, string, uint32, uint16); Mint func(int, .uverse.realm, string, string, uint32, int32, int32, string, .uverse.address) (string, string); Observe func(string, []uint32) ([]int64, []string, .uverse.error); OracleConsult func(string, uint32) (int32, *gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); Render func(string) string; SetFeeProtocol func(int, .uverse.realm, uint8, uint8); SetPoolCreationFee func(int, .uverse.realm, int64); SetSwapEndHook func(int, .uverse.realm, func(.uverse.realm, string) .uverse.error); SetSwapStartHook func(int, .uverse.realm, func(.uverse.realm, string, int64)); SetTickCrossHook func(int, .uverse.realm, func(.uverse.realm, string, int32, bool, int64)); SetWithdrawalFee func(int, .uverse.realm, uint64); SnapshotCumulativesInside func(string, int32, int32) (int64, *gno.land/p/gnoswap/uint256/v1.Uint, uint32, .uverse.error); Swap func(int, .uverse.realm, string, string, uint32, .uverse.address, bool, string, string, func(.uverse.realm, int64, int64, *gno.land/r/gnoswap/pool.CallbackMarker) .uverse.error) (string, string)}
NewTickEventInfo (tickID int32, tickInfo struct{liquidityGross string; liquidityNet string; feeGrowthOutside0X128 string; feeGrowthOutside1X128 string; tickCumulativeOutside int64; secondsPerLiquidityOutsideX128 string; secondsOutside uint32; initialized bool}) *gno.land/r/gnoswap/pool/v1.tickEventInfo
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
This realm does not provide renderable output (vm/qrender → NoRenderDeclError).
View on gnoweb ↗ vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.
"gno.land/r/gnoswap/access/v1"
9 "gno.land/r/gnoswap/common"
10 pl "gno.land/r/gnoswap/pool"
11
12 "gno.land/p/gnoswap/gnsmath/v1"
13 i256 "gno.land/p/gnoswap/int256/v1"
14 u256 "gno.land/p/gnoswap/uint256/v1"
15 )
16
17 // safeTransfer performs a token transfer out of the pool while ensuring
18 // the pool has sufficient balance and updating internal accounting.
19 // This function is typically used during swaps and liquidity removals.
20 //
21 // Important requirements:
22 // - The amount must be a positive value representing tokens to transfer out
23 // - The pool must have sufficient balance for the transfer
24 // - The transfer amount must fit within int64 range
25 //
26 // Parameters:
27 // - p: the pool instance
28 // - to: destination address for the transfer
29 // - tokenPath: path identifier of the token to transfer
30 // - amount: amount to transfer (positive u256.Uint value)
31 // - isToken0: true if transferring token0, false for token1
32 //
33 // The function will:
34 // 1. Check pool has sufficient balance
35 // 2. Execute the transfer
36 // 3. Subtract the amount from pool's internal balance
37 //
38 // Panics if any validation fails or if the transfer fails
39 func (i *poolV1) safeTransfer(
40 _ int ,
41 rlm realm,
42 p *pl.Pool,
43 to address,
44 tokenPath string ,
45 amount *u256.Uint,
46 isToken0 bool ,
47 ) {
48 token0 := p.BalanceToken0()
49 token1 := p.BalanceToken1()
50 amountInt64 := gnsmath.SafeConvertToInt64(amount)
51
52 if err := validatePoolBalance(token0, token1, amountInt64, isToken0); err != nil {
53 panic(err)
54 }
55
56 common.SafeGRC20Transfer( 0 , rlm, tokenPath, to, amountInt64)
57
58 newBalance, err := updatePoolBalance(token0, token1, amountInt64, isToken0)
59 if err != nil {
60 panic(err)
61 }
62
63 poolBalances := p.Balances()
64
65 if isToken0 {
66 poolBalances.SetToken0(newBalance)
67 } else {
68 poolBalances.SetToken1(newBalance)
69 }
70
71 p.SetBalances(poolBalances)
72 }
73
74 // safeTransferFrom securely transfers tokens into the pool while ensuring balance consistency.
75 //
76 // This function performs the following steps:
77 // 1. Validates and converts the transfer amount to `int64` using `gnsmath.SafeConvertToInt64`.
78 // 2. Executes the token transfer using `TransferFrom` via the token teller contract.
79 // 3. Verifies that the destination balance reflects the correct amount after transfer.
80 // 4. Updates the pool's internal balances (`token0` or `token1`) and validates the updated state.
81 //
82 // Parameters:
83 // - p (*pl.Pool): The pool instance to transfer tokens into.
84 // - from (address): Source address for the token transfer.
85 // - to (address): Destination address, typically the pool address.
86 // - tokenPath (string): Path identifier for the token being transferred.
87 // - amount (*u256.Uint): The amount of tokens to transfer (must be a positive value).
88 // - isToken0 (bool): A flag indicating whether the token being transferred is token0 (`true`) or token1 (`false`).
89 //
90 // Panics:
91 // - If the `amount` exceeds the int64 range during conversion.
92 // - If the token transfer (`TransferFrom`) fails.
93 // - If the destination balance after the transfer does not match the expected amount.
94 // - If the pool's internal balances (`token0` or `token1`) overflow or become inconsistent.
95 //
96 // Notes:
97 // - The function assumes that the sender (`from`) has approved the pool to spend the specified tokens.
98 // - The balance consistency check ensures that no tokens are lost or double-counted during the transfer.
99 // - Pool balance updates are performed atomically to ensure internal consistency.
100 func (i *poolV1) safeTransferFrom(
101 _ int ,
102 rlm realm,
103 p *pl.Pool,
104 from, to address,
105 tokenPath string ,
106 amount *u256.Uint,
107 isToken0 bool ,
108 ) {
109 amountInt64 := gnsmath.SafeConvertToInt64(amount)
110
111 beforeBalance := common.BalanceOf(tokenPath, to)
112
113 common.SafeGRC20TransferFrom( 0 , rlm, tokenPath, from, to, amountInt64)
114
115 afterBalance := common.BalanceOf(tokenPath, to)
116 if gnsmath.SafeAddInt64(beforeBalance, amountInt64) != afterBalance {
117 panic(ufmt.Sprintf(
118 "%v. beforeBalance(%d) + amount(%d) != afterBalance(%d)" ,
119 errTransferFailed, beforeBalance, amountInt64, afterBalance,
120 ))
121 }
122
123 poolBalances := p.Balances()
124
125 // update pool balances
126 if isToken0 {
127 poolBalances.SetToken0(gnsmath.SafeAddInt64(poolBalances.Token0(), amountInt64))
128 } else {
129 poolBalances.SetToken1(gnsmath.SafeAddInt64(poolBalances.Token1(), amountInt64))
130 }
131
132 p.SetBalances(poolBalances)
133 }
134
135 // safeSwapCallback executes a swap callback and validates the token payment.
136 //
137 // This function implements the flash swap pattern where the pool first sends output tokens
138 // to the recipient, then invokes the callback to receive input tokens from the caller.
139 // The callback is responsible for transferring the required input tokens to the pool.
140 //
141 // Callback Signature:
142 //
143 // func(cur realm, amount0Delta, amount1Delta int64, _ *pool.CallbackMarker) error
144 //
145 // Delta Convention (following Uniswap V3 standard):
146 // - Positive delta: Amount the pool must RECEIVE (input token)
147 // - Negative delta: Amount the pool has SENT (output token)
148 //
149 // For zeroForOne swaps (token0 → token1):
150 // - amount0Delta > 0: Pool receives token0 (input)
151 // - amount1Delta < 0: Pool sends token1 (output)
152 //
153 // For oneForZero swaps (token1 → token0):
154 // - amount0Delta < 0: Pool sends token0 (output)
155 // - amount1Delta > 0: Pool receives token1 (input)
156 //
157 // Parameters:
158 // - p: The pool instance
159 // - tokenPath: Path of the input token that pool must receive
160 // - amountInInt256: Amount pool must receive (positive i256.Int)
161 // - amountOutInt256: Amount pool has sent (negative i256.Int)
162 // - zeroForOne: Swap direction (true = token0 to token1)
163 // - swapCallback: Callback function that must transfer input tokens to pool
164 //
165 // The callback needed:
166 // 1. Verify the caller is the legitimate pool contract address to prevent unauthorized invocations
167 // 2. Transfer at least `amountIn` of input tokens to the pool
168 // 3. Return nil on success, or an error to revert the swap
169 //
170 // Example callback implementation:
171 //
172 // func(cur realm, amount0Delta, amount1Delta int64, _ *pool.CallbackMarker) error {
173 // // Security check: ensure this callback is invoked by the legitimate pool
174 // caller := runtime.PreviousRealm().Address()
175 // poolAddr := chain.PackageAddress("gno.land/r/gnoswap/pool")
176 //
177 // if caller != poolAddr {
178 // panic("unauthorized caller")
179 // }
180 //
181 // if amount0Delta > 0 {
182 // // Transfer token0 to pool
183 // token0.Transfer(cross, poolAddr, amount0Delta)
184 // }
185 // if amount1Delta > 0 {
186 // // Transfer token1 to pool
187 // token1.Transfer(cross, poolAddr, amount1Delta)
188 // }
189 // return nil
190 // }
191 func (i *poolV1) safeSwapCallback(
192 _ int ,
193 rlm realm,
194 p *pl.Pool,
195 tokenPath string ,
196 amountInInt256 *i256.Int,
197 amountOutInt256 *i256.Int,
198 zeroForOne bool ,
199 swapCallback func (cur realm, amount0Delta, amount1Delta int64 , _ *pl.CallbackMarker) error ,
200 ) {
201 if swapCallback == nil {
202 panic(makeErrorWithDetails(
203 errInvalidInput,
204 "swapCallback is nil" ,
205 ))
206 }
207
208 currentAddress := access.MustGetAddress(prabc.ROLE_POOL.String())
209
210 amountIn := amountInInt256.Int64()
211 amountOut := amountOutInt256.Int64()
212 balanceBefore := common.BalanceOf(tokenPath, currentAddress)
213
214 // Make callback to the calling contract
215 // The contract should transfer tokens to pool within this callback
216 // Following Uniswap V3 convention:
217 // - Positive delta: amount the pool must receive (input token)
218 // - Negative delta: amount the pool has sent (output token)
219 var amount0Delta, amount1Delta, beforeTokenBalance int64
220
221 if zeroForOne {
222 // zeroForOne: pool receives token0 (positive), sends token1 (negative)
223 amount0Delta = amountIn
224 amount1Delta = amountOut
225 beforeTokenBalance = p.BalanceToken0()
226 } else {
227 // !zeroForOne: pool receives token1 (positive), sends token0 (negative)
228 amount0Delta = amountOut
229 amount1Delta = amountIn
230 beforeTokenBalance = p.BalanceToken1()
231 }
232
233 // execute swap callback
234 // CallbackMarker is created by the pool module to enforce type-system level validation.
235 // Allocation goes through pl.NewCallbackMarker so the alloc happens in pool realm
236 // (interrealm v2 forbids /r/-declared types being constructed outside their owner).
237 err := swapCallback(cross(rlm), amount0Delta, amount1Delta, pl.NewCallbackMarker())
238 if err != nil {
239 panic(err)
240 }
241
242 balanceAfter := common.BalanceOf(tokenPath, currentAddress)
243 balanceIncrease := gnsmath.SafeSubInt64(balanceAfter, balanceBefore)
244
245 // check insufficient payment by swap callback
246 if balanceIncrease < amountIn {
247 panic(makeErrorWithDetails(
248 errInsufficientPayment,
249 ufmt.Sprintf( "insufficient payment: expected %d, received %d" , amountIn, balanceIncrease),
250 ))
251 }
252
253 // check overflow update pool balance
254 resultTokenBalance := gnsmath.SafeAddInt64(beforeTokenBalance, amountIn)
255
256 poolBalances := p.Balances()
257
258 if zeroForOne {
259 poolBalances.SetToken0(resultTokenBalance)
260 } else {
261 poolBalances.SetToken1(resultTokenBalance)
262 }
263
264 p.SetBalances(poolBalances)
265 }
266
267 // validatePoolBalance checks if the pool has sufficient balance of either token0 and token1
268 // before proceeding with a transfer. This prevents the pool won't go into a negative balance.
269 func validatePoolBalance(token0, token1, amount int64 , isToken0 bool ) error {
270 if amount < 0 {
271 return ufmt.Errorf( "%v. amount(%d) must be positive" , errTransferFailed, amount)
272 }
273
274 if isToken0 {
275 if token0 < amount {
276 return ufmt.Errorf(
277 "%v. token0(%d) >= amount(%d)" ,
278 errTransferFailed, token0, amount,
279 )
280 }
281 return nil
282 }
283 if token1 < amount {
284 return ufmt.Errorf(
285 "%v. token1(%d) >= amount(%d)" ,
286 errTransferFailed, token1, amount,
287 )
288 }
289 return nil
290 }
291
292 // updatePoolBalance calculates the new balance after a transfer and validate.
293 // It ensures the resulting balance won't be negative or overflow.
294 func updatePoolBalance(
295 token0, token1, amount int64 ,
296 isToken0 bool ,
297 ) ( int64 , error ) {
298 if amount < 0 {
299 return 0 , ufmt.Errorf( "%v. amount(%d) must be positive" , errBalanceUpdateFailed, amount)
300 }
301
302 if isToken0 {
303 if token0 < amount {
304 return 0 , ufmt.Errorf(
305 "%v. cannot decrease, token0(%d) - amount(%d)" ,
306 errBalanceUpdateFailed, token0, amount,
307 )
308 }
309 return gnsmath.SafeSubInt64(token0, amount), nil
310 }
311
312 if token1 < amount {
313 return 0 , ufmt.Errorf(
314 "%v. cannot decrease, token1(%d) - amount(%d)" ,
315 errBalanceUpdateFailed, token1, amount,
316 )
317 }
318 return gnsmath.SafeSubInt64(token1, amount), nil
319 }
320