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

5.62 Kb · 188 lines
  1package router
  2
  3import (
  4	prbac "gno.land/p/gnoswap/rbac/v1"
  5	u256 "gno.land/p/gnoswap/uint256/v1"
  6
  7	"gno.land/r/gnoswap/access/v1"
  8)
  9
 10// multiSwap performs a multi-hop exact-input swap in forward order.
 11func (r *routerV1) multiSwap(_ int, rlm realm, sp SwapParams, numPools int, swapPath string) (int64, int64) {
 12	payer := rlm.Previous().Address()
 13	routerAddr := access.MustGetAddress(prbac.ROLE_ROUTER.String())
 14	firstAmountIn := int64(0)
 15
 16	for currentPoolIndex := 0; ; currentPoolIndex++ {
 17		callbackData := newSwapCallbackData(sp, payer)
 18		amountIn, amountOut := r.swapInner(
 19			0,
 20			rlm,
 21			sp.amountSpecified,
 22			sp.recipient,
 23			u256.Zero(),
 24			callbackData,
 25		)
 26
 27		// every hop of a forward multi-hop swap is exact-in
 28		assertHopFullyConsumed(currentPoolIndex, sp.amountSpecified, amountIn)
 29
 30		if currentPoolIndex == 0 {
 31			firstAmountIn = amountIn
 32		}
 33
 34		nextPoolIndex := currentPoolIndex + 1
 35		if nextPoolIndex >= numPools {
 36			return firstAmountIn, amountOut
 37		}
 38
 39		payer = routerAddr
 40		nextInput, nextOutput, nextFee := getDataForMultiPath(swapPath, nextPoolIndex)
 41		sp.tokenIn = nextInput
 42		sp.tokenOut = nextOutput
 43		sp.fee = nextFee
 44		sp.amountSpecified = amountOut
 45	}
 46}
 47
 48// multiSwapNegative performs a multi-hop exact-output swap. It first quotes
 49// every hop backward, then executes the collected swaps forward.
 50func (r *routerV1) multiSwapNegative(_ int, rlm realm, sp SwapParams, numPools int, swapPath string) (int64, int64) {
 51	swapInfo := r.collectBackwardDrySwapInfo(sp.SingleSwapParams, numPools, swapPath)
 52	return r.executeCollectedSwaps(0, rlm, swapInfo, sp.recipient)
 53}
 54
 55// multiDrySwap simulates a multi-hop exact-input swap in forward order.
 56func (r *routerV1) multiDrySwap(sp SingleSwapParams, numPools int, swapPath string) (int64, int64, error) {
 57	firstAmountIn := int64(0)
 58
 59	for currentPoolIndex := 0; ; currentPoolIndex++ {
 60		amountIn, amountOut := r.swapDryInner(
 61			sp.amountSpecified,
 62			u256.Zero(),
 63			newDrySwapCallbackData(sp),
 64		)
 65
 66		// mirror the real-swap full-consumption guard so dry runs predict reverts
 67		err := validateHopFullyConsumed(currentPoolIndex, sp.amountSpecified, amountIn)
 68		if err != nil {
 69			return 0, 0, err
 70		}
 71
 72		if currentPoolIndex == 0 {
 73			firstAmountIn = amountIn
 74		}
 75
 76		nextPoolIndex := currentPoolIndex + 1
 77		if nextPoolIndex >= numPools {
 78			return firstAmountIn, amountOut, nil
 79		}
 80
 81		nextInput, nextOutput, nextFee := getDataForMultiPath(swapPath, nextPoolIndex)
 82		sp.tokenIn = nextInput
 83		sp.tokenOut = nextOutput
 84		sp.fee = nextFee
 85		sp.amountSpecified = amountOut
 86	}
 87}
 88
 89// multiDrySwapNegative simulates a multi-hop exact-output swap.
 90// It first quotes every hop backward, then simulates the collected swaps forward.
 91func (r *routerV1) multiDrySwapNegative(sp SingleSwapParams, numPools int, swapPath string) (int64, int64, error) {
 92	swapInfo := r.collectBackwardDrySwapInfo(sp, numPools, swapPath)
 93	return r.executeCollectedDrySwaps(swapInfo)
 94}
 95
 96// collectBackwardDrySwapInfo quotes an exact-output route backward before real execution.
 97func (r *routerV1) collectBackwardDrySwapInfo(sp SingleSwapParams, numPools int, swapPath string) []SingleSwapParams {
 98	swapInfo := make([]SingleSwapParams, 0, numPools)
 99
100	for currentPoolIndex := numPools - 1; currentPoolIndex >= 0; currentPoolIndex-- {
101		amountIn, _ := r.singleDrySwap(&sp)
102		swapInfo = append(swapInfo, sp)
103
104		if currentPoolIndex == 0 {
105			break
106		}
107
108		nextPoolIndex := currentPoolIndex - 1
109		nextInput, nextOutput, nextFee := getDataForMultiPath(swapPath, nextPoolIndex)
110		sp.tokenIn = nextInput
111		sp.tokenOut = nextOutput
112		sp.fee = nextFee
113		sp.amountSpecified = -amountIn
114	}
115
116	return swapInfo
117}
118
119// executeCollectedSwaps executes the backward-quoted exact-output swaps in forward order.
120func (r *routerV1) executeCollectedSwaps(_ int, rlm realm, swapInfo []SingleSwapParams, recipient address) (int64, int64) {
121	payer := rlm.Previous().Address()
122	routerAddr := access.MustGetAddress(prbac.ROLE_ROUTER.String())
123	firstAmountIn := int64(0)
124
125	for currentPoolIndex := len(swapInfo) - 1; currentPoolIndex >= 0; currentPoolIndex-- {
126		amountIn, amountOut := r.swapInner(
127			0,
128			rlm,
129			swapInfo[currentPoolIndex].amountSpecified,
130			recipient,
131			u256.Zero(),
132			newSwapCallbackData(swapInfo[currentPoolIndex], payer),
133		)
134
135		// hops re-specified from the previous hop's actual output run as exact-in
136		// (positive amountSpecified) and must consume that amount entirely
137		if spec := swapInfo[currentPoolIndex].amountSpecified; spec > 0 {
138			assertHopFullyConsumed(currentPoolIndex, spec, amountIn)
139		}
140
141		if currentPoolIndex == len(swapInfo)-1 {
142			firstAmountIn = amountIn
143		}
144
145		if currentPoolIndex == 0 {
146			return firstAmountIn, amountOut
147		}
148
149		nextPoolIndex := currentPoolIndex - 1
150		swapInfo[nextPoolIndex].amountSpecified = amountOut
151		payer = routerAddr
152	}
153
154	return firstAmountIn, 0
155}
156
157func (r *routerV1) executeCollectedDrySwaps(swapInfo []SingleSwapParams) (int64, int64, error) {
158	firstAmountIn := int64(0)
159
160	for currentPoolIndex := len(swapInfo) - 1; currentPoolIndex >= 0; currentPoolIndex-- {
161		amountIn, amountOut := r.swapDryInner(
162			swapInfo[currentPoolIndex].amountSpecified,
163			u256.Zero(),
164			newDrySwapCallbackData(swapInfo[currentPoolIndex]),
165		)
166
167		// mirror the real-swap full-consumption guard so dry runs predict reverts
168		if spec := swapInfo[currentPoolIndex].amountSpecified; spec > 0 {
169			err := validateHopFullyConsumed(currentPoolIndex, spec, amountIn)
170			if err != nil {
171				return 0, 0, err
172			}
173		}
174
175		if currentPoolIndex == len(swapInfo)-1 {
176			firstAmountIn = amountIn
177		}
178
179		if currentPoolIndex == 0 {
180			return firstAmountIn, amountOut, nil
181		}
182
183		nextPoolIndex := currentPoolIndex - 1
184		swapInfo[nextPoolIndex].amountSpecified = amountOut
185	}
186
187	return firstAmountIn, 0, nil
188}