Skip to content

fix(core): load wallet & network images on React Native#574

Draft
ignaciosantise wants to merge 2 commits into
developfrom
fix/fetch-image-blob-arraybuffer
Draft

fix(core): load wallet & network images on React Native#574
ignaciosantise wants to merge 2 commits into
developfrom
fix/fetch-image-blob-arraybuffer

Conversation

@ignaciosantise

Copy link
Copy Markdown
Collaborator

Problem

Wallet and network logos in the connect modal never load on React Native — the list shows only placeholder icons on both iOS and Android (wallet names load fine, since those come from plain JSON).

Root cause is in FetchUtil.fetchImage (packages/core/src/utils/FetchUtil.ts), which builds the image data URL with response.blob() + FileReader.readAsDataURL:

const blob = await response.blob();      // ← throws on RN for binary bodies
const reader = new FileReader();
reader.readAsDataURL(blob);

On React Native, fetch(...).blob() for a binary (image) response throws:

Error: Creating blobs from 'ArrayBuffer' and 'ArrayBufferView' are not supported

RN's Blob implementation can't be constructed from an ArrayBuffer body, so the promise rejects, fetchImage returns undefined, and AssetUtil.getWalletImage falls back to the placeholder. This is the only .blob() usage in the codebase.

Observed with Expo SDK 56 / React Native 0.85.3 (New Architecture), @reown/appkit-*@2.0.5. The image fetch itself succeeds (HTTP 200, content-type: image/webp); only the blob() step fails.

Fix

Read the response as an ArrayBuffer and base64-encode it into the data URL, avoiding Blob/FileReader entirely:

const arrayBuffer = await response.arrayBuffer();
const contentType = response.headers.get('content-type') ?? 'image/png';
return `data:${contentType};base64,${FetchUtil._arrayBufferToBase64(arrayBuffer)}`;

The base64 encoder is dependency-free — React Native guarantees neither a global Buffer nor btoa, so relying on either would be fragile for a library.

Tests

Adds unit tests for fetchImage (ArrayBuffer → base64 data URL, default content-type, blob() not called, error → undefined). Full packages/core suite passes:

Tests:       17 passed, 17 total

Verification

Patched into a multichain AppKit + wagmi app on Expo SDK 56 / RN 0.85.3:

  • Android: wallet logos (Binance, MetaMask, SafePal, Trust Wallet, …) render; zero blob errors.
  • iOS: zero blob errors after the fix.

🤖 Generated with Claude Code

FetchUtil.fetchImage built its data URL with response.blob() + FileReader,
but on React Native fetch(...).blob() throws "Creating blobs from 'ArrayBuffer'
and 'ArrayBufferView' are not supported" for binary responses, so wallet and
network images silently failed to load. Read the bytes via response.arrayBuffer()
and base64-encode them into the data URL instead (dependency-free encoder, since
RN guarantees neither Buffer nor btoa).

Adds unit tests for fetchImage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ee0c466

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@reown/appkit-core-react-native Patch
@reown/appkit-react-native Patch
@reown/appkit-ethers-react-native Patch
@reown/appkit-wagmi-react-native Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@claude

claude Bot commented Jul 6, 2026

Copy link
Copy Markdown

Claude finished @ignaciosantise's task in 2m 42s —— View job


Code Review

  • Gather context (read changed files)
  • Check for breaking changes — none found
  • Analyze issues
Found 1 issue(s)

Issue 1: fetchImage encodes error-response bodies as image data URLs

ID: fetchutil-fetchimage-ok-check-a3f1
File: packages/core/src/utils/FetchUtil.ts:89
Severity: MEDIUM
Category: correctness

Context:

  • Pattern: fetchImage calls response.arrayBuffer() without first checking response.ok. processResponse (used by get/post/etc.) does guard with response.ok; fetchImage is inconsistent.
  • Risk: For 4xx/5xx responses the error body is base64-encoded and returned as a data URL (e.g. data:application/json;base64,...) instead of undefined. The catch block never fires because arrayBuffer() itself succeeds on non-ok responses.
  • Impact: Callers receive a syntactically valid but semantically garbage data URL. The RN Image component attempts (and silently fails) to render it rather than falling back to the placeholder as intended.
  • Trigger: Any image URL returning a non-2xx status (CDN 404, rate-limit 429, etc.).

Recommendation:

const response = await fetch(url, { headers });
if (!response.ok) {
  return undefined;
}
const arrayBuffer = await response.arrayBuffer();

Fix this →

// responses, so Blob + FileReader can't be used to build the data URL
// (wallet/network images would silently fail to load). Read the bytes as
// an ArrayBuffer and base64-encode them into a data URL instead.
const arrayBuffer = await response.arrayBuffer();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Auto Review Issue: fetchImage encodes error-response bodies as image data URLs

Severity: MEDIUM
Category: correctness
Tool: Claude Auto Review

Context:

  • Pattern: fetchImage calls response.arrayBuffer() without first checking response.ok. processResponse (used by get/post/etc.) does guard with response.ok; fetchImage is inconsistent.
  • Risk: For 4xx/5xx responses the error body is base64-encoded and returned as a data URL (e.g. data:application/json;base64,...) instead of undefined. The catch block never fires because arrayBuffer() itself succeeds on non-ok responses.
  • Impact: Callers receive a syntactically valid but semantically garbage data URL. The RN Image component attempts (and silently fails) to render it rather than falling back to the placeholder as intended.
  • Trigger: Any image URL returning a non-2xx status (CDN 404, rate-limit 429, etc.).

Recommendation:

const response = await fetch(url, { headers });
if (!response.ok) {
  return undefined;
}
const arrayBuffer = await response.arrayBuffer();

Fix this →

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — this is valid. fetchImage does not guard on response.ok, so a 4xx/5xx would get base64-encoded into a data URL and RN's Image would fail silently instead of falling back to the placeholder.

Note this is not a regression — the previous blob() version had the same gap — but since we're rewriting this block it's the right place to fix it. Will add the if (!response.ok) return undefined; guard before arrayBuffer() (plus a test).

Co-Authored-By: Claude Opus 4.8 <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