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_multi.gno

9.00 Kb · 284 lines
  1package router
  2
  3import (
  4	prbac "gno.land/p/gnoswap/rbac"
  5	u256 "gno.land/p/gnoswap/uint256"
  6	"gno.land/r/gnoswap/access"
  7)
  8
  9// SwapDirection represents the direction of swap execution in multi-hop swaps.
 10// It determines whether swaps are processed in forward order (first to last pool)
 11// or backward order (last to first pool).
 12type SwapDirection int
 13
 14const (
 15	_ SwapDirection = iota
 16	// Forward indicates a swap processing direction from the first pool to the last pool.
 17	// Used primarily for exactIn swaps where the input amount is known.
 18	Forward
 19
 20	// Backward indicates a swap processing direction from the last pool to the first pool.
 21	// Used primarily for exactOut swaps where the output amount is known and input amounts
 22	// Need to be calculated in reverse order.
 23	Backward
 24)
 25
 26// MultiSwapExecutor defines the interface for multi-hop swap operation execution.
 27type MultiSwapExecutor interface {
 28	// Run performs the swap operation and returns pool received and pool output amounts.
 29	Run(p SwapParams, data SwapCallbackData, recipient address) (int64, int64)
 30}
 31
 32// DryMultiSwapExecutor implements MultiSwapExecutor for dry run simulations.
 33type DryMultiSwapExecutor struct {
 34	router *routerV1
 35}
 36
 37// Run performs a dry swap operation without changing state.
 38func (e *DryMultiSwapExecutor) Run(p SwapParams, data SwapCallbackData, _ address) (int64, int64) {
 39	return e.router.swapDryInner(p.amountSpecified, u256.Zero(), data)
 40}
 41
 42// RealMultiSwapExecutor implements MultiSwapExecutor for actual swap operations.
 43type RealMultiSwapExecutor struct {
 44	rlm    realm
 45	router *routerV1
 46}
 47
 48// Run performs a real swap operation with state changes.
 49func (e *RealMultiSwapExecutor) Run(p SwapParams, data SwapCallbackData, recipient address) (int64, int64) {
 50	return e.router.swapInner(0, e.rlm, p.amountSpecified, recipient, u256.Zero(), data)
 51}
 52
 53// MultiSwapProcessor handles the execution flow for multi-hop swaps.
 54type MultiSwapProcessor struct {
 55	executor   MultiSwapExecutor
 56	direction  SwapDirection
 57	router     *routerV1
 58	isSimulate bool
 59	// payer is the address used to identify the user. For real swaps it is the
 60	// PreviousRealm address; for dry-run paths it is resolved at the entry point.
 61	payer address
 62}
 63
 64var (
 65	_ MultiSwapExecutor = (*DryMultiSwapExecutor)(nil)
 66	_ MultiSwapExecutor = (*RealMultiSwapExecutor)(nil)
 67)
 68
 69// newRealMultiSwapProcessor creates a processor that performs real swaps.
 70func newRealMultiSwapProcessor(_ int, rlm realm, r *routerV1, direction SwapDirection, payer address) *MultiSwapProcessor {
 71	return &MultiSwapProcessor{
 72		executor:   &RealMultiSwapExecutor{rlm: rlm, router: r},
 73		direction:  direction,
 74		router:     r,
 75		isSimulate: false,
 76		payer:      payer,
 77	}
 78}
 79
 80// newDryMultiSwapProcessor creates a processor that performs dry-run simulations.
 81func newDryMultiSwapProcessor(r *routerV1, direction SwapDirection, payer address) *MultiSwapProcessor {
 82	return &MultiSwapProcessor{
 83		executor:   &DryMultiSwapExecutor{router: r},
 84		direction:  direction,
 85		router:     r,
 86		isSimulate: true,
 87		payer:      payer,
 88	}
 89}
 90
 91// processForwardSwap handles forward direction swaps (exactIn).
 92func (p *MultiSwapProcessor) processForwardSwap(sp SwapParams, numPools int, swapPath string) (int64, int64, error) {
 93	payer := p.payer // Initial payer is the user
 94	routerAddr := access.MustGetAddress(prbac.ROLE_ROUTER.String())
 95
 96	firstAmountIn := int64(0)
 97	currentPoolIndex := 0
 98
 99	for {
100		currentPoolIndex++
101
102		// Execute the swap operation
103		callbackData := newSwapCallbackData(sp, payer)
104		amountIn, amountOut := p.executor.Run(sp, callbackData, sp.recipient)
105
106		// Record the first hop's input amount
107		if currentPoolIndex == 1 {
108			firstAmountIn = amountIn
109		}
110
111		// Check if we've processed all hops
112		if currentPoolIndex >= numPools {
113			return firstAmountIn, amountOut, nil
114		}
115
116		// Update parameters for the next hop
117		payer = routerAddr
118		nextInput, nextOutput, nextFee := getDataForMultiPath(swapPath, currentPoolIndex)
119		sp.tokenIn = nextInput
120		sp.tokenOut = nextOutput
121		sp.fee = nextFee
122		sp.amountSpecified = amountOut
123	}
124}
125
126// processBackwardSwap handles backward direction swaps (exactOut).
127func (p *MultiSwapProcessor) processBackwardSwap(sp SwapParams, numPools int, swapPath string) (int64, int64, error) {
128	if !p.isSimulate {
129		return p.processBackwardRealSwap(sp, numPools, swapPath)
130	}
131	return p.processBackwardDrySwap(sp, numPools, swapPath)
132}
133
134// processBackwardDrySwap handles backward simulated swaps.
135func (p *MultiSwapProcessor) processBackwardDrySwap(sp SwapParams, numPools int, swapPath string) (int64, int64, error) {
136	firstAmountIn := int64(0)
137	currentPoolIndex := numPools - 1
138	routerAddr := access.MustGetAddress(prbac.ROLE_ROUTER.String())
139	payer := routerAddr
140
141	for {
142		callbackData := newSwapCallbackData(sp, payer)
143		amountIn, amountOut := p.executor.Run(sp, callbackData, sp.recipient)
144
145		if currentPoolIndex == 0 {
146			firstAmountIn = amountIn
147		}
148
149		currentPoolIndex--
150
151		if currentPoolIndex == -1 {
152			return firstAmountIn, amountOut, nil
153		}
154
155		// Update parameters for the next hop
156		nextInput, nextOutput, nextFee := getDataForMultiPath(swapPath, currentPoolIndex)
157
158		sp.amountSpecified = -amountIn
159		sp.tokenIn = nextInput
160		sp.tokenOut = nextOutput
161		sp.fee = nextFee
162	}
163}
164
165// processBackwardRealSwap handles backward real swaps.
166func (p *MultiSwapProcessor) processBackwardRealSwap(sp SwapParams, numPools int, swapPath string) (int64, int64, error) {
167	// First collect all swap information by simulating backward
168	swapInfo := p.collectBackwardSwapInfo(sp, numPools, swapPath)
169
170	// Then execute swaps in forward order
171	return p.executeCollectedSwaps(swapInfo, sp.recipient)
172}
173
174// collectBackwardSwapInfo simulates swaps backward to collect parameters.
175func (p *MultiSwapProcessor) collectBackwardSwapInfo(sp SwapParams, numPools int, swapPath string) []SingleSwapParams {
176	currentPoolIndex := numPools - 1
177	swapInfo := make([]SingleSwapParams, 0, currentPoolIndex)
178
179	for currentPoolIndex >= 0 {
180		thisSwap := SingleSwapParams{
181			tokenIn:         sp.tokenIn,
182			tokenOut:        sp.tokenOut,
183			fee:             sp.fee,
184			amountSpecified: sp.amountSpecified,
185		}
186
187		// dry simulation to calculate input amount
188		amountIn, _ := p.router.singleDrySwap(p.payer, &thisSwap)
189		swapInfo = append(swapInfo, thisSwap)
190
191		if currentPoolIndex == 0 {
192			break
193		}
194		currentPoolIndex--
195
196		// Update parameters for the next simulation
197		nextInput, nextOutput, nextFee := getDataForMultiPath(swapPath, currentPoolIndex)
198
199		sp.tokenIn = nextInput
200		sp.tokenOut = nextOutput
201		sp.fee = nextFee
202		sp.amountSpecified = -amountIn
203	}
204
205	return swapInfo
206}
207
208// executeCollectedSwaps performs the collected swaps in forward order.
209// Only invoked from the real (non-simulated) backward swap path, so the
210// executor is always a *RealMultiSwapExecutor carrying the realm value.
211func (p *MultiSwapProcessor) executeCollectedSwaps(swapInfo []SingleSwapParams, recipient address) (int64, int64, error) {
212	firstAmountIn := int64(0)
213	currentPoolIndex := len(swapInfo) - 1
214	payer := p.payer // Initial payer is the user
215	routerAddr := access.MustGetAddress(prbac.ROLE_ROUTER.String())
216	rlm := p.executor.(*RealMultiSwapExecutor).rlm
217
218	for currentPoolIndex >= 0 {
219		// Execute the swap
220		callbackData := newSwapCallbackData(
221			swapInfo[currentPoolIndex],
222			payer,
223		)
224
225		amountIn, amountOut := p.router.swapInner(
226			0,
227			rlm,
228			swapInfo[currentPoolIndex].amountSpecified,
229			recipient,
230			u256.Zero(),
231			callbackData,
232		)
233
234		// Record the first hop's input amount
235		if currentPoolIndex == len(swapInfo)-1 {
236			firstAmountIn = amountIn
237		}
238
239		if currentPoolIndex == 0 {
240			return firstAmountIn, amountOut, nil
241		}
242
243		// Update parameters for the next swap
244		swapInfo[currentPoolIndex-1].amountSpecified = amountOut
245		payer = routerAddr
246		currentPoolIndex--
247	}
248
249	return firstAmountIn, 0, nil
250}
251
252// multiSwap performs a multi-hop swap in forward direction.
253func (r *routerV1) multiSwap(_ int, rlm realm, p SwapParams, numPools int, swapPath string) (int64, int64) {
254	payer := rlm.Previous().Address()
255	result, output, err := newRealMultiSwapProcessor(0, rlm, r, Forward, payer).
256		processForwardSwap(p, numPools, swapPath)
257	if err != nil {
258		panic(err)
259	}
260	return result, output
261}
262
263// multiSwapNegative performs a multi-hop swap in backward direction.
264func (r *routerV1) multiSwapNegative(_ int, rlm realm, p SwapParams, numPools int, swapPath string) (int64, int64) {
265	payer := rlm.Previous().Address()
266	result, output, err := newRealMultiSwapProcessor(0, rlm, r, Backward, payer).
267		processBackwardSwap(p, numPools, swapPath)
268	if err != nil {
269		panic(err)
270	}
271	return result, output
272}
273
274// multiDrySwap simulates a multi-hop swap in forward direction.
275func (r *routerV1) multiDrySwap(payer address, p SwapParams, numPool int, swapPath string) (int64, int64, error) {
276	return newDryMultiSwapProcessor(r, Forward, payer).
277		processForwardSwap(p, numPool, swapPath)
278}
279
280// multiDrySwapNegative simulates a multi-hop swap in backward direction.
281func (r *routerV1) multiDrySwapNegative(payer address, p SwapParams, numPool int, swapPath string) (int64, int64, error) {
282	return newDryMultiSwapProcessor(r, Backward, payer).
283		processBackwardSwap(p, numPool, swapPath)
284}