@@ -55,27 +55,50 @@ const networkLabels: Record<CoinName, string> = {
5555} ;
5656
5757/**
58- * Decode hex or base64 input to bytes .
58+ * Try to decode input as hex .
5959 */
60- function decodeInput ( input : string ) : Uint8Array {
60+ function tryDecodeHex ( input : string ) : Uint8Array | null {
6161 const trimmed = input . trim ( ) ;
62+ if ( ! / ^ [ 0 - 9 a - f A - F ] * $ / . test ( trimmed ) || trimmed . length % 2 !== 0 ) {
63+ return null ;
64+ }
65+ const bytes = new Uint8Array ( trimmed . length / 2 ) ;
66+ for ( let i = 0 ; i < bytes . length ; i ++ ) {
67+ bytes [ i ] = parseInt ( trimmed . slice ( i * 2 , i * 2 + 2 ) , 16 ) ;
68+ }
69+ return bytes ;
70+ }
6271
63- // Try hex first
64- if ( / ^ [ 0 - 9 a - f A - F ] + $ / . test ( trimmed ) ) {
65- const bytes = new Uint8Array ( trimmed . length / 2 ) ;
66- for ( let i = 0 ; i < bytes . length ; i ++ ) {
67- bytes [ i ] = parseInt ( trimmed . slice ( i * 2 , i * 2 + 2 ) , 16 ) ;
72+ /**
73+ * Try to decode input as base64.
74+ */
75+ function tryDecodeBase64 ( input : string ) : Uint8Array | null {
76+ const trimmed = input . trim ( ) ;
77+ try {
78+ const binary = atob ( trimmed ) ;
79+ const bytes = new Uint8Array ( binary . length ) ;
80+ for ( let i = 0 ; i < binary . length ; i ++ ) {
81+ bytes [ i ] = binary . charCodeAt ( i ) ;
6882 }
6983 return bytes ;
84+ } catch {
85+ return null ;
7086 }
87+ }
7188
72- // Try base64
73- const binary = atob ( trimmed ) ;
74- const bytes = new Uint8Array ( binary . length ) ;
75- for ( let i = 0 ; i < binary . length ; i ++ ) {
76- bytes [ i ] = binary . charCodeAt ( i ) ;
77- }
78- return bytes ;
89+ /**
90+ * Get all possible decodings of the input (hex, base64).
91+ */
92+ function possibleDecodings ( input : string ) : Uint8Array [ ] {
93+ const decodings : Uint8Array [ ] = [ ] ;
94+
95+ const hex = tryDecodeHex ( input ) ;
96+ if ( hex ) decodings . push ( hex ) ;
97+
98+ const base64 = tryDecodeBase64 ( input ) ;
99+ if ( base64 ) decodings . push ( base64 ) ;
100+
101+ return decodings ;
79102}
80103
81104/**
@@ -1076,77 +1099,95 @@ class PsbtTxParser extends BaseComponent {
10761099 this . expandedPaths = new Set ( [ "root" ] ) ;
10771100 this . expandedValues = new Set ( ) ;
10781101
1079- let bytes : Uint8Array ;
1080- try {
1081- bytes = decodeInput ( input ) ;
1082- } catch ( e ) {
1083- errorEl . replaceChildren ( h ( "div" , { class : "error-message" } , `Failed to decode input: ${ e } ` ) ) ;
1102+ // Get all possible decodings (hex, base64)
1103+ const decodings = possibleDecodings ( input ) ;
1104+ if ( decodings . length === 0 ) {
1105+ errorEl . replaceChildren (
1106+ h ( "div" , { class : "error-message" } , "Failed to decode input: not valid hex or base64" ) ,
1107+ ) ;
10841108 resultsEl . replaceChildren (
10851109 h ( "div" , { class : "empty-state" } , h ( "p" , { } , "Enter valid hex or base64 data" ) ) ,
10861110 ) ;
10871111 return ;
10881112 }
10891113
1090- // Auto-detect type (PSBT vs TX)
1091- const detectedPsbt = isPsbt ( bytes ) ;
1092-
10931114 // Parse based on mode with network handling
1094- let node : Node ;
1115+ // Try all decodings and pick the first one that parses successfully
1116+ let node : Node | null = null ;
10951117 let detectedNetwork : CoinName | null = null ;
1096-
1097- try {
1098- if ( this . autoDetectNetwork ) {
1099- // Try all networks and pick the first one that works
1100- if ( this . currentMode === "psbt" ) {
1101- const result = tryParsePsbt ( bytes ) ;
1102- if ( result ) {
1103- detectedNetwork = result . network ;
1104- this . currentNetwork = result . network ;
1105- node = result . node ;
1118+ let successBytes : Uint8Array | null = null ;
1119+ let lastError : string | null = null ;
1120+
1121+ for ( const bytes of decodings ) {
1122+ try {
1123+ if ( this . autoDetectNetwork ) {
1124+ // Try all networks and pick the first one that works
1125+ if ( this . currentMode === "psbt" ) {
1126+ const result = tryParsePsbt ( bytes ) ;
1127+ if ( result ) {
1128+ detectedNetwork = result . network ;
1129+ this . currentNetwork = result . network ;
1130+ node = result . node ;
1131+ successBytes = bytes ;
1132+ break ;
1133+ }
1134+ } else if ( this . currentMode === "psbt-raw" ) {
1135+ const result = tryParsePsbtRaw ( bytes ) ;
1136+ if ( result ) {
1137+ detectedNetwork = result . network ;
1138+ this . currentNetwork = result . network ;
1139+ node = result . node ;
1140+ successBytes = bytes ;
1141+ break ;
1142+ }
11061143 } else {
1107- throw new Error ( "Failed to parse PSBT with any known network" ) ;
1108- }
1109- } else if ( this . currentMode === "psbt-raw" ) {
1110- const result = tryParsePsbtRaw ( bytes ) ;
1111- if ( result ) {
1112- detectedNetwork = result . network ;
1113- this . currentNetwork = result . network ;
1114- node = result . node ;
1115- } else {
1116- throw new Error ( "Failed to parse raw PSBT with any known network" ) ;
1144+ const result = tryParseTx ( bytes ) ;
1145+ if ( result ) {
1146+ detectedNetwork = result . network ;
1147+ this . currentNetwork = result . network ;
1148+ node = result . node ;
1149+ successBytes = bytes ;
1150+ break ;
1151+ }
11171152 }
11181153 } else {
1119- const result = tryParseTx ( bytes ) ;
1120- if ( result ) {
1121- detectedNetwork = result . network ;
1122- this . currentNetwork = result . network ;
1123- node = result . node ;
1124- } else {
1125- throw new Error ( "Failed to parse transaction with any known network" ) ;
1154+ // Use the specified network
1155+ switch ( this . currentMode ) {
1156+ case "psbt" :
1157+ node = parsePsbtToNode ( bytes , this . currentNetwork ) ;
1158+ break ;
1159+ case "psbt-raw" :
1160+ node = parsePsbtRawToNode ( bytes , this . currentNetwork ) ;
1161+ break ;
1162+ case "tx" :
1163+ node = parseTxToNode ( bytes , this . currentNetwork ) ;
1164+ break ;
11261165 }
1127- }
1128- } else {
1129- // Use the specified network
1130- switch ( this . currentMode ) {
1131- case "psbt" :
1132- node = parsePsbtToNode ( bytes , this . currentNetwork ) ;
1133- break ;
1134- case "psbt-raw" :
1135- node = parsePsbtRawToNode ( bytes , this . currentNetwork ) ;
1136- break ;
1137- case "tx" :
1138- node = parseTxToNode ( bytes , this . currentNetwork ) ;
1166+ if ( node ) {
1167+ successBytes = bytes ;
11391168 break ;
1169+ }
11401170 }
1171+ } catch ( e ) {
1172+ lastError = String ( e ) ;
1173+ // Continue trying other decodings
11411174 }
1142- } catch ( e ) {
1143- errorEl . replaceChildren ( h ( "div" , { class : "error-message" } , `Parse error: ${ e } ` ) ) ;
1175+ }
1176+
1177+ if ( ! node ) {
1178+ const errorMsg = lastError
1179+ ? `Parse error (tried hex and base64): ${ lastError } `
1180+ : "Failed to parse with any encoding" ;
1181+ errorEl . replaceChildren ( h ( "div" , { class : "error-message" } , errorMsg ) ) ;
11441182 resultsEl . replaceChildren (
11451183 h ( "div" , { class : "empty-state" } , h ( "p" , { } , "Failed to parse data" ) ) ,
11461184 ) ;
11471185 return ;
11481186 }
11491187
1188+ // Auto-detect type (PSBT vs TX) based on successfully decoded bytes
1189+ const detectedPsbt = successBytes ? isPsbt ( successBytes ) : false ;
1190+
11501191 // Update detected type display
11511192 const typeStr = detectedPsbt ? "PSBT" : "Transaction" ;
11521193 const networkStr = detectedNetwork
0 commit comments