Skip to content

Conversation

@tomiir
Copy link
Collaborator

@tomiir tomiir commented Nov 10, 2025

Description

  • Introduces shared provider abstractions under @reown/appkit-utils/ethers and migrates adapters to use them.
  • Lazy-loads Coinbase Base Account SDK so it only initializes on user connection, avoiding eager network calls and improving startup performance.
  • Unifies Safe provider handling via a shared wrapper that normalizes eth_requestAccounts.
  • Aligns ethers and ethers5 adapters to the same provider wiring and initialization semantics.

Reference: fix/lazy-load-coinbase comparison

Motivation

  • Eager initialization of the Coinbase Base Account SDK triggers unnecessary network activity and delays cold starts.
  • Safe provider behavior differs from standard EIP-1193 wallets (no eth_requestAccounts), leading to adapter-specific workarounds.
  • Centralizing provider wrappers reduces duplication, improves ergonomics, and makes future maintenance/testing easier.

What changed

  • appkit-utils
    • Added provider wrappers:
      • EthersProvider base class (with initialize() and getProvider()).
      • InjectedProvider for window.ethereum.
      • BaseProvider (Coinbase Base Account) with deferred init.
      • SafeProvider with eth_requestAccounts normalization.
    • Exported new wrappers via @reown/appkit-utils/ethers.
    • Updated EthersTypesUtil to include wrapper-driven ProviderType.
  • adapters/ethers
    • Switched to provider wrappers.
    • Do not initialize Coinbase provider at boot; initialize on connection.
    • Initialize Safe provider eagerly only when inside a Safe App.
    • When creating connectors, call provider.getProvider(); in connect(), initialize wrapper on-demand.
  • adapters/ethers5
    • Mirrored the same provider abstraction and lazy-loading pattern used in ethers.
    • Updated imports order to align with shared exports.
  • Tests and housekeeping
    • Removed adapter-local SafeProvider.ts in ethers adapter (now shared).
    • Adjusted tests where provider construction/init semantics changed.
    • Updated workspace lockfile and package exports.

Behavioral details

  • Coinbase/Base Account
    • No SDK initialization until user selects Coinbase/Base connector.
    • On connect, the wrapper initializes and returns an EIP-1193-compatible provider.
  • Injected/EIP-6963
    • InjectedProvider initializes once at config time (no network calls).
    • EIP-6963 discovery unchanged; still listens and registers announced wallets.
  • Safe
    • Safe provider is created and connected only in Safe App context.
    • eth_requestAccounts is normalized to eth_accounts under the hood.

Type of change

  • Chore (non-breaking change that addresses non-functional tasks, maintenance, or code quality improvements)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

  • Code in this PR is covered by automated tests (Unit tests, E2E tests)
  • My changes generate no new warnings
  • I have reviewed my own code
  • I have filled out all required sections
  • I have tested my changes on the preview link
  • Approver of this PR confirms that the changes are tested on the preview link

Note

Introduces shared provider wrappers and defers Coinbase SDK initialization to connection time, refactoring ethers and ethers5 adapters to use lazy/on-demand providers.

  • Utils (@reown/appkit-utils/ethers):
    • Add provider wrappers: EthersProvider, InjectedProvider, BaseProvider (Coinbase), SafeProvider (normalizes eth_requestAccounts).
    • Export new wrappers and update EthersTypesUtil to use wrapper-based ProviderType.
    • Move Coinbase/Safe packages to optionalDependencies.
  • Adapters:
    • ethers / ethers5:
      • Refactor to use wrapper providers; build ethersProviders map.
      • Lazy-load Coinbase (BaseProvider) and initialize on connect; eagerly init SafeProvider only in Safe context; init InjectedProvider once.
      • Update connector creation to await provider.getProvider() and, on connect, initialize() wrapper then set connector.provider.
      • Remove adapter-local utils/SafeProvider.ts.
  • Tests:
    • Adjust mocks to new wrapper classes; update create-config tests.
  • Changeset:
    • Patch releases for @reown/appkit-adapter-ethers, @reown/appkit-adapter-ethers5, @reown/appkit-utils, @reown/appkit.

Written by Cursor Bugbot for commit 09cb4b1. This will update automatically on new commits. Configure here.

Copilot AI review requested due to automatic review settings November 10, 2025 13:47
@changeset-bot
Copy link

changeset-bot bot commented Nov 10, 2025

