connext / connext/chain-abstraction-reference
Destination swap uses amountOutMin = 0, so the displayed quote is not enforced
- Dominant language
- Solidity
- Stars
- 4
- Forks
- 6
- PR merge metrics
- No merged PRs in 30d
Description
Destination swap is prepared with `amountOutMin: "0"`, so the user's displayed quote is not enforced
Hi! I found a quote/settlement mismatch in the reference frontend flow.
The UI requests an estimated amount received and displays it to the user, but the actual destination swap calldata is built with `amountOutMin: "0"`. This means the destination Uniswap swap can execute with any nonzero or dust output, even when the displayed quote / expected receive amount is much higher.
Relevant code:
```ts
// packages/frontend/utils/handleGreetHelper.ts
55 const params: DestinationCallDataParams = {
56 fallback: address as `0x${string}`,
57 swapForwarderData: {
58 toAsset: destinationDesiredAsset,
59 swapData: {
60 amountOutMin: "0",
61 poolFee,
62 },
63 },
64 };
...
76 const swapAndXCallParams: SwapAndXCallParams = {
77 originDomain,
78 destinationDomain,
79 fromAsset:
80 originTransactingAsset === ARBITRUM_PROTOCOL_TOKEN_ADDRESS
81 ? constants.AddressZero
82 : originTransactingAsset,
83 toAsset: originUSDC,
84 amountIn: amountIn.toString(),
85 to: POLYGON_ADAPTER_CONTRACT,
86 relayerFeeInNativeAsset: relayerFee,
87 callData: xCallData,
88 };
89 console.log("swapAndXCallParams: ", swapAndXCallParams);
90
91 const txRequest = await connextService.prepareSwapAndXCallHelper(
92 swapAndXCallParams,
93 address as Hex,
94 );
```
The quote path separately fetches and displays an estimated amount:
```tsx
// packages/frontend/pages/index.tsx
305 const quoteAmount =
306 await connextService.getEstimateAmountReceivedHelper({
307 originDomain,
308 destinationDomain,
309 amountIn: utils
310 .parseUnits(amountIn.toString(), selectedAsset?.decimals)
...
323 console.log("amount received: ", quoteAmount);
324 if (quoteAmount) {
325 setQuotedAmountOut(quoteAmount);
```
Impact:
- The user's quote preview and the submitted route have different slippage semantics.
- Any adverse price movement, bad pool selection, or manipulated execution can still pass the destination swap because the minimum output is zero.
- The fallback address only helps if the call fails; with `amountOutMin = 0`, a terrible swap can succeed instead of refunding/fallbacking.
Expected invariant:
The minimum destination output used in the actual route should be derived from the quoted amount and the user's slippage tolerance, and it should be passed into `swapForwarderData.swapData.amountOutMin`.
Suggested fix:
- Compute `amountOutMin = quoteAmount * (1 - slippageBps / 10_000)` using integer math.
- Include the slippage/min-output value in the UI state the user confirms.
- Reject submit if the quote has expired or if a fresh quote differs materially from the confirmed one.
- Add a test asserting `handleGreetHelper` never passes `"0"` for `amountOutMin` when a destination swap is required.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in packages/frontend/utils/handleGreetHelper.ts at the destination swapData construction, then trace the quote and confirmation state in packages/frontend/pages/index.tsx. Verify how the quoted amount and slippage tolerance can reach transaction preparation. Done means the submitted route uses the confirmed minimum output, rejects stale or materially changed quotes, and includes a test that prevents zero amountOutMin for required destination swaps.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 62/100