| 727 | } |
| 728 | |
| 729 | public Object exceptionMessage(Object exc, Object... optionalArgs) { |
| 730 | boolean includeStack = optionalArgs.length > 0 && optionalArgs[0] != null ? (boolean) optionalArgs[0] : true; |
| 731 | if (exc instanceof Throwable t) { |
| 732 | // Walk the cause chain and include each level's class+message. Without |
| 733 | // this, reflection-driven failures show as bare "InvocationTargetException" |
| 734 | // and the real exchange/parser error is hidden one or two `getCause()` levels |
| 735 | // deep — which made bitmex, deribit, etc. unfixable from logs alone. |
| 736 | StringBuilder sb = new StringBuilder(); |
| 737 | Throwable cur = t; |
| 738 | int depth = 0; |
| 739 | while (cur != null && depth < 5) { |
| 740 | if (depth > 0) sb.append(" → caused by: "); |
| 741 | sb.append("[").append(cur.getClass().getSimpleName()).append("] "); |
| 742 | sb.append(includeStack ? cur.toString() : cur.getMessage()); |
| 743 | cur = cur.getCause(); |
| 744 | depth++; |
| 745 | } |
| 746 | String message = sb.toString(); |
| 747 | int length = Math.min(100000, message.length()); |
| 748 | return message.substring(0, length); |
| 749 | } |
| 750 | return String.valueOf(exc); |
| 751 | } |
| 752 | |
| 753 | public Object ethGetAddressFromPrivateKey(Object privateKey) { |
| 754 | try { |