Search Apps Documentation Source Content File Folder Download Copy Actions Download

swap_multi.gno

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