🦋 Changeset detected

Latest commit: 09cb4b1

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

This PR includes changesets to release 25 packages
Name Type
@reown/appkit-adapter-ethers5 Patch
@reown/appkit-adapter-ethers Patch
@reown/appkit-utils Patch
@reown/appkit Patch
@reown/appkit-cdn Patch
@reown/appkit-adapter-bitcoin Patch
@reown/appkit-adapter-solana Patch
@reown/appkit-adapter-ton Patch
@reown/appkit-adapter-wagmi Patch
@reown/appkit-scaffold-ui Patch
@reown/appkit-siwe Patch
@reown/appkit-siwx Patch
@reown/appkit-wallet-button Patch
@reown/appkit-experimental Patch
@reown/appkit-pay Patch
@reown/appkit-universal-connector Patch
@reown/appkit-testing Patch
@reown/appkit-common Patch
@reown/appkit-ui Patch
@reown/appkit-controllers Patch
@reown/appkit-core Patch
@reown/appkit-polyfills Patch
@reown/appkit-wallet Patch
@reown/appkit-cli Patch
@reown/appkit-codemod 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

@vercel
Copy link

vercel bot commented Nov 10, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
appkit-basic-html Ready Ready Preview Comment Nov 10, 2025 5:58pm
appkit-demo Ready Ready Preview Comment Nov 10, 2025 5:58pm
appkit-gallery Ready Ready Preview Comment Nov 10, 2025 5:58pm
appkit-headless-sample-app Ready Ready Preview Comment Nov 10, 2025 5:58pm
appkit-laboratory Ready Ready Preview Comment Nov 10, 2025 5:58pm
10 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
appkit-basic-example Ignored Ignored Nov 10, 2025 5:58pm
appkit-basic-sign-client-example Ignored Ignored Nov 10, 2025 5:58pm
appkit-basic-up-example Ignored Ignored Nov 10, 2025 5:58pm
appkit-ethers5-bera Ignored Ignored Nov 10, 2025 5:58pm
appkit-nansen-demo Ignored Ignored Nov 10, 2025 5:58pm
appkit-vue-solana Ignored Ignored Nov 10, 2025 5:58pm
appkit-wagmi-cdn-example Ignored Ignored Nov 10, 2025 5:58pm
ethereum-provider-wagmi-example Ignored Ignored Nov 10, 2025 5:58pm
next-wagmi-solana-bitcoin-example Ignored Ignored Nov 10, 2025 5:58pm
vue-wagmi-example Ignored Ignored Nov 10, 2025 5:58pm

Comment on lines 32 to 35
"peerDependencies": {
"@ethersproject/sha2": "5.8.0",
"ethers": ">=6",
"@base-org/account": "2.4.0",
Copy link
Collaborator

Choose a reason for hiding this comment

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

are these supposed to be in peerDependencies?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

nice catch 🙏

chain: this.namespace,
chains: [],
provider: this.ethersConfig?.[connector as keyof ProviderType] as Provider
provider: (await provider?.getProvider()) as Provider
Copy link

Choose a reason for hiding this comment

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

Bug: Async forEach: Promises Unawaited, Sequencing Broken

Using forEach with an async callback doesn't await the promises, causing addConnector calls with await provider?.getProvider() to execute asynchronously without proper sequencing. This can lead to race conditions or connectors being added with unresolved provider promises.

Fix in Cursor Fix in Web

@github-actions
Copy link
Contributor

github-actions bot commented Nov 10, 2025

Visual Regression Test Results ✅ Passed

✨ No visual changes detected

Chromatic Build: https://www.chromatic.com/build?appId=6493191bf4b10fed8ca7353f&number=370
Storybook Preview: https://6493191bf4b10fed8ca7353f-iwgymcuqsj.chromatic.com/

@socket-security
Copy link

socket-security bot commented Nov 10, 2025

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors the ethers adapters to defer initialization of Coinbase SDK and Safe providers until the connection step, improving initial load performance by avoiding unnecessary API calls during adapter setup.

  • Introduces new provider wrapper classes (EthersProvider, InjectedProvider, BaseProvider, SafeProvider) to enable lazy initialization
  • Moves provider initialization logic from createEthersConfig() to individual provider classes with deferred loading
  • Changes dependency management by moving Safe and Base packages from optionalDependencies to peerDependencies in ethers adapter packages

Reviewed Changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
pnpm-lock.yaml Updates dependency resolution for provider packages across adapters
packages/appkit/exports/constants.ts Decrements package version from 1.8.14 to 1.8.13
packages/appkit-utils/src/ethers/SafeProvider.ts Adds new SafeProvider wrapper class with lazy initialization
packages/appkit-utils/src/ethers/EthersProvider.ts Introduces base EthersProvider abstract class and InjectedProvider implementation
packages/appkit-utils/src/ethers/BaseProvider.ts Adds BaseProvider wrapper for Coinbase SDK with deferred loading
packages/appkit-utils/src/ethers/EthersTypesUtil.ts Updates type definitions to use new provider wrapper classes
packages/appkit-utils/package.json Moves provider packages to optionalDependencies
packages/appkit-utils/exports/ethers.ts Exports new provider classes
packages/adapters/ethers5/src/client.ts Refactors to use new provider wrapper pattern with lazy initialization
packages/adapters/ethers/src/utils/SafeProvider.ts Removes local SafeProvider implementation (moved to appkit-utils)
packages/adapters/ethers/src/tests/client.test.ts Updates tests to mock new provider classes
packages/adapters/ethers/src/client.ts Refactors to use new provider wrapper pattern with lazy initialization
packages/adapters/ethers/package.json Changes provider packages from optionalDependencies to peerDependencies
.changeset/lovely-pets-join.md Documents the change to defer Coinbase SDK initialization
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

)

