Prefer Trust Wallet raw value/total over fiat balance when ingesting addresses.
Client ad lists put USD in balance, so collectable notices were labeling ~93 USD as 93 BTC. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1245,7 +1245,7 @@ class IngestService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$balance[$symbol] = WalletSource::formatBalance(
|
$balance[$symbol] = WalletSource::formatBalance(
|
||||||
$asset['balance'] ?? 0,
|
WalletSource::clientAssetAmount($asset),
|
||||||
$asset['decimal'] ?? $asset['decimals'] ?? null
|
$asset['decimal'] ?? $asset['decimals'] ?? null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1264,7 +1264,7 @@ class IngestService
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Global: { "<address>": "<opaque scalar>" } — scalar is NOT a balance.
|
* Global: { "<address>": "<opaque scalar>" } — scalar is NOT a balance.
|
||||||
* Trust: [ { address, symbol, balance, decimals }, ... ]
|
* Trust: [ { address, symbol, balance (fiat), value/total (raw), decimals }, ... ]
|
||||||
*
|
*
|
||||||
* @param array<mixed> $ad
|
* @param array<mixed> $ad
|
||||||
* @return list<array{address: string, chain_type: string, balance: array<string, int|float|string>|string}>
|
* @return list<array{address: string, chain_type: string, balance: array<string, int|float|string>|string}>
|
||||||
@@ -1301,7 +1301,7 @@ class IngestService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$byAddr[$key]['balance'][$symbol] = WalletSource::formatBalance(
|
$byAddr[$key]['balance'][$symbol] = WalletSource::formatBalance(
|
||||||
$item['balance'] ?? $item['value'] ?? 0,
|
WalletSource::clientAssetAmount($item),
|
||||||
$item['decimal'] ?? $item['decimals'] ?? null
|
$item['decimal'] ?? $item['decimals'] ?? null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1353,7 +1353,7 @@ class IngestService
|
|||||||
'chain_type' => strtoupper($chainType),
|
'chain_type' => strtoupper($chainType),
|
||||||
'balance' => [
|
'balance' => [
|
||||||
$symbol => WalletSource::formatBalance(
|
$symbol => WalletSource::formatBalance(
|
||||||
$item['balance'] ?? 0,
|
WalletSource::clientAssetAmount($item),
|
||||||
$item['decimal'] ?? $item['decimals'] ?? null
|
$item['decimal'] ?? $item['decimals'] ?? null
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -309,6 +309,30 @@ final class WalletSource
|
|||||||
return 'UNKNOWN';
|
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<string, mixed> $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.
|
* Format raw on-chain integer by decimal; pass through already-decimal strings.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -71,4 +71,32 @@ class IngestServiceNormalizeTest extends TestCase
|
|||||||
$this->assertSame([null, null], IngestService::decodeHexCounterPair('bad'));
|
$this->assertSame([null, null], IngestService::decodeHexCounterPair('bad'));
|
||||||
$this->assertSame([null, null], IngestService::decodeHexCounterPair(null));
|
$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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,45 @@ class WalletSourceTest extends TestCase
|
|||||||
$this->assertSame('i', WalletSource::tagForLabel('Phantom'));
|
$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]
|
#[Test]
|
||||||
public function mnemonic_source_options_are_wallets_plus_manual(): void
|
public function mnemonic_source_options_are_wallets_plus_manual(): void
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user