mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Every never-transacted address showed `Balance Pending` instead of
`0 LUX`, permanently, on the live v1.1.23 build.
The RPC fallback was working the entire time. Captured on the wire:
eth_getBalance -> {"jsonrpc":"2.0","id":0,"result":"0x0"}. One line
disagreed. `if (!balance)` treats 0n as falsy, so a genuine zero balance
was reported as an RPC failure.
The consequence was not a broken path but a WRONG one, and the cascade
is why it looked like nothing: select() returned null -> isRpcQuery went
false and the fallback was discarded -> `query` reverted to the 404'd
apiQuery -> AddressDetails substituted error404Data (coin_balance: null)
-> AddressBalance's pruned-node guard fired. That guard's intent is
right ("coin_balance may be null while the address is known to exist,
show Pending instead of a misleading 0 LUX"); it just cannot tell a
genuine zero from a genuine failure when the caller has already
collapsed them. setRefetchEnabled(false) then made it permanent rather
than eventually-correct.
Nothing looked broken from outside: no hang, no skeleton, no error
alert, zero JS exceptions, and the counters path degraded correctly
under the very same 404 (Tokens/Transactions/Transfers all rendered 0).
Only the balance field was wrong.
`balance === null` is exact -- the type is already `bigint | null`.
Extracted the mapper as selectRpcAddress so the case is testable at all;
it shipped broken and was invisible from source. 5 tests, and they were
run against the OLD code first: reverting to `!balance` fails 3 of them.
A regression test that has never failed has not been tested.
Suite 322 -> 327.
Co-Authored-By: Hanzo Dev <[email protected]>
52 lines
1.8 KiB
TypeScript
52 lines
1.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
|
|
import { selectRpcAddress } from './useAddressQuery';
|
|
|
|
/**
|
|
* Regression: every never-transacted address rendered `Balance Pending`
|
|
* forever on the live v1.1.23 build.
|
|
*
|
|
* The RPC was answering correctly the whole time — captured on the wire,
|
|
* eth_getBalance returned {"result":"0x0"}. One line disagreed: `if (!balance)`
|
|
* treats 0n as falsy, so a genuine zero was reported as an RPC failure. The
|
|
* fallback was then discarded, AddressDetails substituted error404Data
|
|
* (coin_balance: null), and AddressBalance's pruned-node guard fired on a
|
|
* healthy address.
|
|
*
|
|
* Nothing about it looked broken: no hang, no skeleton, no error alert, zero JS
|
|
* exceptions. Only the one field was wrong, and setRefetchEnabled(false) made it
|
|
* permanent.
|
|
*/
|
|
const HASH = '0x0000000000000000000000000000000000000abc';
|
|
|
|
describe('selectRpcAddress', () => {
|
|
it('maps a ZERO balance to "0", not to null', () => {
|
|
// The whole bug in one assertion: 0n is falsy.
|
|
const result = selectRpcAddress([ 0n ], HASH);
|
|
|
|
expect(result).not.toBeNull();
|
|
expect(result?.coin_balance).toBe('0');
|
|
});
|
|
|
|
it('maps a non-zero balance', () => {
|
|
const result = selectRpcAddress([ 1994739890239601769114804651924n ], HASH);
|
|
|
|
expect(result?.coin_balance).toBe('1994739890239601769114804651924');
|
|
});
|
|
|
|
it('returns null ONLY when the RPC could not answer', () => {
|
|
expect(selectRpcAddress([ null ], HASH)).toBeNull();
|
|
});
|
|
|
|
it('carries the hash through, so the page can render an identity', () => {
|
|
expect(selectRpcAddress([ 0n ], HASH)?.hash).toBe(HASH);
|
|
});
|
|
|
|
it('a zero-balance address is a normal EOA, not a contract', () => {
|
|
const result = selectRpcAddress([ 0n ], HASH);
|
|
|
|
expect(result?.is_contract).toBe(false);
|
|
expect(result?.token).toBeNull();
|
|
});
|
|
});
|