QA report: external/trade.achswap.app at hosted
Token swap submission remains enabled for amounts exceeding wallet balances, triggering failed simulation transactions.
Testing exercised the core decentralized exchange workflows on the application, including token swaps, liquidity pool deposits, input validations, UI controls, and settings persistence across twelve scenarios. Eight scenarios passed, but three scenarios involving liquidity removal and bridge rejection handling were blocked due to the absence of existing liquidity positions and network funds in the test wallet.
One functional issue was confirmed in balance handling: the swap form fails to disable the submission button when the requested swap amount exceeds the available wallet balance, permitting users to submit transactions that immediately fail during simulation. A second finding about the MAX shortcut was withdrawn in review, because USDC is the native gas token on Arc and reserving gas there is correct.
These flaws introduce confusing user experiences and avoidable transaction failures during swap execution. Core swap and liquidity addition paths function when inputs are valid, but balance validation and shortcut helpers require correction before release.
Run summary
| Metric | Count |
|---|
| Scenarios executed | 12 |
| Passed | 8 |
| Failed | 1 |
| Blocked | 3 |
| Findings raised | 1 |
| Issues after the audit | 2 |
| Withdrawn by the audit | 0 |
| Critical / high / medium / low | 0 / 1 / 1 / 0 |
Target: https://trade.achswap.app · Testing level: deep_feature · Stack: unknown
Issues
High severity
F1 · Swap submission button remains enabled when input amount exceeds wallet balance
Severity: high · Type: functional · Verdict: confirmed · Scenario: S6
When entering an input amount of 100 USDC which exceeds the current wallet balance of 1.9963 USDC, the 'Swap' button becomes active instead of indicating an insufficient balance or becoming disabled.
Expected: The Swap button should be disabled and display text indicating insufficient balance (e.g. 'Insufficient USDC balance'), preventing submission of an amount exceeding wallet balance.
Actual: The Swap button becomes active with the label 'Swap', allowing the user to click it and trigger a failing transaction that results in a 'Simulation Failed' error.
Steps to reproduce:
- Navigate to Swap page (/)
- Ensure USDC is selected as the input token (wallet balance ~1.9963 USDC)
- Enter 100 in the input amount spinbutton
- Observe the Swap button state and text
Evidence: screenshots/S6-4.png, screenshots/S6-6.png, screenshots/S6-8.png
Medium severity
F2 · MAX button incorrectly deducts native gas reserve from ERC-20 balance (withdrawn in review)
Severity: medium · Type: functional · Verdict: withdrawn · Scenario: S7
Review note (2026-09-16): Withdrawn in human review on 2026-09-16: on Arc, USDC is the native gas token, and the 1.9963 balance shown is the wallet's native balance, so the MAX button correctly reserves gas before populating the input. The Critic's premise that USDC is an ERC-20 token does not hold on this chain.
Surfaced by the audit of S7, which the Tester passed, and reproduced live: Clicked the 'MAX' button while USDC (an ERC-20 token) was selected as input. The input field was populated with 1.9566 instead of the full available balance of 1.9963, showing that a native gas reserve was incorrectly deducted from the ERC-20 token [redacted]
Expected: The MAX button should populate the input with the full available balance of the ERC-20 token (1.9963 USDC).
Actual: The MAX button populates a reduced amount (1.9566 USDC), incorrectly applying native token gas reservation logic to an ERC-20 token.
Steps to reproduce:
- Navigate to the Swap page.
- Select an ERC-20 token (USDC) as the input.
- Click the MAX balance button.
Evidence: screenshots/critic-M1-1.png
Scenario results
| Scenario | Priority | Result | Issues |
|---|
| S1 Execute a token swap | high | pass | none |
| S2 Add liquidity to a pool | high | pass | none |
| S3 Remove liquidity from a position | high | blocked (The test wallet does not have any active liquidity positions (V2, V3, or V4) to remove.) | none |
| S4 Bridge network switch rejection handling | high | blocked (Unable to initiate the bridge transaction from Base Sepolia because the connected test wallet has 0.0000 USDC balance on that chain, causing the bridge button to be disabled with 'Insufficient USDC Balance'.) | none |
| S5 Swap configuration settings persistence | medium | pass | none |
| S6 Validation for insufficient swap balance | medium | fail | F1 |
| S7 Max balance populates input correctly | medium | pass | F2 |
| S8 Toggle auxiliary UI elements (Gasless and Chart) | medium | pass | none |
| S9 Add Liquidity missing input validation | medium | pass | none |
| S10 Navigation link integrity | low | pass | none |
| S11 Swap zero input validation | low | pass | none |
| S12 Remove Liquidity disabled on 0% | low | blocked (No LP positions exist for the connected wallet on /remove-liquidity, preventing selection of an LP position to test 0% removal.) | none |
The audit
The Critic reviewed 1 finding and re-verified 2 of them live in the browser, replaying the reported steps on a fresh page.
- Three scenarios (S3, S4, S12) were blocked because the test wallet lacked the necessary LP positions or token [redacted] on the required networks, indicating a gap in test data preparation.
- The application inconsistently handles insufficient balances; the Bridge form disables the submit button properly (S4), while the Swap form does not (S6/F1).
What to fix first
- Disable the Swap submission button and indicate insufficient balance when input amounts exceed the wallet balance (F1).
- (Withdrawn in review) F2 on the MAX button: USDC is Arc's native gas token, so the reserve is correct.
Coverage and caveats
In scope: Core AMM V2/V3/V4 swap functionalities; Swap configuration and UI toggles; Adding and removing liquidity across versions; Cross-chain bridge form interactions and network switch rejection handling; Client-side input validations.
Not covered: End-to-end CCTP cross-chain bridge completion to destination networks, as the pre-configured test wallet rejects non-Arc chains and destination actions fall outside single-chain control.; Blockscout trade history chart rendering fidelity, due to known HTTP 429 rate limits on the testnet API..
- The pre-injected test wallet auto-approves or auto-rejects transactions without requiring manual window switching by the Tester.
- Tokens such as ACHS are available for selection in the token modals.
- The 'Remove Liquidity' scenario assumes a liquidity position was successfully created in the 'Add Liquidity' step, or pre-exists for the test wallet.
- The 404 console error observed in the AppMap does not block core UI rendering or transaction submission.
- S3 could not be executed: The test wallet does not have any active liquidity positions (V2, V3, or V4) to remove..
- S4 could not be executed: Unable to initiate the bridge transaction from Base Sepolia because the connected test wallet has 0.0000 USDC balance on that chain, causing the bridge button to be disabled with 'Insufficient USDC Balance'..
- S12 could not be executed: No LP positions exist for the connected wallet on /remove-liquidity, preventing selection of an LP position to test 0% removal..
By the numbers
| Metric | Value |
|---|
| Scenarios | 8 passed, 1 failed, 3 blocked of 12 (46 planned steps) |
| Screenshots | 42 (4 explore, 36 scenario, 2 critic), 36 captioned |
| Coverage | 4 pages, 4 forms, 4 flows, 1 console errors |
| Audit | 1 findings, 2 re-verified live, 1 confirmed, 0 promoted, 1 withdrawn |
| Model calls | 218 |
| Tokens | 930,049 input, 10,125 output, 13,693 thinking |
| Time | 11 min |
| Wallet | 2 transactions, 0 signatures, 0 refusals on chain 5042002 |
| Stage | Calls | Input | Output | Thinking | Seconds |
|---|
| explore | 32 | 163,761 | 2,344 | 1,441 | 129 |
| plan | 1 | 4,017 | 2,081 | 2,039 | 32 |
| test | 176 | 742,639 | 4,771 | 7,023 | 473 |
| critique | 8 | 17,967 | 648 | 2,827 | 47 |
| report | 1 | 1,665 | 281 | 363 | 6 |