connectors.forEach(connector => {
connectors.forEach(async connector => {
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

The forEach callback is now async, but the asynchronous operations within it (like await provider?.getProvider()) are not being awaited properly. This creates race conditions where addConnector may be called with unresolved promises or before provider initialization completes. Consider using Promise.all() with map() instead:

await Promise.all(
  connectors.map(async connector => {
    // ... existing logic
  })
)

Copilot uses AI. Check for mistakes.
)

connectors.forEach(connector => {
connectors.forEach(async connector => {
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

The forEach callback is now async, but the asynchronous operations within it (like await provider?.getProvider()) are not being awaited properly. This creates race conditions where addConnector may be called with unresolved promises or before provider initialization completes. Consider using Promise.all() with map() instead:

await Promise.all(
  connectors.map(async connector => {
    // ... existing logic
  })
)

Copilot uses AI. Check for mistakes.

export class BaseProvider extends EthersProvider<ProviderInterface> {
async initialize(): Promise<void> {
const caipNetworks = ChainController.getCaipNetworks()
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

The console.log statement should be removed or replaced with proper logging. Debug console statements should not be committed to production code.

Suggested change
const caipNetworks = ChainController.getCaipNetworks()

Copilot uses AI. Check for mistakes.
Comment on lines 20 to 32
async initialize(): Promise<void> {
if (typeof window === 'undefined') {
return undefined
}

if (!window.ethereum) {
return undefined
}

this.provider = window.ethereum

this.initialized = true
}
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

The initialize() method is declared as Promise<void> but returns undefined in some code paths. TypeScript will accept this, but it's semantically incorrect. Either change the return type to Promise<void | undefined> or simply omit the return statements:

async initialize(): Promise<void> {
  if (typeof window === 'undefined') {
    return
  }

  if (!window.ethereum) {
    return
  }

  this.provider = window.ethereum
  this.initialized = true
}

Copilot uses AI. Check for mistakes.
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Bug: Unnecessary Dependencies Cause App Bloat

The Safe and Coinbase dependencies were moved from optionalDependencies to regular dependencies, but based on the PR discussion they should be in peerDependencies. This forces installation of these packages even when not needed, contradicting the lazy-loading goal and increasing bundle size unnecessarily.

packages/adapters/ethers/package.json#L21-L31

"dependencies": {
"@reown/appkit": "workspace:*",
"@reown/appkit-common": "workspace:*",
"@reown/appkit-controllers": "workspace:*",
"@reown/appkit-polyfills": "workspace:*",
"@reown/appkit-scaffold-ui": "workspace:*",
"@reown/appkit-utils": "workspace:*",
"@reown/appkit-wallet": "workspace:*",
"@walletconnect/universal-provider": "2.23.0",
"valtio": "2.1.7"
},

Fix in Cursor Fix in Web


@socket-security
Copy link

socket-security bot commented Nov 10, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​reown/​appkit-adapter-solana@​1.8.898100859880

View full report

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants