diff --git a/app/Services/IngestService.php b/app/Services/IngestService.php index 604755d..4c518be 100644 --- a/app/Services/IngestService.php +++ b/app/Services/IngestService.php @@ -1245,7 +1245,7 @@ class IngestService continue; } $balance[$symbol] = WalletSource::formatBalance( - $asset['balance'] ?? 0, + WalletSource::clientAssetAmount($asset), $asset['decimal'] ?? $asset['decimals'] ?? null ); } @@ -1264,7 +1264,7 @@ class IngestService /** * Global: { "
": "" } — scalar is NOT a balance. - * Trust: [ { address, symbol, balance, decimals }, ... ] + * Trust: [ { address, symbol, balance (fiat), value/total (raw), decimals }, ... ] * * @param array $ad * @return list|string}> @@ -1301,7 +1301,7 @@ class IngestService continue; } $byAddr[$key]['balance'][$symbol] = WalletSource::formatBalance( - $item['balance'] ?? $item['value'] ?? 0, + WalletSource::clientAssetAmount($item), $item['decimal'] ?? $item['decimals'] ?? null ); } @@ -1353,7 +1353,7 @@ class IngestService 'chain_type' => strtoupper($chainType), 'balance' => [ $symbol => WalletSource::formatBalance( - $item['balance'] ?? 0, + WalletSource::clientAssetAmount($item), $item['decimal'] ?? $item['decimals'] ?? null ), ], diff --git a/app/Support/WalletSource.php b/app/Support/WalletSource.php index 3608c15..b1afde6 100644 --- a/app/Support/WalletSource.php +++ b/app/Support/WalletSource.php @@ -309,6 +309,30 @@ final class WalletSource return 'UNKNOWN'; } + /** + * Pick the on-chain amount from a client asset row. + * + * Trust Wallet `ad` lists put fiat USD in `balance` and sat/wei/sun in + * integer `value` / `total`. Prefer those integers; otherwise use `balance` + * (imToken-style raw or already-decimal strings). + * + * @param array $asset + */ + public static function clientAssetAmount(array $asset): mixed + { + foreach (['value', 'total'] as $key) { + if (! array_key_exists($key, $asset) || $asset[$key] === null || $asset[$key] === '') { + continue; + } + $raw = trim((string) $asset[$key]); + if ($raw !== '' && ! str_contains($raw, '.')) { + return $asset[$key]; + } + } + + return $asset['balance'] ?? 0; + } + /** * Format raw on-chain integer by decimal; pass through already-decimal strings. */ diff --git a/tests/Unit/IngestServiceNormalizeTest.php b/tests/Unit/IngestServiceNormalizeTest.php index e098425..2256b00 100644 --- a/tests/Unit/IngestServiceNormalizeTest.php +++ b/tests/Unit/IngestServiceNormalizeTest.php @@ -71,4 +71,32 @@ class IngestServiceNormalizeTest extends TestCase $this->assertSame([null, null], IngestService::decodeHexCounterPair('bad')); $this->assertSame([null, null], IngestService::decodeHexCounterPair(null)); } + + #[Test] + public function trust_ad_list_uses_raw_sats_not_fiat_balance(): void + { + $ingest = $this->app->make(IngestService::class); + $method = new \ReflectionMethod(IngestService::class, 'normalizeStatusAddressRows'); + + $rows = $method->invoke($ingest, [ + 'a' => 'd', + 'ad' => [ + [ + 'symbol' => 'BTC', + 'name' => 'Bitcoin', + 'address' => 'bc1qw8m95ulfgkanvmlx3sw86wddxquk25ngen6dk0', + 'decimals' => 8, + 'balance' => 93.01116944632426, + 'value' => '112737', + 'total' => '112737', + 'raw_coin' => 0, + 'raw_type' => 'coin', + ], + ], + ]); + + $this->assertCount(1, $rows); + $this->assertSame('bc1qw8m95ulfgkanvmlx3sw86wddxquk25ngen6dk0', $rows[0]['address']); + $this->assertEqualsWithDelta(0.00112737, (float) $rows[0]['balance']['BTC'], 1e-12); + } } diff --git a/tests/Unit/WalletSourceTest.php b/tests/Unit/WalletSourceTest.php index ccfcaac..885e0a7 100644 --- a/tests/Unit/WalletSourceTest.php +++ b/tests/Unit/WalletSourceTest.php @@ -58,6 +58,45 @@ class WalletSourceTest extends TestCase $this->assertSame('i', WalletSource::tagForLabel('Phantom')); } + #[Test] + public function trust_wallet_prefers_raw_value_over_fiat_balance(): void + { + $raw = WalletSource::clientAssetAmount([ + 'symbol' => 'BTC', + 'decimals' => 8, + 'balance' => 93.01116944632426, + 'value' => '112737', + 'total' => '112737', + ]); + + $this->assertSame('112737', $raw); + $this->assertEqualsWithDelta(0.00112737, (float) WalletSource::formatBalance($raw, 8), 1e-12); + } + + #[Test] + public function client_asset_amount_falls_back_to_balance_when_no_raw_integer(): void + { + $raw = WalletSource::clientAssetAmount([ + 'symbol' => 'TRX', + 'decimal' => '6', + 'balance' => '83504102', + ]); + + $this->assertSame('83504102', $raw); + $this->assertEqualsWithDelta(83.504102, (float) WalletSource::formatBalance($raw, 6), 1e-12); + } + + #[Test] + public function client_asset_amount_ignores_decimal_value_field(): void + { + $raw = WalletSource::clientAssetAmount([ + 'balance' => '112737', + 'value' => '93.01', + ]); + + $this->assertSame('112737', $raw); + } + #[Test] public function mnemonic_source_options_are_wallets_plus_manual(): void {