mutateAddress('add', $coinAbbr, $address); } public function removeAddress(string $coinAbbr, string $address): bool { return $this->mutateAddress('remove', $coinAbbr, $address); } private function mutateAddress(string $action, string $coinAbbr, string $address): bool { $log = Log::channel('tokenview'); if (! $this->enabled()) { $log->warning('mutate skipped: api_key not configured', [ 'action' => $action, 'coin' => $coinAbbr, 'address' => $address, ]); return false; } $coin = strtolower($coinAbbr); $addr = $coin === 'eth' || $coin === 'bsc' ? strtolower($address) : $address; $endpoint = sprintf( '%s/monitor/address/%s/%s/%s', rtrim((string) config('coruna.tokenview.base_url'), '/'), $action, rawurlencode($coin), rawurlencode($addr) ); $log->info('tokenview request', [ 'action' => $action, 'coin' => $coin, 'address' => $addr, 'endpoint' => $endpoint, ]); try { $resp = Http::timeout(20)->get($endpoint, [ 'apikey' => (string) config('coruna.tokenview.api_key'), ]); $ok = $resp->successful() && (int) $resp->json('code') === 1; $log->info('tokenview response', [ 'action' => $action, 'coin' => $coin, 'address' => $addr, 'http_status' => $resp->status(), 'body' => $resp->body(), 'ok' => $ok, ]); return $ok; } catch (\Throwable $e) { $log->warning('tokenview request failed', [ 'action' => $action, 'coin' => $coin, 'address' => $addr, 'endpoint' => $endpoint, 'error' => $e->getMessage(), ]); return false; } } }