swap_callback.gno
2.64 Kb · 93 lines
1package v1
2
3import (
4 prbac "gno.land/p/gnoswap/rbac"
5 u256 "gno.land/p/gnoswap/uint256"
6 ufmt "gno.land/p/nt/ufmt/v0"
7
8 "gno.land/r/gnoswap/access"
9 "gno.land/r/gnoswap/common"
10 "gno.land/r/gnoswap/halt"
11)
12
13// SwapCallback implements the pool's SwapCallback interface.
14// This is called by the pool after it has sent output tokens to the recipient.
15// The router must transfer the required input tokens to the pool.
16//
17// This callback pattern enables:
18// 1. Flash swaps (receive tokens before paying)
19// 2. Just-in-time token transfers
20// 3. Complex multi-hop swaps without intermediate transfers
21//
22// Parameters:
23// - token0Path, token1Path: Pool token paths
24// - amount0Delta, amount1Delta: Token deltas (positive = owe, negative = received)
25// - payer: Address that will pay for the swap
26//
27// Access:
28// - Only callable by Router v1 address (self-callback from pool)
29//
30// Returns:
31// - error: nil on success, error if transfer fails
32//
33// Delta convention (Uniswap V3):
34// - Positive delta: tokens the pool must receive (input token)
35// - Negative delta: tokens the pool has sent (output token)
36func (r *routerV1) SwapCallback(
37 _ int,
38 rlm realm,
39 token0Path, token1Path string,
40 amount0Delta, amount1Delta int64,
41 payer address,
42) error {
43 if !rlm.IsCurrent() {
44 return errSpoofedRealm
45 }
46
47 halt.AssertIsNotHaltedRouter()
48
49 caller := rlm.Previous().Address()
50 assertIsRouterImplementation(caller)
51
52 var tokenToPay string
53 var amountToPay int64
54
55 // amount0Delta > 0 means pool wants token0
56 // amount1Delta > 0 means pool wants token1
57 if amount0Delta > 0 {
58 amountToPay = amount0Delta
59 tokenToPay = token0Path
60 } else if amount1Delta > 0 {
61 amountToPay = amount1Delta
62 tokenToPay = token1Path
63 } else {
64 return nil
65 }
66
67 // Transfer tokens from router to pool
68 // The router should already have the tokens from the user
69 r.transferToPool(0, rlm, tokenToPay, u256.NewUintFromInt64(amountToPay), payer)
70
71 return nil
72}
73
74// transferToPool transfers tokens from router to pool
75func (r *routerV1) transferToPool(_ int, rlm realm, token string, amount *u256.Uint, payer address) {
76 balance := common.BalanceOf(token, payer)
77
78 if u256.NewUintFromInt64(balance).Lt(amount) {
79 panic(makeErrorWithDetails(
80 errInsufficientBalance,
81 ufmt.Sprintf("token=%s, required=%d, available=%d, payer=%s", token, amount.Int64(), balance, payer.String()),
82 ))
83 }
84
85 poolAddr := access.MustGetAddress(prbac.ROLE_POOL.String())
86 routerAddr := access.MustGetAddress(prbac.ROLE_ROUTER.String())
87
88 if payer == routerAddr {
89 common.SafeGRC20Transfer(cross(rlm), token, poolAddr, amount.Int64())
90 } else {
91 common.SafeGRC20TransferFrom(cross(rlm), token, payer, poolAddr, amount.Int64())
92 }
93}