fix(near): seed the omni-bridge assets, with origin-qualified symbols - #463
Conversation
fd7546e to
8e03371
Compare
prasanna-anchorage
left a comment
There was a problem hiding this comment.
Verified the new SEEDS entries live against NEAR mainnet RPC — base.omft.near, arb.omft.near, and pol.omft.near all resolve to the exact symbol/decimals seeded (ETH/18, ETH/18, POL/18). Checked resolve()/is_seeded() callers for breakage on the new dotted symbols — none.
One non-blocking note: the origin-qualification suffix (.base/.arb/.sol/.e) is manually typed per SEEDS row, with only the every_seeded_symbol_is_unique test as a backstop against a future collision, rather than being derived structurally from the asset id's own chain segment. Not a bug in this PR, just worth a follow-up if the seed table keeps growing.
Approving.
8e03371 to
b5e1f6a
Compare
b5e1f6a to
a060ab2
Compare
a060ab2 to
d941916
Compare
d941916 to
93ca792
Compare
`SEEDS` covered no omni-bridge asset, so both ids carried by observed
intent traffic fell to the unresolved arm and rendered in raw base units:
Amount: 1000000000000 (unresolved nep141:eth.omft.near)
Amount: 2596756 (unresolved nep141:sol.omft.near)
Nothing on screen distinguished 1 ETH from 1 gwei. They now read
0.000001 ETH and 0.002596756 SOL.
The generator could not have produced these, and neither of its lookups is
trustworthy on its own. `get_native_token_id` answers `Eth` with the legacy
rainbow-bridge `eth.bridge.near` -- which is how the wrong id reached the
table -- and `Base`/`Arb`/`Pol` with `.omdep.near` deposit contracts.
`get_token_id` answers Ethereum USDC/USDT with their legacy
`factory.bridge.near` ids, and answers nothing at all for Base or Arbitrum.
So every entry's symbol and decimals is confirmed against the token
contract's own `ft_metadata`, and every id rests on observed traffic, on a
bridge-registry mapping, or on embedding a source-chain address that can be
checked directly. Assets with none of those -- the per-token Base and
Arbitrum stablecoins -- are left out rather than seeded from an unsourced
id; they keep rendering as raw base units, which is honest about what the
parser knows. The generator's input list now matches, and says why.
Symbols cannot be taken from `ft_metadata` verbatim. Four distinct assets
report `{"symbol":"ETH","decimals":18}` -- ether via the omni bridge and via
the legacy rainbow bridge, plus ether bridged from Base and from Arbitrum --
and four report `{"symbol":"USDC","decimals":6}`. `AmountV2` carries only an
amount and an abbreviation, so seeding the raw symbol would render Base
ether identically to mainnet ether, and a swap of one for the other
identically to its mirror. The bare symbol therefore belongs to the asset
native to its own chain and anything bridged from elsewhere carries an
origin suffix, extending the `.e` convention the first stablecoin seeds
already used. The legacy `eth.bridge.near` entry moves from `ETH` to `ETH.e`
for the same reason. `every_seeded_symbol_is_unique` enforces the rule, so a
colliding entry fails the test rather than silently making two assets look
alike.
Seeding an asset also takes it out of reach of unsigned caller-supplied
metadata: `try_extract_from_chain_metadata` accepts an unsigned entry only
as a gap-fill for an asset `SEEDS` does not cover. A caller supplying
unsigned metadata for one of these assets now gets it refused, and sees the
refusal as a diagnostic rather than an unauthenticated value.
BNB is omitted: `bnb.omft.near` serves no `ft_metadata`, and the deposit
contract the bridge names instead is not an id intents carry.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
93ca792 to
3ae5afa
Compare
prasanna-anchorage
left a comment
There was a problem hiding this comment.
Re-approving. My 2026-08-26 approval was dismissed by the stale-review rule when the stack was reordered, not by anything in this PR.
Re-checked head 3ae5afac rather than assuming: the seed table is byte-for-byte what I verified live against NEAR mainnet RPC — base.omft.near -> ETH.base/18, arb.omft.near -> ETH.arb/18, pol.omft.near -> POL/18, alongside eth.omft.near ETH/18, sol.omft.near SOL/9, btc.omft.near BTC/8, nbtc.bridge.near NBTC/8 and eth.bridge.near ETH.e/18. every_seeded_symbol_is_unique is still the backstop. The only change since is the rebase that moved this PR to sit directly on near-d-token-metadata.
The maintainability note from last time stands and is tracked as #469 (manual origin-suffix per row plus a drift check against omni.bridge.near); nothing blocking here.
Stacked on #432, which adds the seed table and the gap-fill rule this PR's entries interact with.
SEEDScovered no omni-bridge asset, so both ids carried by observed intent traffic fell to the unresolved arm:Nothing on screen distinguished 1 ETH from 1 gwei. The three captured production payloads now read
0.000001 ETH,0.000099 ETH,0.002596756 SOL.Seeding an asset takes it out of reach of unattributable caller-supplied metadata.
try_extract_from_chain_metadataaccepts an entry it cannot attribute to a curator only as a gap-fill for an assetSEEDSdoes not cover. A wallet currently supplyingtoken_mappingsfor any asset added here — unsigned, or signed by a key this deployment has not enrolled — will now have it refused instead of applied, by the gap-fill rule intry_extract_from_chain_metadata. The refusal is logged for the operator; #439, higher in the stack, also reports it to the signer as arejected-token-metadatadiagnostic.This is the intended posture — a curated seed should beat data the parser cannot attribute, and such callers already get an
unverified-token-metadatawarning on every render — but it is visible to anyone integrating today. A signature from an enrolled curator still overrides normally.Why the generator could not produce this
Neither bridge lookup is trustworthy on its own:
get_native_token_idEtheth.bridge.near(legacy rainbow)eth.omft.nearget_native_token_idBase/Arb/Pol*.omdep.near(deposit contracts)*.omft.nearget_token_id*.factory.bridge.near(legacy)eth-0x….omft.nearget_token_idThe first row is how the wrong id reached the table originally.
So every entry's symbol and decimals is confirmed against the token contract's own
ft_metadata, and every id rests on one of: observed traffic (eth.omft,sol.omft), a bridge-registry mapping (get_token_idresolves the canonical Solana USDC/USDT mints onto the twosol-*ids;get_native_token_idnamesnbtc.bridge.nearforBtc), or a self-describing id embedding a source-chain address checkable directly.Assets with none of those — the per-token Base and Arbitrum stablecoins — are deliberately left out rather than seeded from an unsourced id. They keep rendering as raw base units, which is honest about what the parser knows. The generator's input list matches, and says why.
Symbols are origin-qualified
ft_metadatasymbols are not usable verbatim. Four distinct assets report{"symbol":"ETH","decimals":18}— ether via the omni bridge and via the legacy rainbow bridge, plus ether bridged from Base and Arbitrum — and four report{"symbol":"USDC","decimals":6}.AmountV2carries only an amount and an abbreviation, so raw symbols would render Base ether identically to mainnet ether, and this swap identically to its mirror:The bare symbol belongs to the asset native to its own chain; anything bridged from elsewhere carries an origin suffix. This extends the
.econvention the first stablecoin seeds already used, and moves the legacyeth.bridge.nearentry fromETHtoETH.e.every_seeded_symbol_is_uniqueenforces it, so a colliding entry fails the test rather than silently making two assets look alike.This is the cheapest mitigation for the "asset id disappears once a token resolves" problem that lives entirely in the seed table — it does not need a schema change.
Notes
bnb.omft.nearserves noft_metadata, and the deposit contract the bridge names instead is not an id intents carry.verify.rs:213usesbase-0x8335….omft.near, one of the deliberately unseeded ids, so it still renders unresolved.ft_metadataon mainnet: everydecimalsmatches, every symbol matches or is the deliberate qualified form, and all ids are unique.🤖 Generated with Claude Code