From a6090809325982645d96a6ae755faf8f23689ff1 Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Thu, 26 Mar 2026 11:37:10 +0100 Subject: [PATCH 1/2] new: add ``blockNb`` parameter support to balance query Allow API callers to specify which block number to query the balance at via the ``blockNb`` request parameter. Defaults to ``"pending"`` preserving current behavior. --- api.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/api.php b/api.php index 3f67e8d..bd60543 100644 --- a/api.php +++ b/api.php @@ -7,7 +7,8 @@ $gethRPC = new jsonRPCClient('http://127.0.0.1:8545'); if(isset($_REQUEST['balance'])){ header('Content-Type: application/json'); - echo getEthBalance($_REQUEST['balance'],$gethRPC); + $blockNb = isset($_REQUEST['blockNb']) ? $_REQUEST['blockNb'] : "pending"; + echo getEthBalance($_REQUEST['balance'],$gethRPC,$blockNb); } else if(isset($_REQUEST['rawtx'])){ header('Content-Type: application/json'); echo sendRawTransaction($_REQUEST['rawtx'],$gethRPC); @@ -156,13 +157,13 @@ function getTransactionData($addr, $gethRPC){ } return json_encode($data); } -function getEthBalance($addr, $gethRPC) +function getEthBalance($addr, $gethRPC, $blockNb = "pending") { $data = getDefaultResponse(); try { $addr = formatAddress($addr); $balancehex = getRPCResponse($gethRPC->eth_getBalance($addr, - "pending")); + $blockNb)); $balance=bchexdec($balancehex); $tarr['address'] = $addr; $tarr['balance'] = $balance; From 64d6dbe11585a5e63653ab9fd855e87df61f811c Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Thu, 26 Mar 2026 11:37:13 +0100 Subject: [PATCH 2/2] new: add ``batch.php`` batch JSON-RPC endpoint Accepts a JSON array of operations, aggregates them into a single batched geth RPC call, and maps responses back. Supports ``balance``, ``ethCall``, ``ethCallAt``, ``estimatedGas``, ``blockByNumber``, ``blockNumber``. Rejects write operations (``rawtx``, ``txdata``, ``hash``). --- batch.php | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 batch.php diff --git a/batch.php b/batch.php new file mode 100644 index 0000000..ffe5359 --- /dev/null +++ b/batch.php @@ -0,0 +1,141 @@ + true, 'msg' => 'Expected JSON array']); + exit; +} + +$REJECTED = ['rawtx', 'txdata', 'hash']; + +$requests = []; +$mapping = []; // tracks which input index maps to which RPC call(s) + +foreach ($input as $idx => $item) { + // Check for rejected operations + foreach ($REJECTED as $key) { + if (isset($item[$key])) { + $mapping[$idx] = ['rejected' => "Operation '$key' is not supported in batch"]; + continue 2; + } + } + + if (isset($item['balance'])) { + $addr = formatAddress($item['balance']); + $blockNb = isset($item['blockNb']) ? $item['blockNb'] : 'pending'; + $mapping[$idx] = ['rpc_index' => count($requests), 'type' => 'balance', 'addr' => $addr]; + $requests[] = [ + 'method' => 'eth_getBalance', + 'params' => [$addr, $blockNb], + ]; + } else if (isset($item['ethCallAt']) && isset($item['blockNb'])) { + $tx = [ + 'to' => $item['ethCallAt']['to'], + 'data' => $item['ethCallAt']['data'], + ]; + $mapping[$idx] = ['rpc_index' => count($requests), 'type' => 'ethCall']; + $requests[] = [ + 'method' => 'eth_call', + 'params' => [$tx, $item['blockNb']], + ]; + } else if (isset($item['ethCall'])) { + $tx = [ + 'to' => $item['ethCall']['to'], + 'data' => $item['ethCall']['data'], + ]; + $mapping[$idx] = ['rpc_index' => count($requests), 'type' => 'ethCall']; + $requests[] = [ + 'method' => 'eth_call', + 'params' => [$tx, 'pending'], + ]; + } else if (isset($item['estimatedGas'])) { + $mapping[$idx] = ['rpc_index' => count($requests), 'type' => 'estimatedGas']; + $requests[] = [ + 'method' => 'eth_estimateGas', + 'params' => [$item['estimatedGas']], + ]; + } else if (isset($item['blockByNumber'])) { + $mapping[$idx] = ['rpc_index' => count($requests), 'type' => 'block']; + $requests[] = [ + 'method' => 'eth_getBlockByNumber', + 'params' => [$item['blockByNumber'], false], + ]; + } else if (isset($item['blockNumber'])) { + $mapping[$idx] = ['rpc_index' => count($requests), 'type' => 'blockNumber']; + $requests[] = [ + 'method' => 'eth_blockNumber', + 'params' => [], + ]; + } else { + $mapping[$idx] = ['rejected' => 'Unknown operation']; + } +} + +// Send single batched RPC call to geth +try { + $responses = count($requests) > 0 ? $gethRPC->batch($requests) : []; +} catch (Exception $e) { + echo json_encode(['error' => true, 'msg' => $e->getMessage()]); + exit; +} + +// Map RPC responses back to input items +$out = []; +for ($idx = 0; $idx < count($input); $idx++) { + $map = $mapping[$idx]; + + if (isset($map['rejected'])) { + $out[] = ['error' => ['msg' => $map['rejected']]]; + continue; + } + + $r = $responses[$map['rpc_index']]; + + if (isset($r['error'])) { + $msg = isset($r['error']['message']) ? $r['error']['message'] : 'unknown error'; + $out[] = ['error' => ['msg' => $msg]]; + continue; + } + + if ($map['type'] === 'balance') { + $balancehex = $r['result']; + $out[] = ['data' => [ + 'address' => $map['addr'], + 'balance' => bchexdec($balancehex), + 'balancehex' => $balancehex, + ]]; + } else { + $out[] = ['data' => $r['result']]; + } +} + +echo json_encode(['error' => false, 'data' => $out]); + + +// -- Helpers (duplicated from api.php to avoid including its routing code) -- + +function formatAddress($addr) { + if (substr($addr, 0, 2) == "0x") + return $addr; + else + return "0x" . $addr; +} + +function bchexdec($hex) { + if (substr($hex, 0, 2) == "0x") { + $hex = substr($hex, 2); + } + $dec = 0; + $len = strlen($hex); + for ($i = 1; $i <= $len; $i++) { + $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i)))); + } + return $dec; +} + +?>