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