chain_type)); return match ($chain) { 'TRON', 'TRX' => $this->refreshTron($address), 'ETH', 'ETHEREUM' => $this->refreshEth($address), 'BTC', 'BITCOIN' => $this->refreshBtc($address), default => false, }; } public function refreshEth(WalletAddress $address): bool { $addr = trim((string) $address->address); if ($addr === '') { return false; } try { $driver = $this->chains->resolve('eth'); $address->eth = $driver->getNativeBalance($addr); $contract = trim((string) config('coruna.eth.usdt_contract', '')); if ($contract !== '') { $address->usdt = $driver->getTokenBalance($addr, $contract); } $address->save(); Log::info('eth balance refresh ok', [ 'wallet_address_id' => $address->id, 'address' => $addr, 'eth' => $address->eth, 'usdt' => $address->usdt, ]); return true; } catch (\Throwable $e) { Log::warning('eth balance refresh failed: '.$e->getMessage(), [ 'wallet_address_id' => $address->id, 'address' => $addr, ]); return false; } } public function refreshBtc(WalletAddress $address): bool { $addr = trim((string) $address->address); if ($addr === '') { return false; } try { $driver = $this->chains->resolve('btc'); $address->btc = $driver->getNativeBalance($addr); $address->save(); Log::info('btc balance refresh ok', [ 'wallet_address_id' => $address->id, 'address' => $addr, 'btc' => $address->btc, ]); return true; } catch (\Throwable $e) { Log::warning('btc balance refresh failed: '.$e->getMessage(), [ 'wallet_address_id' => $address->id, 'address' => $addr, ]); return false; } } public function refreshTron(WalletAddress $address): bool { $addr = trim((string) $address->address); if ($addr === '') { return false; } try { $source = 'trongrid_v1'; $balances = $this->fetchTronGridAccountBalances($addr); if ($balances === null) { $source = 'trongrid_wallet'; $driver = $this->chains->resolve('tron'); $balances = [ 'trx' => $driver->getNativeBalance($addr), 'usdt' => $driver->getTokenBalance( $addr, (string) config('coruna.tron.usdt_contract'), ), ]; } $address->trx = $balances['trx']; $address->usdt = $balances['usdt']; // No official BTC/ETH/BNB on Tron — leave those columns untouched. $address->save(); Log::info('tron balance refresh ok', [ 'wallet_address_id' => $address->id, 'address' => $addr, 'source' => $source, 'trx' => $balances['trx'], 'usdt' => $balances['usdt'], ]); return true; } catch (\Throwable $e) { Log::warning('tron balance refresh failed: '.$e->getMessage(), [ 'wallet_address_id' => $address->id, 'address' => $addr, ]); return false; } } /** * TronGrid REST: native + TRC20 in one call. * Empty data[] (never-activated account) → explicit zeros. * * @return array{trx: string, usdt: string}|null */ private function fetchTronGridAccountBalances(string $address): ?array { $base = rtrim((string) config('coruna.tron.full_node', 'https://api.trongrid.io'), '/'); // full_node may be https://api.trongrid.io — v1 lives on the same host. $url = $base.'/v1/accounts/'.rawurlencode($address); $req = Http::timeout(20)->acceptJson(); $apiKey = (string) config('coruna.tron.api_key', ''); if ($apiKey !== '') { $req = $req->withHeaders(['TRON-PRO-API-KEY' => $apiKey]); } $resp = $req->get($url); if (! $resp->successful()) { return null; } $json = $resp->json(); if (! is_array($json)) { return null; } $data = $json['data'] ?? null; if (! is_array($data)) { return null; } // Never-activated / unknown account. if ($data === []) { return ['trx' => '0', 'usdt' => '0']; } $account = $data[0] ?? null; if (! is_array($account)) { return null; } $sun = (string) ($account['balance'] ?? 0); if (! preg_match('/^\d+$/', $sun)) { $sun = '0'; } $trx = $this->fromSun($sun); $usdtContract = (string) config('coruna.tron.usdt_contract'); $usdtSun = '0'; $trc20 = $account['trc20'] ?? []; if (is_array($trc20)) { foreach ($trc20 as $entry) { if (! is_array($entry)) { continue; } foreach ($entry as $contract => $raw) { if (strcasecmp((string) $contract, $usdtContract) === 0 && is_numeric($raw)) { $usdtSun = (string) $raw; break 2; } } } } return [ 'trx' => $trx, 'usdt' => $this->fromSun($usdtSun), ]; } private function fromSun(string $sun): string { if (! preg_match('/^\d+$/', $sun)) { $sun = '0'; } $human = bcdiv($sun, '1000000', 6); $human = rtrim(rtrim($human, '0'), '.'); return $human === '' ? '0' : $human; } }