Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

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}