Repository: coinbase/coinbase-wallet-sdk
Pull request #1599: Move config into single store, remove config store
Author: login: cb-jake
Created: 2025-04-11T19:58:27Z | Merged: 2025-04-15T14:59:23Z

### _Summary_

Move config into single store, remove config store

### _How did you test your changes?_

Tested manually


Diff:
diff --git a/packages/wallet-sdk/src/CoinbaseWalletProvider.test.ts b/packages/wallet-sdk/src/CoinbaseWalletProvider.test.ts
index 21c0671d2b..e589442989 100644
--- a/packages/wallet-sdk/src/CoinbaseWalletProvider.test.ts
+++ b/packages/wallet-sdk/src/CoinbaseWalletProvider.test.ts
@@ -1,10 +1,10 @@
-import { CoinbaseWalletProvider } from './CoinbaseWalletProvider.js';
-import * as util from './sign/util.js';
-import * as providerUtil from './util/provider.js';
 import { CB_WALLET_RPC_URL } from ':core/constants.js';
 import { standardErrorCodes } from ':core/error/constants.js';
 import { standardErrors } from ':core/error/errors.js';
 import { ProviderEventCallback, RequestArguments } from ':core/provider/interface.js';
+import { CoinbaseWalletProvider } from './CoinbaseWalletProvider.js';
+import * as util from './sign/util.js';
+import * as providerUtil from './util/provider.js';
 
 function createProvider() {
   return new CoinbaseWalletProvider({
diff --git a/packages/wallet-sdk/src/CoinbaseWalletProvider.ts b/packages/wallet-sdk/src/CoinbaseWalletProvider.ts
index 0747adb13a..47211ae377 100644
--- a/packages/wallet-sdk/src/CoinbaseWalletProvider.ts
+++ b/packages/wallet-sdk/src/CoinbaseWalletProvider.ts
@@ -1,5 +1,3 @@
-import { Signer } from './sign/interface.js';
-import { createSigner, fetchSignerType, loadSignerType, storeSignerType } from './sign/util.js';
 import { Communicator } from ':core/communicator/Communicator.js';
 import { CB_WALLET_RPC_URL } from ':core/constants.js';
 import { standardErrorCodes } from ':core/error/constants.js';
@@ -17,6 +15,8 @@ import {
 import { ScopedLocalStorage } from ':core/storage/ScopedLocalStorage.js';
 import { hexStringFromNumber } from ':core/type/util.js';
 import { checkErrorForInvalidRequestArgs, fetchRPCRequest } from ':util/provider.js';
+import { Signer } from './sign/interface.js';
+import { createSigner, fetchSignerType, loadSignerType, storeSignerType } from './sign/util.js';
 
 export class CoinbaseWalletProvider extends ProviderEventEmitter implements ProviderInterface {
   private readonly metadata: AppMetadata;
diff --git a/packages/wallet-sdk/src/CoinbaseWalletSDK.ts b/packages/wallet-sdk/src/CoinbaseWalletSDK.ts
index fb916b6402..dccf1ddb36 100644
--- a/packages/wallet-sdk/src/CoinbaseWalletSDK.ts
+++ b/packages/wallet-sdk/src/CoinbaseWalletSDK.ts
@@ -1,14 +1,13 @@
 // Copyright (c) 2018-2024 Coinbase, Inc. <https://www.coinbase.com/>
 
-import { LogoType, walletLogo } from './assets/wallet-logo.js';
-import { CoinbaseWalletProvider } from './CoinbaseWalletProvider.js';
-import { AppMetadata, Preference, ProviderInterface } from './core/provider/interface.js';
 import { getFavicon } from ':core/type/util.js';
-import { config } from ':store/config.js';
 import { store } from ':store/store.js';
 import { checkCrossOriginOpenerPolicy } from ':util/checkCrossOriginOpenerPolicy.js';
 import { getCoinbaseInjectedProvider } from ':util/provider.js';
 import { validatePreferences } from ':util/validatePreferences.js';
+import { CoinbaseWalletProvider } from './CoinbaseWalletProvider.js';
+import { LogoType, walletLogo } from './assets/wallet-logo.js';
+import { AppMetadata, Preference, ProviderInterface } from './core/provider/interface.js';
 
 // for backwards compatibility
 type CoinbaseWalletSDKOptions = Partial<AppMetadata>;
@@ -31,7 +30,7 @@ export class CoinbaseWalletSDK {
       appChainIds: metadata.appChainIds || [],
     };
 
-    config.setState({
+    store.config.set({
       metadata: this.metadata,
     });
 
@@ -40,7 +39,7 @@ export class CoinbaseWalletSDK {
 
   public makeWeb3Provider(preference: Preference = { options: 'all' }): ProviderInterface {
     validatePreferences(preference);
-    config.setState({
+    store.config.set({
       preference,
     });
     const params = { metadata: this.metadata, preference };
diff --git a/packages/wallet-sdk/src/createCoinbaseWalletSDK.ts b/packages/wallet-sdk/src/createCoinbaseWalletSDK.ts
index f7e5c57e77..8d58841cd5 100644
--- a/packages/wallet-sdk/src/createCoinbaseWalletSDK.ts
+++ b/packages/wallet-sdk/src/createCoinbaseWalletSDK.ts
@@ -1,7 +1,5 @@
 import { decodeAbiParameters, encodeFunctionData, toHex } from 'viem';
 
-import { createCoinbaseWalletProvider } from './createCoinbaseWalletProvider.js';
-import { store, SubAccount, ToSubAccountSigner } from './store/store.js';
 import {
   AppMetadata,
   ConstructorOptions,
@@ -11,10 +9,11 @@ import {
 import { AddSubAccountAccount } from ':core/rpc/wallet_addSubAccount.js';
 import { WalletConnectResponse } from ':core/rpc/wallet_connect.js';
 import { abi } from ':sign/scw/utils/constants.js';
-import { config } from ':store/config.js';
 import { assertPresence } from ':util/assertPresence.js';
 import { checkCrossOriginOpenerPolicy } from ':util/checkCrossOriginOpenerPolicy.js';
 import { validatePreferences, validateSubAccount } from ':util/validatePreferences.js';
+import { createCoinbaseWalletProvider } from './createCoinbaseWalletProvider.js';
+import { SubAccount, ToSubAccountSigner, store } from './store/store.js';
 
 export type CreateCoinbaseWalletSDKOptions = Partial<AppMetadata> & {
   preference?: Preference;
@@ -47,7 +46,7 @@ export function createCoinbaseWalletSDK(params: CreateCoinbaseWalletSDKOptions)
   };
 
   // set the options in the store
-  config.setState(options);
+  store.config.set(options);
 
   // rehydrate the store from storage
   void store.persist.rehydrate();
diff --git a/packages/wallet-sdk/src/sign/scw/SCWSigner.test.ts b/packages/wallet-sdk/src/sign/scw/SCWSigner.test.ts
index 78471ed674..ce8f17c4ff 100644
--- a/packages/wallet-sdk/src/sign/scw/SCWSigner.test.ts
+++ b/packages/wallet-sdk/src/sign/scw/SCWSigner.test.ts
@@ -1,7 +1,5 @@
 import { Mock, Mocked, vi } from 'vitest';
 
-import { SCWKeyManager } from './SCWKeyManager.js';
-import { SCWSigner } from './SCWSigner.js';
 import { Communicator } from ':core/communicator/Communicator.js';
 import { CB_KEYS_URL } from ':core/constants.js';
 import { standardErrors } from ':core/error/errors.js';
@@ -15,6 +13,8 @@ import {
   importKeyFromHexString,
 } from ':util/cipher.js';
 import { fetchRPCRequest } from ':util/provider.js';
+import { SCWKeyManager } from './SCWKeyManager.js';
+import { SCWSigner } from './SCWSigner.js';
 
 vi.mock(':util/provider');
 vi.mock('./SCWKeyManager');
@@ -226,6 +226,11 @@ describe('SCWSigner', () => {
         },
         chains: [],
         keys: {},
+        config: {
+          metadata: mockMetadata,
+          preference: { keysUrl: CB_KEYS_URL, options: 'all' },
+          version: '1.0.0',
+        },
       }));
     });
 
diff --git a/packages/wallet-sdk/src/sign/scw/SCWSigner.ts b/packages/wallet-sdk/src/sign/scw/SCWSigner.ts
index 5badb8701b..4fb4c62144 100644
--- a/packages/wallet-sdk/src/sign/scw/SCWSigner.ts
+++ b/packages/wallet-sdk/src/sign/scw/SCWSigner.ts
@@ -9,7 +9,6 @@ import { WalletConnectResponse } from ':core/rpc/wallet_connect.js';
 import { Address } from ':core/type/index.js';
 import { ensureIntNumber, hexStringFromNumber } from ':core/type/util.js';
 import { SDKChain, createClients } from ':store/chain-clients/utils.js';
-import { config } from ':store/config.js';
 import { store } from ':store/store.js';
 import { assertPresence } from ':util/assertPresence.js';
 import { assertSubAccount } from ':util/assertSubAccount.js';
@@ -206,7 +205,7 @@ export class SCWSigner implements Signer {
   }
 
   async cleanup() {
-    const metadata = config.getState().metadata;
+    const metadata = store.config.get().metadata;
     await this.keyManager.clear();
 
     // clear the store
diff --git a/packages/wallet-sdk/src/store/config.ts b/packages/wallet-sdk/src/store/config.ts
deleted file mode 100644
index f791afbef0..0000000000
--- a/packages/wallet-sdk/src/store/config.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { createJSONStorage, persist } from 'zustand/middleware';
-import { createStore } from 'zustand/vanilla';
-
-import { VERSION } from '../sdk-info.js';
-import { AppMetadata, Preference } from ':core/provider/interface.js';
-
-export type ConfigState = {
-  metadata?: AppMetadata;
-  preference?: Preference;
-  version: string;
-};
-
-export const sdkconfig = createStore(
-  persist<ConfigState>(
-    () => ({
-      metadata: undefined,
-      preference: undefined,
-      version: VERSION,
-    }),
-    {
-      name: 'cbwsdk.config',
-      storage: createJSONStorage(() => localStorage),
-      partialize: (state: ConfigState) => ({
-        metadata: state.metadata,
-        preference: state.preference,
-        version: state.version,
-      }),
-    }
-  )
-);
-
-export const config = {
-  ...sdkconfig,
-};
diff --git a/packages/wallet-sdk/src/store/store.ts b/packages/wallet-sdk/src/store/store.ts
index c5babf90de..c9fc1ec7ed 100644
--- a/packages/wallet-sdk/src/store/store.ts
+++ b/packages/wallet-sdk/src/store/store.ts
@@ -1,7 +1,9 @@
-import { Address, Hex, LocalAccount, OneOf } from 'viem';
-import { WebAuthnAccount } from 'viem/account-abstraction';
-import { createJSONStorage, persist } from 'zustand/middleware';
-import { createStore, StateCreator } from 'zustand/vanilla';
+import { AppMetadata, Preference } from ":core/provider/interface.js";
+import { Address, Hex, LocalAccount, OneOf } from "viem";
+import { WebAuthnAccount } from "viem/account-abstraction";
+import { createJSONStorage, persist } from "zustand/middleware";
+import { StateCreator, createStore } from "zustand/vanilla";
+import { VERSION } from "../sdk-info.js";
 
 export type ToSubAccountSigner = () => Promise<{
   account: OneOf<LocalAccount | WebAuthnAccount> | null;
@@ -29,6 +31,12 @@ type Account = {
   chain?: Chain;
 };
 
+type Config = {
+  metadata?: AppMetadata;
+  preference?: Preference;
+  version: string;
+};
+
 type ChainSlice = {
   chains: Chain[];
 };
@@ -53,7 +61,12 @@ type AccountSlice = {
   account: Account;
 };
 
-const createAccountSlice: StateCreator<StoreState, [], [], AccountSlice> = () => {
+const createAccountSlice: StateCreator<
+  StoreState,
+  [],
+  [],
+  AccountSlice
+> = () => {
   return {
     account: {},
   };
@@ -63,14 +76,32 @@ type SubAccountSlice = {
   subAccount?: SubAccount;
 };
 
-const createSubAccountSlice: StateCreator<StoreState, [], [], SubAccountSlice> = () => {
+const createSubAccountSlice: StateCreator<
+  StoreState,
+  [],
+  [],
+  SubAccountSlice
+> = () => {
   return {
     subAccount: undefined,
   };
 };
 
+type ConfigSlice = {
+  config: Config;
+};
+
+const createConfigSlice: StateCreator<StoreState, [], [], ConfigSlice> = () => {
+  return {
+    config: {
+      version: VERSION,
+    },
+  };
+};
+
 type MergeTypes<T extends unknown[]> = T extends [infer First, ...infer Rest]
-  ? First & (Rest extends unknown[] ? MergeTypes<Rest> : Record<string, unknown>)
+  ? First &
+      (Rest extends unknown[] ? MergeTypes<Rest> : Record<string, unknown>)
   : Record<string, unknown>;
 
 export type StoreState = MergeTypes<
@@ -79,7 +110,8 @@ export type StoreState = MergeTypes<
     KeysSlice,
     AccountSlice,
     SubAccountSlice,
-    { toSubAccountSigner?: ToSubAccountSigner },
+    ConfigSlice,
+    { toSubAccountSigner?: ToSubAccountSigner }
   ]
 >;
 
@@ -90,10 +122,11 @@ export const sdkstore = createStore(
       ...createKeysSlice(...args),
       ...createAccountSlice(...args),
       ...createSubAccountSlice(...args),
+      ...createConfigSlice(...args),
       toSubAccountSigner: undefined,
     }),
     {
-      name: 'cbwsdk.store',
+      name: "cbwsdk.store",
       storage: createJSONStorage(() => localStorage),
       partialize: (state) => {
         // Explicitly select only the data properties we want to persist
@@ -103,6 +136,7 @@ export const sdkstore = createStore(
           keys: state.keys,
           account: state.account,
           subAccount: state.subAccount,
+          config: state.config,
         } as StoreState;
       },
     }
@@ -128,7 +162,9 @@ export const subAccounts = {
 export const account = {
   get: () => sdkstore.getState().account,
   set: (account: Partial<Account>) => {
-    sdkstore.setState((state) => ({ account: { ...state.account, ...account } }));
+    sdkstore.setState((state) => ({
+      account: { ...state.account, ...account },
+    }));
   },
   clear: () => {
     sdkstore.setState({
@@ -161,11 +197,19 @@ export const keys = {
   },
 };
 
+export const config = {
+  get: () => sdkstore.getState().config,
+  set: (config: Partial<Config>) => {
+    sdkstore.setState((state) => ({ config: { ...state.config, ...config } }));
+  },
+};
+
 const actions = {
   subAccounts,
   account,
   chains,
   keys,
+  config,
   setSubAccountSigner: (toSubAccountSigner: ToSubAccountSigner) => {
     sdkstore.setState({ toSubAccountSigner });
   },


Reviews:
user: login: stephancill
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T20:03:15Z
user: login: cb-jake
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T21:14:57Z
user: login: stephancill
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T12:56:50Z
user: login: stephancill
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T12:58:25Z
user: login: montycheese
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T13:32:20Z
user: login: stephancill
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T13:42:43Z
user: login: stephancill
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T13:45:38Z

Review comments:
diff_hunk: @@ -1,34 +0,0 @@
-import { createJSONStorage, persist } from 'zustand/middleware';
-import { createStore } from 'zustand/vanilla';
-
-import { VERSION } from '../sdk-info.js';
-import { AppMetadata, Preference } from ':core/provider/interface.js';
-
-export type ConfigState = {
-  metadata?: AppMetadata;
-  preference?: Preference;
-  version: string;
-};
-
-export const sdkconfig = createStore(
-  persist<ConfigState>(
-    () => ({
-      metadata: undefined,
-      preference: undefined,
-      version: VERSION,
-    }),
-    {
-      name: 'cbwsdk.config',
path: packages/wallet-sdk/src/store/config.ts
user: login: stephancill
body: does moving the config to different key break existing stored configs?
created_at: 2025-04-11T20:03:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,34 +0,0 @@
-import { createJSONStorage, persist } from 'zustand/middleware';
-import { createStore } from 'zustand/vanilla';
-
-import { VERSION } from '../sdk-info.js';
-import { AppMetadata, Preference } from ':core/provider/interface.js';
-
-export type ConfigState = {
-  metadata?: AppMetadata;
-  preference?: Preference;
-  version: string;
-};
-
-export const sdkconfig = createStore(
-  persist<ConfigState>(
-    () => ({
-      metadata: undefined,
-      preference: undefined,
-      version: VERSION,
-    }),
-    {
-      name: 'cbwsdk.config',
path: packages/wallet-sdk/src/store/config.ts
user: login: cb-jake
body: No this value might persist but no versions of the SDK have been published since this config store was added
created_at: 2025-04-11T21:14:57Z
author_association: CONTRIBUTOR

PR comments:
user: login: cb-heimdall
created_at: 2025-04-11T19:58:32Z
body: <h1>✅ Heimdall Review Status</h1>
<table>
<tr>
<th>Requirement</th>
<th>Status</th>
<th>More Info</th>
</tr>
<tr>
<td>Reviews</td>
<td>
✅
<code>1/1</code>
</td>
<td>
<details>
<summary>Denominator calculation</summary>
<table>
<tr>
<td>
<details>
<summary>Show calculation</summary>
<table>
<tr>
<td><code>1</code> if user is bot</td>
<td>0</td>
</tr>
<tr>
<td><code>1</code> if user is external</td>
<td>0</td>
</tr>
<tr>
<td>
From <code>.codeflow.yml</code>
</td>
<td>1</td>
</tr>
<tr>
<td>
Additional review requirements
<details>
<summary>Show calculation</summary>
<table>
<tr>
<td><strong>Max</strong></td>
<td><strong>0</strong></td>
</tr>
</table>
</details>
</td>
<td>0</td>
</tr>
<tr>
<td>
From <code>CODEOWNERS</code>
</td>
<td>0</td>
</tr>
<tr>
<td>Global minimum</td>
<td>0</td>
</tr>
<tr>
<td><strong>Max</strong></td>
<td>
<strong>
1
</strong>
</td>
</tr>
</table>
</details>
</td>
<td>1</td>
</tr>
<tr>
<td><code>1</code> if commit is unverified</td>
<td>0</td>
</tr>
<tr>
<td><strong>Sum</strong></td>
<td><strong>1</strong></td>
</tr>
</table>
</details>
</td>
</tr>
</table>
<!-- CODEOWNERS & Bar Raisers -->
<!-- Power Rangers -->
<!-- Links -->

author_association: COLLABORATOR
user: login: cb-heimdall
created_at: 2025-04-14T12:57:55Z
body: Review Error for stephancill @ 2025-04-14 12:57:55 UTC
User failed mfa authentication, public email is not set on your github profile. see [go/mfa-help](http://go/mfa-help)
author_association: COLLABORATOR
user: login: cb-heimdall
created_at: 2025-04-14T12:58:36Z
body: Review Error for stephancill @ 2025-04-14 12:58:36 UTC
User failed mfa authentication, public email is not set on your github profile. see [go/mfa-help](http://go/mfa-help)
author_association: COLLABORATOR
user: login: cb-heimdall
created_at: 2025-04-14T13:32:31Z
body: Review Error for montycheese @ 2025-04-14 13:32:31 UTC
User failed mfa authentication, see [go/mfa-help](http://go/mfa-help)
author_association: COLLABORATOR
user: login: cb-heimdall
created_at: 2025-04-14T13:43:47Z
body: Review Error for stephancill @ 2025-04-14 13:43:46 UTC
User failed mfa authentication, public email is not set on your github profile. see [go/mfa-help](http://go/mfa-help)
author_association: COLLABORATOR