feat: address
This commit is contained in:
@@ -855,37 +855,46 @@ class IngestService
|
||||
}
|
||||
|
||||
/**
|
||||
* New addresses start with monitor=1. Turn off only when Tokenview is
|
||||
* not configured or the chain is unsupported. If the API call fails
|
||||
* (network error, rate limit, etc.), keep monitor=1 so it can be
|
||||
* retried later via the admin UI or a background job.
|
||||
* Two-field monitor model:
|
||||
* monitor — user-facing switch (default ON for new addresses)
|
||||
* monitor_synced — actual Tokenview registration state (default OFF;
|
||||
* only set to true when syncMonitor() succeeds)
|
||||
*
|
||||
* On ingest we attempt the initial Tokenview registration. If it fails
|
||||
* (network error, rate limit, etc.) we leave monitor=1 and
|
||||
* monitor_synced=0 so the sync-monitors scheduled command will retry.
|
||||
*/
|
||||
private function enableMonitorOrDisable(WalletAddress $address): void
|
||||
{
|
||||
// Tokenview not configured or unsupported chain → don't monitor.
|
||||
// Tokenview not configured or unsupported chain → switch off.
|
||||
if (! $this->tokenview->shouldMonitor($address)) {
|
||||
if ((int) $address->monitor !== 0) {
|
||||
if ((int) $address->monitor !== 0 || (bool) $address->monitor_synced || (int) $address->monitor_failures !== 0) {
|
||||
$address->monitor = 0;
|
||||
$address->monitor_synced = false;
|
||||
$address->monitor_failures = 0;
|
||||
$address->save();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to register with Tokenview. On transient failure, keep
|
||||
// monitor=1 so the address is still eligible for retry / webhooks.
|
||||
// Try to register with Tokenview.
|
||||
try {
|
||||
if ($this->tokenview->syncMonitor($address)) {
|
||||
return; // success
|
||||
// syncMonitor() flips monitor_synced=true on success.
|
||||
return;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('tokenview enable on ingest failed: '.$e->getMessage(), [
|
||||
'wallet_address_id' => $address->id,
|
||||
]);
|
||||
}
|
||||
// syncMonitor returned false or threw — but Tokenview is configured
|
||||
// and the chain is supported, so this is likely a transient API
|
||||
// failure. Keep monitor=1 for retry.
|
||||
// syncMonitor returned false or threw — transient API failure.
|
||||
// Keep monitor=1, ensure monitor_synced=0 for retry by scheduled task.
|
||||
if ((bool) $address->monitor_synced) {
|
||||
$address->monitor_synced = false;
|
||||
$address->save();
|
||||
}
|
||||
Log::info('tokenview sync failed, keeping monitor=1 for retry', [
|
||||
'wallet_address_id' => $address->id,
|
||||
]);
|
||||
|
||||
@@ -24,7 +24,15 @@ class TokenviewClient
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -38,16 +46,36 @@ class TokenviewClient
|
||||
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;
|
||||
|
||||
return $resp->successful() && (int) $resp->json('code') === 1;
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning("tokenview address {$action} failed: ".$e->getMessage(), [
|
||||
$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;
|
||||
|
||||
@@ -12,6 +12,9 @@ use Illuminate\Support\Facades\Log;
|
||||
|
||||
class TokenviewMonitorService
|
||||
{
|
||||
/** Stop retrying an address after this many consecutive sync failures. */
|
||||
public const MAX_SYNC_FAILURES = 3;
|
||||
|
||||
public function __construct(
|
||||
private readonly TokenviewClient $client,
|
||||
private readonly TelegramNotifier $telegram,
|
||||
@@ -78,10 +81,29 @@ class TokenviewMonitorService
|
||||
|
||||
$addr = (string) $address->address;
|
||||
if ((int) $address->monitor === 1) {
|
||||
return $this->client->addAddress($coin, $addr);
|
||||
$ok = $this->client->addAddress($coin, $addr);
|
||||
if ($ok) {
|
||||
$address->monitor_synced = true;
|
||||
$address->monitor_failures = 0;
|
||||
$address->save();
|
||||
} else {
|
||||
// Bump consecutive failure counter; the scheduled task stops
|
||||
// retrying once it reaches MAX_SYNC_FAILURES.
|
||||
$address->monitor_failures = (int) $address->monitor_failures + 1;
|
||||
$address->save();
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
return $this->client->removeAddress($coin, $addr);
|
||||
$ok = $this->client->removeAddress($coin, $addr);
|
||||
if ($ok) {
|
||||
$address->monitor_synced = false;
|
||||
$address->monitor_failures = 0;
|
||||
$address->save();
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user