connext / connext/chain-abstraction-reference

Destination swap uses amountOutMin = 0, so the displayed quote is not enforced

Open
#13 0 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.