swap_callback.gno
2.84 Kb · 95 lines
1package router
2
3import (
4 "errors"
5
6 prbac "gno.land/p/gnoswap/rbac/v1"
7 u256 "gno.land/p/gnoswap/uint256/v1"
8 ufmt "gno.land/p/nt/ufmt/v0"
9
10 "gno.land/r/gnoswap/access/v1"
11 "gno.land/r/gnoswap/common"
12 "gno.land/r/gnoswap/halt/v1"
13)
14
15// SwapCallback implements the pool's SwapCallback interface.
16// The pool invokes it after sending output tokens; the router then transfers the
17// positive input delta to the pool.
18//
19// Parameters:
20// - _: leading callback discriminator; callers pass 0
21// - rlm: propagated current realm context; must remain the current crossing frame
22// - token0Path: token-0 contract path in canonical pool order
23// - token1Path: token-1 contract path in canonical pool order
24// - amount0Delta: signed token-0 delta; positive means the pool is owed this amount
25// - amount1Delta: signed token-1 delta; positive means the pool is owed this amount
26// - payer: address whose balance supplies the positive input delta
27//
28// Returns:
29// - err: nil after the owed input transfer; an error for spoofed realm, invalid liquidity delta, or failed payment
30//
31// Positive deltas are tokens the pool must receive; negative deltas are tokens
32// the pool has sent to the recipient.
33func (r *routerV1) SwapCallback(
34 _ int,
35 rlm realm,
36 token0Path, token1Path string,
37 amount0Delta, amount1Delta int64,
38 payer address,
39) error {
40 if !rlm.IsCurrent() {
41 return errors.New(errSpoofedRealm)
42 }
43
44 if amount0Delta <= 0 && amount1Delta <= 0 {
45 return makeErrorWithDetails(
46 errInsufficientLiquidity,
47 "swap entirely within 0-liquidity region",
48 )
49 }
50
51 halt.AssertIsNotHaltedRouter()
52
53 caller := rlm.Previous().Address()
54 assertIsRouterImplementation(caller)
55
56 var tokenToPay string
57 var amountToPay int64
58
59 // amount0Delta > 0 means pool wants token0
60 // amount1Delta > 0 means pool wants token1
61 if amount0Delta > 0 {
62 amountToPay = amount0Delta
63 tokenToPay = token0Path
64 } else {
65 amountToPay = amount1Delta
66 tokenToPay = token1Path
67 }
68
69 // Transfer tokens from router to pool
70 // The router should already have the tokens from the user
71 r.transferToPool(0, rlm, tokenToPay, u256.NewUintFromInt64(amountToPay), payer)
72
73 return nil
74}
75
76// transferToPool transfers tokens from router to pool
77func (r *routerV1) transferToPool(_ int, rlm realm, token string, amount *u256.Uint, payer address) {
78 balance := common.BalanceOf(token, payer)
79
80 if u256.NewUintFromInt64(balance).Lt(amount) {
81 panic(makeErrorWithDetails(
82 errInsufficientBalance,
83 ufmt.Sprintf("token=%s, required=%d, available=%d, payer=%s", token, amount.Int64(), balance, payer.String()),
84 ))
85 }
86
87 poolAddr := access.MustGetAddress(prbac.ROLE_POOL.String())
88 routerAddr := access.MustGetAddress(prbac.ROLE_ROUTER.String())
89
90 if payer == routerAddr {
91 common.SafeGRC20Transfer(0, rlm, token, poolAddr, amount.Int64())
92 } else {
93 common.SafeGRC20TransferFrom(0, rlm, token, payer, poolAddr, amount.Int64())
94 }
95}