Commit 0b20391
authored
fix: Fix stuck pending withdraw (MetaMask#24214)
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->
## **Description**
Withdrawal indicators in Perps could get stuck in "pending" or
"bridging" state indefinitely, even after the withdrawal had
successfully completed on-chain. This created a poor user experience
where users saw stale pending indicators that never resolved.
**Solution**
Enhanced the 1useWithdrawalRequests1 hook to automatically reconcile
pending withdrawal states with completed withdrawals from the
HyperLiquid API:
1. Auto-reconciliation: When a completed withdrawal from the API matches
a pending one in controller state, the controller is automatically
updated to reflect the completion
2. Active polling: Polls the HyperLiquid ledger API every 10 seconds
when there are active (pending/bridging) withdrawals to detect
completions promptly
3. Relaxed flexible matching: Matches withdrawals by amount (±$0.01
tolerance) and asset type; removed timestamp constraint since bridging
operations can sometimes take hours
4. Deduplication tracking: Uses a ref to track which withdrawals have
already been updated, preventing duplicate updateWithdrawalStatus calls
Account isolation: Filters withdrawals by the current selected account
to prevent showing stale indicators from other accounts
## **Changelog**
CHANGELOG entry: Fixed an issue where Perps withdrawal indicators could
remain stuck in "pending" state after the withdrawal completed
## **Related issues**
Fixes: https://consensyssoftware.atlassian.net/browse/TAT-2128
## **Manual testing steps**
```gherkin
Feature: Perps Withdrawal Status Resolution
Scenario: Pending withdrawal resolves to completed
Given user has an active Perps account with USDC balance
And user initiates a withdrawal from Perps
When the withdrawal completes on HyperLiquid (wait up to 10 seconds for poll)
Then the withdrawal status updates from "pending" or "bridging" to "completed"
And the withdrawal displays the transaction hash
Scenario: Pending indicators clear on account switch
Given user has a pending withdrawal on Account A
And user switches to Account B which has no withdrawals
When the withdrawal list renders
Then no pending withdrawal indicators are shown for Account B
Scenario: Completed withdrawals persist after app restart
Given user had a withdrawal complete while the app was closed
When user opens the app and navigates to Perps withdrawals
Then the withdrawal shows as "completed" (fetched from HyperLiquid API)
And no stale "pending" indicator is displayed
```
## **Screenshots/Recordings**
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
<!-- [screenshots/recordings] -->
### **After**
<!-- [screenshots/recordings] -->
## **Pre-merge author checklist**
- [x] I’ve followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Resolves stuck pending/bridging withdrawal indicators by reconciling
controller state with HyperLiquid ledger completions.
>
> - Reworks `useWithdrawalRequests` to merge pending with completed
withdrawals, matching by `amount` (±0.01) and `asset` and removing
timestamp constraints; requires `txHash` for completed matches
> - Moves side-effectful controller updates into `useEffect` and adds
`updatedWithdrawalIdsRef` to prevent duplicate `updateWithdrawalStatus`
calls
> - Adds polling every 10s when active withdrawals exist; maintains
sorting by timestamp (newest first) and filters by selected account
> - Updates tests to cover long-bridge timestamp gaps, small amount
tolerances, txHash requirement, polling behavior, and error handling
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
836ad08. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent dc5cb66 commit 0b20391
2 files changed
Lines changed: 154 additions & 79 deletions
Lines changed: 16 additions & 8 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
675 | 675 | | |
676 | 676 | | |
677 | 677 | | |
678 | | - | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
679 | 681 | | |
680 | | - | |
681 | | - | |
| 682 | + | |
| 683 | + | |
682 | 684 | | |
683 | 685 | | |
684 | 686 | | |
| |||
701 | 703 | | |
702 | 704 | | |
703 | 705 | | |
704 | | - | |
| 706 | + | |
705 | 707 | | |
706 | 708 | | |
707 | 709 | | |
| |||
714 | 716 | | |
715 | 717 | | |
716 | 718 | | |
717 | | - | |
718 | | - | |
| 719 | + | |
| 720 | + | |
719 | 721 | | |
720 | 722 | | |
721 | | - | |
722 | | - | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
723 | 731 | | |
724 | 732 | | |
725 | 733 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| |||
200 | 200 | | |
201 | 201 | | |
202 | 202 | | |
203 | | - | |
204 | | - | |
205 | | - | |
206 | | - | |
207 | | - | |
208 | | - | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
214 | | - | |
215 | | - | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
216 | 206 | | |
217 | | - | |
218 | | - | |
219 | | - | |
220 | | - | |
221 | | - | |
222 | | - | |
223 | | - | |
224 | | - | |
225 | | - | |
226 | | - | |
227 | | - | |
228 | | - | |
229 | | - | |
230 | | - | |
231 | | - | |
232 | | - | |
233 | | - | |
234 | | - | |
235 | | - | |
236 | | - | |
237 | | - | |
238 | | - | |
239 | | - | |
240 | | - | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
241 | 243 | | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
247 | | - | |
248 | | - | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
258 | | - | |
259 | | - | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
266 | 244 | | |
267 | | - | |
268 | | - | |
| 245 | + | |
| 246 | + | |
269 | 247 | | |
270 | 248 | | |
271 | | - | |
272 | | - | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
273 | 270 | | |
274 | 271 | | |
275 | | - | |
276 | | - | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
277 | 276 | | |
278 | | - | |
279 | | - | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
280 | 280 | | |
281 | | - | |
| 281 | + | |
| 282 | + | |
282 | 283 | | |
283 | 284 | | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
284 | 351 | | |
285 | 352 | | |
286 | 353 | | |
| |||
0 commit comments