@@ -1047,3 +1047,201 @@ export async function recoveryBlockchainExplorerQuery(
10471047export function getDefaultExpireTime ( ) : number {
10481048 return Math . floor ( new Date ( ) . getTime ( ) / 1000 ) + 60 * 60 * 24 * 7 ;
10491049}
1050+
1051+ export async function recovery_HBAREVM_BlockchainExplorerQuery (
1052+ query : Record < string , string > ,
1053+ rpcUrl : string ,
1054+ explorerUrl : string ,
1055+ token ?: string
1056+ ) : Promise < Record < string , unknown > > {
1057+ // Hedera Mirror Node API does not use API keys, but we keep this for compatibility
1058+ if ( token ) {
1059+ query . apikey = token ;
1060+ }
1061+
1062+ const { module, action } = query ;
1063+
1064+ // Remove trailing slash from explorerUrl if present
1065+ const baseUrl = explorerUrl . replace ( / \/ $ / , '' ) ;
1066+
1067+ try {
1068+ switch ( `${ module } .${ action } ` ) {
1069+ case 'account.balance' :
1070+ return await queryAddressBalanceHedera ( query , baseUrl ) ;
1071+
1072+ case 'account.txlist' :
1073+ return await getAddressNonceHedera ( query , baseUrl ) ;
1074+
1075+ case 'account.tokenbalance' :
1076+ return await queryTokenBalanceHedera ( query , baseUrl ) ;
1077+
1078+ case 'proxy.eth_gasPrice' :
1079+ return await getGasPriceHedera ( query , rpcUrl ) ;
1080+
1081+ case 'proxy.eth_estimateGas' :
1082+ return await getGasLimitHedera ( query , rpcUrl ) ;
1083+
1084+ case 'proxy.eth_call' :
1085+ return await querySequenceIdHedera ( query , rpcUrl ) ;
1086+
1087+ default :
1088+ throw new Error ( `Unsupported API call: ${ module } .${ action } ` ) ;
1089+ }
1090+ } catch ( error ) {
1091+ throw error ;
1092+ }
1093+ }
1094+
1095+ /**
1096+ * 1. Gets address balance using Hedera Mirror Node API
1097+ */
1098+ async function queryAddressBalanceHedera (
1099+ query : Record < string , string > ,
1100+ baseUrl : string
1101+ ) : Promise < Record < string , unknown > > {
1102+ const address = query . address ;
1103+ const url = `${ baseUrl } /accounts/${ address } ` ;
1104+ const response = await request . get ( url ) . send ( ) ;
1105+
1106+ if ( ! response . ok ) {
1107+ throw new Error ( 'could not reach explorer' ) ;
1108+ }
1109+
1110+ const balance = response . body . balance ?. balance || '0' ;
1111+
1112+ // Convert from tinybars to wei (1 HBAR = 10^8 tinybars, 1 HBAR = 10^18 wei)
1113+ // So: wei = tinybars * 10^10
1114+ const balanceInWei = ( BigInt ( balance ) * BigInt ( '10000000000' ) ) . toString ( ) ;
1115+
1116+ return { result : balanceInWei } ;
1117+ }
1118+
1119+ /**
1120+ * 2. Gets nonce using Hedera Mirror Node API
1121+ */
1122+ async function getAddressNonceHedera ( query : Record < string , string > , baseUrl : string ) : Promise < Record < string , unknown > > {
1123+ const address = query . address ;
1124+ const accountUrl = `${ baseUrl } /accounts/${ address } ` ;
1125+ const response = await request . get ( accountUrl ) . send ( ) ;
1126+
1127+ if ( ! response . ok ) {
1128+ throw new Error ( 'could not reach explorer' ) ;
1129+ }
1130+
1131+ const nonce = response . body . ethereum_nonce || 0 ;
1132+
1133+ return { nonce : nonce } ;
1134+ }
1135+
1136+ /**
1137+ * 3. Gets token balance using Hedera Mirror Node API
1138+ */
1139+ async function queryTokenBalanceHedera (
1140+ query : Record < string , string > ,
1141+ baseUrl : string
1142+ ) : Promise < Record < string , unknown > > {
1143+ const contractAddress = query . contractaddress ;
1144+ const address = query . address ;
1145+
1146+ // Get token balances for the account
1147+ const url = `${ baseUrl } /accounts/${ address } /tokens` ;
1148+ const response = await request . get ( url ) . send ( ) ;
1149+
1150+ if ( ! response . ok ) {
1151+ throw new Error ( 'could not reach explorer' ) ;
1152+ }
1153+
1154+ // Find the specific token balance
1155+ const tokens = response . body . tokens || [ ] ;
1156+ const tokenBalance = tokens . find (
1157+ ( token : { token_id : string ; contract_address : string ; balance : number } ) =>
1158+ token . token_id === contractAddress || token . contract_address === contractAddress
1159+ ) ;
1160+
1161+ const balance = tokenBalance ? tokenBalance . balance . toString ( ) : '0' ;
1162+ // Convert from tinybars to wei (1 HBAR = 10^8 tinybars, 1 HBAR = 10^18 wei)
1163+ // So: wei = tinybars * 10^10
1164+ const balanceInWei = ( BigInt ( balance ) * BigInt ( '10000000000' ) ) . toString ( ) ;
1165+
1166+ return { result : balanceInWei } ;
1167+ }
1168+
1169+ /**
1170+ * 4. Gets sequence ID using Hedera Mirror Node API or rpc call
1171+ */
1172+ async function querySequenceIdHedera ( query : Record < string , string > , rpcUrl : string ) : Promise < Record < string , unknown > > {
1173+ const { to, data } = query ;
1174+
1175+ const url = rpcUrl ;
1176+
1177+ const requestBody = {
1178+ jsonrpc : '2.0' ,
1179+ method : 'eth_call' ,
1180+ params : [
1181+ {
1182+ to : to ,
1183+ data : data ,
1184+ } ,
1185+ ] ,
1186+ id : 1 ,
1187+ } ;
1188+
1189+ const response = await request . post ( url ) . send ( requestBody ) . set ( 'Content-Type' , 'application/json' ) ;
1190+
1191+ if ( ! response . ok ) {
1192+ throw new Error ( 'could not fetch from rpc url' ) ;
1193+ }
1194+
1195+ return response . body ;
1196+ }
1197+
1198+ /**
1199+ * 5. getGasPriceFromExternalAPI - Gets gas price using Hedera Mirror Node API
1200+ */
1201+ async function getGasPriceHedera ( query : Record < string , string > , rpcUrl : string ) : Promise < Record < string , unknown > > {
1202+ const url = rpcUrl ;
1203+
1204+ const requestBody = {
1205+ jsonrpc : '2.0' ,
1206+ method : 'eth_gasPrice' ,
1207+ params : [ ] ,
1208+ id : 1 ,
1209+ } ;
1210+
1211+ const response = await request . post ( url ) . send ( requestBody ) . set ( 'Content-Type' , 'application/json' ) ;
1212+
1213+ if ( ! response . ok ) {
1214+ throw new Error ( 'could not fetch from rpc url' ) ;
1215+ }
1216+
1217+ return response . body ;
1218+ }
1219+
1220+ /**
1221+ * 6. getGasLimitFromExternalAPI - Gets gas limit using Hedera Mirror Node API
1222+ */
1223+ async function getGasLimitHedera ( query : Record < string , string > , rpcUrl : string ) : Promise < Record < string , unknown > > {
1224+ const url = rpcUrl ;
1225+
1226+ const { from, to, data } = query ;
1227+
1228+ const requestBody = {
1229+ jsonrpc : '2.0' ,
1230+ method : 'eth_estimateGas' ,
1231+ params : [
1232+ {
1233+ from,
1234+ to,
1235+ data,
1236+ } ,
1237+ ] ,
1238+ id : 1 ,
1239+ } ;
1240+ const response = await request . post ( url ) . send ( requestBody ) . set ( 'Content-Type' , 'application/json' ) ;
1241+
1242+ if ( ! response . ok ) {
1243+ throw new Error ( 'could not fetch from rpc url' ) ;
1244+ }
1245+
1246+ return response . body ;
1247+ }
0 commit comments