Skip to content

Commit bc7eecf

Browse files
committed
fix(tokens): key deduplication on (chain_id, address)
token_policy.select() deduplicated on address alone, but an Ethereum token's identity is (chain_id, address). The vetted source carries the same address on several chains -- 0x0000..0000 is listed as BURNER under EXP (2), OP (10) and MATIC (137) -- so every chain after the first was silently dropped from the generated firmware table. Verified: chains 10 and 137 were absent before this change and present after, while the genuine same-chain duplicate (CARD twice on chain 1) is still collapsed. uniswap_tokens passes a constant 1 because its list is mainnet-only, which serialize_c already hardcodes.
1 parent f275388 commit bc7eecf

3 files changed

Lines changed: 22 additions & 5 deletions

File tree

keepkeylib/eth/ethereum_tokens.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ def serialize_c(self, outf):
5555
self.tokens,
5656
token_policy.BUDGET_ETHEREUM_LISTS,
5757
symbol_of=lambda t: t.token.get('symbol', ''),
58-
address_of=lambda t: t.token['address'].lower())
58+
address_of=lambda t: t.token['address'].lower(),
59+
chain_of=lambda t: t.network['chain_id'])
5960
print('ethereum_tokens: %d of %d kept (budget %d)'
6061
% (len(chosen), len(self.tokens),
6162
token_policy.BUDGET_ETHEREUM_LISTS), file=sys.stderr)

keepkeylib/eth/token_policy.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,27 @@
8787
+ STABLECOINS + MAJORS)
8888

8989

90-
def select(records, budget, symbol_of, address_of):
90+
def select(records, budget, symbol_of, address_of, chain_of=None):
9191
"""Return `records` trimmed to `budget`, priority symbols first.
9292
9393
`records` is any iterable; `symbol_of`/`address_of` pull the two fields.
9494
Priority symbols with more than one address in `records` are DROPPED from
9595
the priority pass -- see rule 3 -- though they may still be picked up by
9696
the deterministic fill, where they carry no special standing.
97+
98+
`chain_of` supplies the chain id. A token's identity is (chain_id,
99+
address), NOT address alone: the vetted source carries the same address on
100+
several chains -- 0x0000..0000 is listed as BURNER under EXP (2), OP (10)
101+
and MATIC (137) -- and deduplicating on address alone silently dropped
102+
every chain after the first from the generated firmware table. Left as
103+
None the key falls back to address alone, which is only correct for a
104+
single-chain source.
97105
"""
106+
if chain_of is None:
107+
chain_of = lambda r: None
108+
109+
def key_of(r):
110+
return (chain_of(r), address_of(r))
98111
records = list(records)
99112
by_symbol = {}
100113
for r in records:
@@ -108,15 +121,15 @@ def select(records, budget, symbol_of, address_of):
108121
ambiguous.append(sym)
109122
continue
110123
for r in hits:
111-
key = address_of(r)
124+
key = key_of(r)
112125
if key not in seen:
113126
seen.add(key)
114127
chosen.append(r)
115128

116129
for r in sorted(records, key=address_of):
117130
if len(chosen) >= budget:
118131
break
119-
key = address_of(r)
132+
key = key_of(r)
120133
if key not in seen:
121134
seen.add(key)
122135
chosen.append(r)

keepkeylib/eth/uniswap_tokens.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ def serialize_c(self):
3838
self.ustoks,
3939
token_policy.BUDGET_UNISWAP_LIST,
4040
symbol_of=lambda t: t.token.get('symbol', ''),
41-
address_of=lambda t: t.token['contractAddress'].lower())
41+
address_of=lambda t: t.token['contractAddress'].lower(),
42+
# This list is mainnet-only (serialize_c hardcodes chain_id 1),
43+
# so the chain component is constant rather than absent.
44+
chain_of=lambda t: 1)
4245
print('uniswap_tokens: %d of %d kept (budget %d)'
4346
% (len(chosen), len(self.ustoks),
4447
token_policy.BUDGET_UNISWAP_LIST), file=_sys.stderr)

0 commit comments

Comments
 (0)