Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}