| 529 | } |
| 530 | |
| 531 | public static Exception getRootException(Exception exc) { |
| 532 | if (exc == null) { |
| 533 | return null; |
| 534 | } |
| 535 | // Walk the entire cause chain rather than peeking one level. The |
| 536 | // virtual-thread + CompletableFuture pipeline wraps the original |
| 537 | // ccxt exception (AuthenticationError, NotSupported, etc.) under |
| 538 | // CompletionException → RuntimeException, which made the |
| 539 | // `isAuthError`/`isNotSupported` instanceof checks at the call site |
| 540 | // miss every typed ccxt error and report it as TEST_FAILURE instead |
| 541 | // of being skipped per the public-test policy. |
| 542 | Throwable cur = exc; |
| 543 | Throwable last = exc; |
| 544 | int depth = 0; |
| 545 | while (cur != null && depth < 10) { |
| 546 | last = cur; |
| 547 | cur = cur.getCause(); |
| 548 | depth++; |
| 549 | } |
| 550 | return (last instanceof Exception) ? (Exception) last : exc; |
| 551 | } |
| 552 | |
| 553 | public static Object jsonParse(Object jsonString) { |
| 554 | return JsonHelper.deserialize((String) jsonString); |