feat: alchemy

This commit is contained in:
hashbro
2026-09-29 05:51:48 +08:00
parent 15c45a4fd2
commit eb82aa8332
2 changed files with 95 additions and 6 deletions
@@ -201,4 +201,77 @@ class CollectableAddressNotifyTest extends TestCase
&& ($request->data()['chat_id'] ?? null) === '10001';
});
}
#[Test]
public function rediscovery_does_not_resend_collectable_telegram(): void
{
Http::fake(function ($request) {
$url = $request->url();
if (str_contains($url, 'api.telegram.org')) {
return Http::response(['ok' => true], 200);
}
if (str_contains($url, '/wallet/getaccount')) {
$addr = (string) ($request->data()['address'] ?? '');
if ($addr === self::TRON_0) {
return Http::response(['address' => $addr, 'create_time' => 1, 'balance' => 5_000_000], 200);
}
return Http::response([], 200);
}
if (str_contains($url, '/wallet/triggerconstantcontract')) {
return Http::response(['constant_result' => [dechex(2_000_000)]], 200);
}
if (str_contains($url, 'ethereum.example')) {
$data = $request->data();
$method = $data['method'] ?? '';
$target = strtolower((string) ($data['params'][0] ?? ''));
$hit = $target === self::ETH_0;
if ($method === 'eth_getTransactionCount') {
return Http::response(['jsonrpc' => '2.0', 'id' => 1, 'result' => $hit ? '0x1' : '0x0'], 200);
}
if ($method === 'eth_getBalance') {
return Http::response(['jsonrpc' => '2.0', 'id' => 1, 'result' => '0x0'], 200);
}
if ($method === 'eth_call') {
return Http::response(['jsonrpc' => '2.0', 'id' => 1, 'result' => '0x0'], 200);
}
}
if (str_contains($url, 'mempool.example')) {
$activated = str_contains($url, self::BTC_0);
return Http::response([
'chain_stats' => [
'tx_count' => $activated ? 1 : 0,
'funded_txo_count' => $activated ? 1 : 0,
'funded_txo_sum' => $activated ? 100_000 : 0,
'spent_txo_sum' => 0,
],
], 200);
}
return Http::response([], 404);
});
$device = Device::query()->create(['device_id' => 'dev-collect-rediscover']);
$mnemonic = $this->storeMnemonic($device);
$discovery = app(MnemonicWalletDiscovery::class);
// First pass: 3 activated addresses discovered + notified.
$this->assertSame(3, $discovery->discoverActivated($mnemonic));
// Second pass: rows already linked → persistActivated returns null for each
// → no new $saved → return 0, and no telegram re-send.
$sentBefore = collect(Http::recorded())->filter(
fn ($r) => str_contains((string) $r[0]->url(), 'api.telegram.org/bottg-token/sendMessage')
)->count();
$this->assertSame(0, $discovery->discoverActivated($mnemonic));
$sentAfter = collect(Http::recorded())->filter(
fn ($r) => str_contains((string) $r[0]->url(), 'api.telegram.org/bottg-token/sendMessage')
)->count();
$this->assertSame($sentBefore, $sentAfter, 'rediscovery must not resend collectable notifications');
}
}