Skip to content

Fix NFT page showing only Ethereum: IPFS gateway fallback, show-more, Solana wallet changes - #16

Merged
Tarrant64 merged 2 commits into
mainfrom
fix/nft-ipfs-gateway-fallback
Sep 29, 2026
Merged

Tarrant64 merged 2 commits into
mainfrom
fix/nft-ipfs-gateway-fallback

Conversation

@Tarrant64

Copy link
Copy Markdown
Owner

Problem

After #11 + #12 were deployed, the NFT page appeared to show only Ethereum NFTs.

Reproduced in a real (headless) browser against the deployed build:

  • The API was fine. /nfts returned 299 Cardano NFTs, 296 of them with an image URL. Ethereum returned 31, Algorand 1.
  • Every Cardano image and the Algorand image failed. Show Cardano NFT images on the NFT page; load Algorand NFTs in /next #12 points them at https://ipfs.io/ipfs/<cid>, and the public ipfs.io gateway now answers hot-linked images with a Cloudflare challenge (HTTP 403, or 429 without a browser user agent). dweb.link, w3s.link and nftstorage.link answer the same way.
  • Gallery Mode hides tiles whose image fails. Of 328 tiles, only the 29 Ethereum ones (Alchemy CDN) stayed visible. The grid showed a placeholder on every Cardano card, so the only artwork on the page was Ethereum's.
  • The All Chains grid stopped at 100 cards (sorted by value). Algorand and Solana NFTs, which have no floor price, could never appear there.
  • Collections rendered as [object Object], and the collection count was inflated (331).
  • Solana: a wallet added after the last NFT fetch stayed invisible for up to 24 h (in-memory cache) or 30 days (persistent cache). The /next Refresh button does not force a refetch.

Fix

  • services/nft_display_images.py turns one metadata image URL into an ordered list of browser-safe URLs.
    • IPFS content is offered on every configured gateway. This covers ipfs://, ipfs://ipfs/, a bare CID, and any /ipfs/<cid> gateway URL, with the sub-path percent-encoded and .. rejected.
    • Other URLs pass only if they are https:// or data:image/.
    • Gateways are set with NFT_DISPLAY_IPFS_GATEWAYS. The default order is ipfs.blockfrost.dev, then Pinata, ipfs.io, dweb.link. ipfs.blockfrost.dev served the same content in under a second when this PR was made.
  • /nfts (Cardano) and /nfts/algorand return image_url plus image_fallbacks.
  • v2 NFT page:
    • nftImgFallback() tries the next URL before showing the placeholder or hiding the tile. This applies to the grid, Gallery Mode and the detail modal.
    • Only https:// and data:image/ URLs are ever used. This closes the raw http:// nft.image fallback noted in the Show Cardano NFT images on the NFT page; load Algorand NFTs in /next #12 review.
    • The All Chains grid has a "Show more" button instead of the hard 100-card cap.
    • Collection names come from the collection object, and the collection count is per chain.
  • Solana NFT cache: stores a SHA-256 of the Solana wallet set (never the addresses) and refetches when that set changes. Legacy cache rows without the hash are refetched once.

Tests

  • 36 new unit tests in tests/unit/test_nft_display_images.py: URL parsing, gateway ordering, the https-only rules, routes carrying fallbacks, Solana wallet-set invalidation and static checks of the page. One assertion in test_cardano_nft_images.py was updated for the gateway change.
  • All tests use mocked HTTP, DB and cache, with synthetic CIDs and addresses and no live calls.
  • Full unit suite: 511 passed, 8 failed. The same 8 fail on main, so no new failures.
  • Browser check against production data, with the patched page and API output applied client-side only:
    • Cardano images load from the first gateway (60 of 60 image requests returned 200 in 30 s).
    • Gallery Mode keeps 325 of 328 tiles.
    • "Show more" reaches all 331 cards, including Algorand.
    • Collection names render correctly.

Not in this PR

  • Images are still full-size originals, about 10 MB each on average. A thumbnail path, the image cache or a working NFTCDN integration would make the page much lighter.
  • Ethereum, Polygon and Base could get the same wallet-set invalidation as Solana.
  • The /next Refresh button could force a server-side refetch.

No Docker, compose, schema, auth or mobile/ changes.

🤖 Generated with Claude Code

… Solana wallet changes

After #12 every Cardano NFT image pointed at the public ipfs.io gateway,
which now answers hot-linked images with a Cloudflare challenge (HTTP
403/429). All Cardano and Algorand images failed to load; Gallery Mode
hides tiles whose image fails, so only Ethereum NFTs were left, and the
grid showed placeholders for every Cardano card.

- New services/nft_display_images.py: one metadata image URL becomes an
  ordered list of browser-safe URLs. IPFS content (ipfs://, bare CID, any
  /ipfs/<cid> gateway URL) is offered on every configured gateway
  (NFT_DISPLAY_IPFS_GATEWAYS, default ipfs.blockfrost.dev, Pinata,
  ipfs.io, dweb.link); other URLs pass only if https:// or data:image/.
- /nfts (Cardano) and /nfts/algorand return image_url + image_fallbacks.
- v2 NFT page: a failed image tries the next gateway before giving up
  (grid, Gallery Mode, detail modal); only https:// / data:image/ URLs
  are used (closes the raw http:// fallback from the #12 review); the
  All Chains grid is no longer capped at 100 cards (Show more), so
  Algorand/Solana are reachable; collection objects render by name and
  the collection count is per chain.
- Solana NFTs: the cache stores a SHA-256 of the Solana wallet set and
  is refetched when a wallet is added or removed, instead of hiding a new
  wallet's NFTs for up to 24 h (memory) / 30 days (persistent cache).
- 36 new unit tests (mocked HTTP/DB/cache, synthetic data, no live calls).

Task: ABCT-NFT-DIAG2-2026-09-28

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Overview

Image reference tarrant64/abct:latest tarrant64/abct:pr-16
- digest 60cf286aad6d 1d6e65e60b5c
- tag latest pr-16
- provenance 4f68c10
- vulnerabilities critical: 0 high: 7 medium: 7 low: 56 critical: 0 high: 7 medium: 7 low: 56
- platform linux/amd64 linux/amd64
- size 110 MB 126 MB (+16 MB)
- packages 285 285
Labels (5 changes)
  • - 2 removed
  • ± 3 changed
  • 8 unchanged
 org.opencontainers.image.authors=Chris Catalano
 org.opencontainers.image.base.name=python:3.11-slim
-org.opencontainers.image.created=2026-09-29T01:04:27.039Z
-org.opencontainers.image.description=A Better Crypto Tracker
+org.opencontainers.image.description=Multi-chain cryptocurrency portfolio tracker supporting Cardano, Ethereum, Bitcoin, Solana, Polygon, and Base
 org.opencontainers.image.documentation=https://github.com/Tarrant64/abct/blob/main/README.md
 org.opencontainers.image.icon=https://raw.githubusercontent.com/Tarrant64/abct/main/frontend/static/apple-touch-icon.png
 org.opencontainers.image.licenses=MIT
-org.opencontainers.image.revision=4f68c10493f540619781490d297f71ab1c211ce8
 org.opencontainers.image.source=https://github.com/Tarrant64/abct
-org.opencontainers.image.title=abct
+org.opencontainers.image.title=ABCT - A Better Crypto Tracker
 org.opencontainers.image.url=https://github.com/Tarrant64/abct
 org.opencontainers.image.vendor=ABCT Project
-org.opencontainers.image.version=latest
+org.opencontainers.image.version=1.12.3

Resolve CHANGELOG.md conflict by keeping both this branch's and
main's Unreleased/Fixed entries (PR #14 iagon scan-state, PR #13
system-cache-upsert, and PR #15 helius-balances fixes landed on
main first).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Tarrant64
Tarrant64 merged commit 4f68c10 into main Sep 29, 2026
2 checks passed
Tarrant64 added a commit that referenced this pull request Sep 29, 2026
Split out of PR #17 (fix/evm-multichain-fanout) after independent review
found its balance/token fan-out half wrong (native coin double-counted,
spam ERC-20s priced by raw on-chain symbol) while judging the NFT half
correct: http://192.168.50.240/data/2026-09-28/ABCT-PR17-REVIEW-2026-09-28.html

Wallets are stored one-row-per-(address, blockchain), and a bare 0x
address is always filed as 'ethereum' (utils/address.detect_blockchain
only assigns polygon/base from an explicit prefixed address). /nfts/polygon
and /nfts/base only ever asked Alchemy about addresses whose own row
already said that chain, so a real Polygon/Base holder registered as
'ethereum' was invisible. Every EVM-family wallet (ethereum/polygon/base/
bsc/arbitrum/avalanche) is now a candidate address for Ethereum, Polygon
and Base NFT fetches, deduplicated by address, with the existing spam
filter, persistent cache and wipe-guard-on-provider-failure behaviour
(#11/#16) plus the #16 wallet-set-fingerprint cache invalidation extended
to Ethereum/Polygon/Base.

Excludes services/evm_balance_fanout.py and the routers/portfolio.py
balance-total wiring entirely -- that half is being reworked separately.
Totals are unaffected by this PR.

8 unit tests in tests/unit/test_evm_fanout.py (mocked HTTP/DB, fake
addresses, no live calls or real keys).

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant