Repository: dusk-network/rusk (language: rust)

Issue:
Decouple cache from the wallet store
#### Summary

The `Treasury` class should be the only one dealing with the cache.


Discussion / hints:

Fix patch:
diff --git a/web-wallet/src/lib/__mocks__/mockedWalletStore.js b/web-wallet/src/lib/__mocks__/mockedWalletStore.js
index 5463953502..6e798b73c5 100644
--- a/web-wallet/src/lib/__mocks__/mockedWalletStore.js
+++ b/web-wallet/src/lib/__mocks__/mockedWalletStore.js
@@ -12,11 +12,12 @@ const profiles = [
   await profileGenerator.next(),
   await profileGenerator.next(),
 ];
+
 const currentProfile = profiles[0];
 const shielded = { spendable: 50_000_000_000_000n, value: 2_345_000_000_000n };
 const unshielded = { nonce: 1234n, value: shielded.value / 2n };
 
-/** @type {WalletStoreContent} */
+/** @type {WalletStoreContent & { currentProfile: NonNullable<WalletStoreContent["currentProfile"]> }} */
 const content = {
   balance: { shielded, unshielded },
   currentProfile,
diff --git a/web-wallet/src/lib/stores/walletStore.js b/web-wallet/src/lib/stores/walletStore.js
index ed26eff3a7..ba2e4206fa 100644
--- a/web-wallet/src/lib/stores/walletStore.js
+++ b/web-wallet/src/lib/stores/walletStore.js
@@ -1,12 +1,11 @@
 import { get, writable } from "svelte/store";
-import { setKey, setPathIn } from "lamb";
+import { setKey } from "lamb";
 import {
   Bookkeeper,
   Bookmark,
   ProfileGenerator,
 } from "$lib/vendor/w3sper.js/src/mod";
 
-import walletCache from "$lib/wallet-cache";
 import WalletTreasury from "$lib/wallet-treasury";
 
 import { transactions } from "$lib/mock-data";
@@ -90,22 +89,18 @@ const updateCacheAfterTransaction = async (txInfo) => {
      * writing the pending notes info, as we'll
      * change soon how they are handled (probably by w3sper directly).
      */
-    await walletCache
-      .setPendingNoteInfo(txInfo.nullifiers, txInfo.hash)
+    await treasury
+      .updateCachedPendingNotes(txInfo.nullifiers, txInfo.hash)
       .catch(() => {});
   } else {
-    const address = String(getCurrentProfile()?.address);
-    const currentBalance = await walletCache.getBalanceInfo(address);
+    const profile = getCurrentProfile();
 
     /**
      * We update the stored `nonce` so that if a transaction is made
      * before the sync gives us an updated one, the transaction
      * won't be rejected by reusing the old value.
      */
-    await walletCache.setBalanceInfo(
-      address,
-      setPathIn(currentBalance, "unshielded.nonce", txInfo.nonce)
-    );
+    profile && (await treasury.updateCachedNonce(profile, txInfo.nonce));
   }
 
   return txInfo;
@@ -113,23 +108,21 @@ const updateCacheAfterTransaction = async (txInfo) => {
 
 /** @type {() => Promise<void>} */
 const updateBalance = async () => {
-  const { currentProfile } = get(walletStore);
+  const profile = getCurrentProfile();
 
-  if (!currentProfile) {
+  if (!profile) {
     return;
   }
 
-  const shielded = await bookkeeper.balance(currentProfile.address);
-  const unshielded = await bookkeeper.balance(currentProfile.account);
+  const shielded = await bookkeeper.balance(profile.address);
+  const unshielded = await bookkeeper.balance(profile.account);
   const balance = { shielded, unshielded };
 
   /**
    * We ignore the error as the cached balance is only
    * a nice to have for the user.
    */
-  await walletCache
-    .setBalanceInfo(currentProfile.address.toString(), balance)
-    .catch(() => {});
+  await treasury.setCachedBalance(profile, balance).catch(() => {});
 
   update((currentStore) => ({
     ...currentStore,
@@ -139,21 +132,20 @@ const updateBalance = async () => {
 
 /** @type {() => Promise<void>} */
 const updateStakeInfo = async () => {
-  const { currentProfile } = get(walletStore);
+  const profile = getCurrentProfile();
 
-  if (!currentProfile) {
+  if (!profile) {
     return;
   }
 
-  const stakeInfo = await bookkeeper.stakeInfo(currentProfile.account);
+  /** @type {StakeInfo} */
+  const stakeInfo = await bookkeeper.stakeInfo(profile.account);
 
   /**
    * We ignore the error as the cached stake info is only
    * a nice to have for the user.
    */
-  await walletCache
-    .setStakeInfo(currentProfile.account.toString(), stakeInfo)
-    .catch(() => {});
+  await treasury.setCachedStakeInfo(profile, stakeInfo).catch(() => {});
 
   update((currentStore) => ({
     ...currentStore,
@@ -172,10 +164,10 @@ const abortSync = () => {
 };
 
 /** @type {WalletStoreServices["clearLocalData"]} */
-const clearLocalData = () => {
+const clearLocalData = async () => {
   abortSync();
 
-  return walletCache.clear();
+  await treasury.clearCache();
 };
 
 /** @type {WalletStoreServices["clearLocalDataAndInit"]} */
@@ -202,11 +194,8 @@ const getTransactionsHistory = async () => transactions;
 /** @type {WalletStoreServices["init"]} */
 async function init(profileGenerator, syncFromBlock) {
   const currentProfile = await profileGenerator.default;
-  const currentAddress = currentProfile.address.toString();
-  const cachedBalance = await walletCache.getBalanceInfo(currentAddress);
-  const cachedStakeInfo = await walletCache.getStakeInfo(
-    currentProfile.account.toString()
-  );
+  const cachedBalance = await treasury.getCachedBalance(currentProfile);
+  const cachedStakeInfo = await treasury.getCachedStakeInfo(currentProfile);
   const minimumStake = await bookkeeper.minimumStake;
 
   treasury.setProfiles([currentProfile]);
@@ -223,7 +212,7 @@ async function init(profileGenerator, syncFromBlock) {
 
   sync(syncFromBlock)
     .then(() => {
-      settingsStore.update(setKey("userId", currentAddress));
+      settingsStore.update(setKey("userId", currentProfile.address.toString()));
     })
     .finally(updateStaticInfo);
 }
@@ -297,7 +286,7 @@ async function sync(fromBlock) {
     syncController = new AbortController();
 
     const { block, bookmark, lastFinalizedBlockHeight } =
-      await walletCache.getSyncInfo();
+      await treasury.getCachedSyncInfo();
 
     /** @type {bigint | Bookmark} */
     let from;
@@ -324,7 +313,7 @@ async function sync(fromBlock) {
     }
 
     if (from === 0n) {
-      await walletCache.clear();
+      await treasury.clearCache();
     }
 
     update((currentStore) => ({
diff --git a/web-wallet/src/lib/wallet-cache/index.js b/web-wallet/src/lib/wallet-cache/index.js
index d3ee4eac32..68766b5e3c 100644
--- a/web-wallet/src/lib/wallet-cache/index.js
+++ b/web-wallet/src/lib/wallet-cache/index.js
@@ -297,7 +297,7 @@ class WalletCache {
    * @param {string} txId
    * @returns {Promise<void>}
    */
-  async setPendingNoteInfo(nullifiers, txId) {
+  async setPendingNotesInfo(nullifiers, txId) {
     const data = nullifiers.map((nullifier) => ({ nullifier, txId }));
 
     await this.#db
diff --git a/web-wallet/src/lib/wallet-treasury/index.js b/web-wallet/src/lib/wallet-treasury/index.js
index 2817c6090f..135765a7d0 100644
--- a/web-wallet/src/lib/wallet-treasury/index.js
+++ b/web-wallet/src/lib/wallet-treasury/index.js
@@ -1,9 +1,11 @@
-import { map } from "lamb";
+import { map, setPathIn } from "lamb";
 
 import notesArrayToMap from "$lib/wallet/notesArrayToMap";
 import walletCache from "$lib/wallet-cache";
 import networkStore from "$lib/stores/networkStore";
 
+/** @typedef {import("$lib/vendor/w3sper.js/src/mod").Profile} Profile */
+
 class WalletTreasury {
   /** @type {AccountBalance[]} */
   #accountBalances = [];
@@ -35,13 +37,13 @@ class WalletTreasury {
     };
   }
 
-  /** @param {Array<import("$lib/vendor/w3sper.js/src/mod").Profile>} profiles */
+  /** @param {Profile[]} profiles */
   constructor(profiles = []) {
     this.#profiles = profiles;
   }
 
   /**
-   * @param {import("$lib/vendor/w3sper.js/src/mod").Profile["address"]} identifier
+   * @param {Profile["account"]} identifier
    * @returns {Promise<AccountBalance>}
    */
   async account(identifier) {
@@ -56,7 +58,7 @@ class WalletTreasury {
   }
 
   /**
-   * @param {import("$lib/vendor/w3sper.js/src/mod").Profile["address"]} identifier
+   * @param {Profile["address"]} identifier
    * @returns {Promise<Map<Uint8Array, Uint8Array>>}
    */
   async address(identifier) {
@@ -79,13 +81,59 @@ class WalletTreasury {
       : new Map();
   }
 
-  /** @param {Array<import("$lib/vendor/w3sper.js/src/mod").Profile>} profiles */
+  /** @returns {Promise<void>} */
+  async clearCache() {
+    await walletCache.clear();
+  }
+
+  /**
+   * @param {Profile} profile
+   * @returns {Promise<WalletCacheBalanceInfo["balance"]>}
+   */
+  async getCachedBalance(profile) {
+    return await walletCache.getBalanceInfo(profile.address.toString());
+  }
+
+  /**
+   * @param {Profile} profile
+   * @returns {Promise<StakeInfo>}
+   */
+  async getCachedStakeInfo(profile) {
+    return await walletCache.getStakeInfo(profile.account.toString());
+  }
+
+  /**
+   * @returns {Promise<WalletCacheSyncInfo>}
+   */
+  async getCachedSyncInfo() {
+    return await walletCache.getSyncInfo();
+  }
+
+  /**
+   * @param {Profile} profile
+   * @param {WalletCacheBalanceInfo["balance"]} balance
+   * @returns {Promise<void>}
+   */
+  async setCachedBalance(profile, balance) {
+    await walletCache.setBalanceInfo(profile.address.toString(), balance);
+  }
+
+  /**
+   * @param {Profile} profile
+   * @param {StakeInfo} stakeInfo
+   * @returns {Promise<void>}
+   */
+  async setCachedStakeInfo(profile, stakeInfo) {
+    await walletCache.setStakeInfo(profile.account.toString(), stakeInfo);
+  }
+
+  /** @param {Profile[]} profiles */
   setProfiles(profiles) {
     this.#profiles = profiles;
   }
 
   /**
-   * @param {import("$lib/vendor/w3sper.js/src/mod").Profile["account"]} identifier
+   * @param {Profile["account"]} identifier
    * @returns {Promise<StakeInfo>}
    */
   async stakeInfo(identifier) {
@@ -226,6 +274,30 @@ class WalletTreasury {
     // @ts-ignore
     addressSyncer.removeEventListener("synciteration", syncIterationListener);
   }
+
+  /**
+   * @param {Profile} profile
+   * @param {bigint} nonce
+   * @returns {Promise<void>}
+   */
+  async updateCachedNonce(profile, nonce) {
+    const address = profile.address.toString();
+    const currentBalance = await walletCache.getBalanceInfo(address);
+
+    await walletCache.setBalanceInfo(
+      address,
+      setPathIn(currentBalance, "unshielded.nonce", nonce)
+    );
+  }
+
+  /**
+   * @param {Uint8Array[]} nullifiers
+   * @param {string} hash
+   * @returns {Promise<void>}
+   */
+  async updateCachedPendingNotes(nullifiers, hash) {
+    await walletCache.setPendingNotesInfo(nullifiers, hash);
+  }
 }
 
 export default WalletTreasury;


Test patch:
diff --git a/web-wallet/src/lib/dusk/test-helpers/mockReadableStore.js b/web-wallet/src/lib/dusk/test-helpers/mockReadableStore.js
index 57a8c59ee9..965397a094 100644
--- a/web-wallet/src/lib/dusk/test-helpers/mockReadableStore.js
+++ b/web-wallet/src/lib/dusk/test-helpers/mockReadableStore.js
@@ -1,12 +1,15 @@
 import { get, writable } from "svelte/store";
 
-/** @param {*} initialValue */
+/**
+ * @template T
+ * @param {T} initialValue
+ */
 function mockReadableStore(initialValue) {
   const store = writable(initialValue);
   const { set, subscribe } = store;
   const getMockedStoreValue = () => get(store);
 
-  /** @param {*} value */
+  /** @param {T} value */
   const setMockedStoreValue = (value) => set(value);
 
   return {
diff --git a/web-wallet/src/lib/stores/__tests__/walletStore.spec.js b/web-wallet/src/lib/stores/__tests__/walletStore.spec.js
index ef8e9c84ca..7eee8603ee 100644
--- a/web-wallet/src/lib/stores/__tests__/walletStore.spec.js
+++ b/web-wallet/src/lib/stores/__tests__/walletStore.spec.js
@@ -18,7 +18,6 @@ import { generateMnemonic } from "bip39";
 
 import { stakeInfo } from "$lib/mock-data";
 
-import walletCache from "$lib/wallet-cache";
 import WalletTreasury from "$lib/wallet-treasury";
 import { getSeedFromMnemonic } from "$lib/wallet";
 
@@ -83,16 +82,16 @@ describe("Wallet store", async () => {
     .mockImplementation(async () => stakeInfo);
 
   const getCachedBalanceSpy = vi
-    .spyOn(walletCache, "getBalanceInfo")
+    .spyOn(WalletTreasury.prototype, "getCachedBalance")
     .mockResolvedValue(cachedBalance);
   const setCachedBalanceSpy = vi
-    .spyOn(walletCache, "setBalanceInfo")
+    .spyOn(WalletTreasury.prototype, "setCachedBalance")
     .mockResolvedValue(undefined);
   const getCachedStakeInfoSpy = vi
-    .spyOn(walletCache, "getStakeInfo")
+    .spyOn(WalletTreasury.prototype, "getCachedStakeInfo")
     .mockResolvedValue(cachedStakeInfo);
   const setCachedStakeInfoSpy = vi
-    .spyOn(walletCache, "setStakeInfo")
+    .spyOn(WalletTreasury.prototype, "setCachedStakeInfo")
     .mockResolvedValue(undefined);
   const setProfilesSpy = vi.spyOn(WalletTreasury.prototype, "setProfiles");
   const treasuryUpdateSpy = vi.spyOn(WalletTreasury.prototype, "update");
@@ -180,13 +179,9 @@ describe("Wallet store", async () => {
       });
 
       expect(getCachedBalanceSpy).toHaveBeenCalledTimes(1);
-      expect(getCachedBalanceSpy).toHaveBeenCalledWith(
-        defaultProfile.address.toString()
-      );
+      expect(getCachedBalanceSpy).toHaveBeenCalledWith(defaultProfile);
       expect(getCachedStakeInfoSpy).toHaveBeenCalledTimes(1);
-      expect(getCachedStakeInfoSpy).toHaveBeenCalledWith(
-        defaultProfile.account.toString()
-      );
+      expect(getCachedStakeInfoSpy).toHaveBeenCalledWith(defaultProfile);
 
       await vi.advanceTimersByTimeAsync(AUTO_SYNC_INTERVAL - 1);
 
@@ -209,16 +204,13 @@ describe("Wallet store", async () => {
         treasuryUpdateSpy.mock.invocationCallOrder[0]
       );
       expect(setCachedBalanceSpy).toHaveBeenCalledTimes(1);
-      expect(setCachedBalanceSpy).toHaveBeenCalledWith(
-        defaultProfile.address.toString(),
-        {
-          shielded: await balanceSpy.mock.results[0].value,
-          unshielded: await balanceSpy.mock.results[1].value,
-        }
-      );
+      expect(setCachedBalanceSpy).toHaveBeenCalledWith(defaultProfile, {
+        shielded: await balanceSpy.mock.results[0].value,
+        unshielded: await balanceSpy.mock.results[1].value,
+      });
       expect(setCachedStakeInfoSpy).toHaveBeenCalledTimes(1);
       expect(setCachedStakeInfoSpy).toHaveBeenCalledWith(
-        defaultProfile.account.toString(),
+        defaultProfile,
         await stakeInfoSpy.mock.results[0].value
       );
       expect(setCachedBalanceSpy.mock.invocationCallOrder[0]).toBeGreaterThan(
@@ -326,7 +318,16 @@ describe("Wallet store", async () => {
     const executeSpy = vi
       .spyOn(Network.prototype, "execute")
       .mockResolvedValue(phoenixTxResult);
-    const setPendingNotesSpy = vi.spyOn(walletCache, "setPendingNoteInfo");
+
+    const updateNonceSpy = vi.spyOn(
+      WalletTreasury.prototype,
+      "updateCachedNonce"
+    );
+
+    const updateCachedPendingNotesSpy = vi.spyOn(
+      WalletTreasury.prototype,
+      "updateCachedPendingNotes"
+    );
 
     /**
      * @typedef { "claimRewards" | "shield" | "stake" | "transfer" | "unshield" | "unstake" } TransferMethod
@@ -347,9 +348,10 @@ describe("Wallet store", async () => {
       clearTimeoutSpy.mockRestore();
       vi.useRealTimers();
 
-      const currentlyCachedBalance = await walletCache.getBalanceInfo(
-        defaultProfile.address.toString()
-      );
+      const currentlyCachedBalance =
+        await new WalletTreasury().getCachedBalance(
+          defaultProfile.address.toString()
+        );
       const newNonce = currentlyCachedBalance.unshielded.nonce + 1n;
 
       let expectedTx;
@@ -400,26 +402,17 @@ describe("Wallet store", async () => {
       );
 
       if (isPhoenixTransfer) {
-        expect(setCachedBalanceSpy).not.toHaveBeenCalled();
-        expect(setPendingNotesSpy).toHaveBeenCalledTimes(1);
-        expect(setPendingNotesSpy).toHaveBeenCalledWith(
+        expect(updateNonceSpy).not.toHaveBeenCalled();
+        expect(updateCachedPendingNotesSpy).toHaveBeenCalledTimes(1);
+        expect(updateCachedPendingNotesSpy).toHaveBeenCalledWith(
           phoenixTxResult.nullifiers,
           phoenixTxResult.hash
         );
-        setPendingNotesSpy.mockClear();
+        updateCachedPendingNotesSpy.mockClear();
       } else {
-        expect(setCachedBalanceSpy).toHaveBeenCalledTimes(1);
-        expect(setCachedBalanceSpy).toHaveBeenCalledWith(
-          defaultProfile.address.toString(),
-          {
-            ...currentlyCachedBalance,
-            unshielded: {
-              ...currentlyCachedBalance.unshielded,
-              nonce: newNonce,
-            },
-          }
-        );
-        expect(setPendingNotesSpy).not.toHaveBeenCalled();
+        expect(updateNonceSpy).toHaveBeenCalledTimes(1);
+        expect(updateNonceSpy).toHaveBeenCalledWith(defaultProfile, newNonce);
+        expect(updateCachedPendingNotesSpy).not.toHaveBeenCalled();
         setCachedBalanceSpy.mockClear();
       }
 
@@ -464,16 +457,13 @@ describe("Wallet store", async () => {
         treasuryUpdateSpy.mock.invocationCallOrder[1]
       );
       expect(setCachedBalanceSpy).toHaveBeenCalledTimes(1);
-      expect(setCachedBalanceSpy).toHaveBeenCalledWith(
-        defaultProfile.address.toString(),
-        {
-          shielded: await balanceSpy.mock.results[0].value,
-          unshielded: await balanceSpy.mock.results[1].value,
-        }
-      );
+      expect(setCachedBalanceSpy).toHaveBeenCalledWith(defaultProfile, {
+        shielded: await balanceSpy.mock.results[0].value,
+        unshielded: await balanceSpy.mock.results[1].value,
+      });
       expect(setCachedStakeInfoSpy).toHaveBeenCalledTimes(1);
       expect(setCachedStakeInfoSpy).toHaveBeenCalledWith(
-        defaultProfile.account.toString(),
+        defaultProfile,
         await stakeInfoSpy.mock.results[0].value
       );
       expect(setCachedBalanceSpy.mock.invocationCallOrder[0]).toBeGreaterThan(
@@ -512,12 +502,14 @@ describe("Wallet store", async () => {
 
     afterEach(async () => {
       executeSpy.mockClear();
-      setPendingNotesSpy.mockClear();
+      updateNonceSpy.mockClear();
+      updateCachedPendingNotesSpy.mockClear();
     });
 
     afterAll(() => {
       executeSpy.mockRestore();
-      setPendingNotesSpy.mockRestore();
+      updateNonceSpy.mockRestore();
+      updateCachedPendingNotesSpy.mockRestore();
     });
 
     it("should expose a method to claim the rewards", async () => {
@@ -550,7 +542,7 @@ describe("Wallet store", async () => {
   });
 
   describe("Wallet store services", () => {
-    const cacheClearSpy = vi.spyOn(walletCache, "clear");
+    const cacheClearSpy = vi.spyOn(WalletTreasury.prototype, "clearCache");
 
     beforeEach(async () => {
       walletStore.reset();
@@ -651,16 +643,13 @@ describe("Wallet store", async () => {
       expect(stakeInfoSpy).toHaveBeenCalledTimes(1);
       expect(stakeInfoSpy).toHaveBeenCalledWith(fakeExtraProfile.account);
       expect(setCachedBalanceSpy).toHaveBeenCalledTimes(1);
-      expect(setCachedBalanceSpy).toHaveBeenCalledWith(
-        fakeExtraProfile.address.toString(),
-        {
-          shielded: await balanceSpy.mock.results[0].value,
-          unshielded: await balanceSpy.mock.results[1].value,
-        }
-      );
+      expect(setCachedBalanceSpy).toHaveBeenCalledWith(fakeExtraProfile, {
+        shielded: await balanceSpy.mock.results[0].value,
+        unshielded: await balanceSpy.mock.results[1].value,
+      });
       expect(setCachedStakeInfoSpy).toHaveBeenCalledTimes(1);
       expect(setCachedStakeInfoSpy).toHaveBeenCalledWith(
-        fakeExtraProfile.account.toString(),
+        fakeExtraProfile,
         await stakeInfoSpy.mock.results[0].value
       );
       expect(setCachedBalanceSpy.mock.invocationCallOrder[0]).toBeGreaterThan(
diff --git a/web-wallet/src/lib/wallet-cache/__tests__/index.spec.js b/web-wallet/src/lib/wallet-cache/__tests__/index.spec.js
index a778576189..2d89110c5d 100644
--- a/web-wallet/src/lib/wallet-cache/__tests__/index.spec.js
+++ b/web-wallet/src/lib/wallet-cache/__tests__/index.spec.js
@@ -632,7 +632,7 @@ describe("Wallet cache", () => {
       ).resolves.toStrictEqual(modifiedBalance);
     });
 
-    it("should expose a method to set a note as pending", async () => {
+    it("should expose a method to set a group of notes as pending", async () => {
       const existingPendingNullifiersAsStrings = await walletCache
         .getPendingNotesInfo()
         .then(mapWith(compose(String, getKey("nullifier"))));
@@ -660,7 +660,7 @@ describe("Wallet cache", () => {
         }))
       );
 
-      await walletCache.setPendingNoteInfo(spendableNullifiers, "some-tx-id");
+      await walletCache.setPendingNotesInfo(spendableNullifiers, "some-tx-id");
 
       await expect(
         walletCache
diff --git a/web-wallet/src/lib/wallet-treasury/__tests__/index.spec.js b/web-wallet/src/lib/wallet-treasury/__tests__/index.spec.js
index efa5c88a39..2d3d072895 100644
--- a/web-wallet/src/lib/wallet-treasury/__tests__/index.spec.js
+++ b/web-wallet/src/lib/wallet-treasury/__tests__/index.spec.js
@@ -8,7 +8,7 @@ import {
   it,
   vi,
 } from "vitest";
-import { mapWith, pluckFrom } from "lamb";
+import { mapValues, mapWith, multiplyBy, pluckFrom } from "lamb";
 
 import mockedWalletStore from "$lib/__mocks__/mockedWalletStore";
 
@@ -21,6 +21,7 @@ import {
 import networkStore from "$lib/stores/networkStore";
 
 import WalletTreasury from "..";
+import walletCache from "$lib/wallet-cache";
 
 describe("WalletTreasury", () => {
   /** @type {WalletTreasury} */
@@ -179,4 +180,118 @@ describe("WalletTreasury", () => {
       ).rejects.toThrow();
     });
   });
+
+  describe("Cache access methods", () => {
+    it("should expose a method to clear the local cache", async () => {
+      // Right now we just call the cache method,
+      // so we won't repeat the wallet-cache test here.
+      const clearCacheSpy = vi
+        .spyOn(walletCache, "clear")
+        .mockResolvedValue(undefined);
+
+      await walletTreasury.clearCache();
+
+      expect(clearCacheSpy).toHaveBeenCalledTimes(1);
+
+      clearCacheSpy.mockRestore();
+    });
+
+    it("should expose a method to get the cached balance", async () => {
+      const expected = await walletCache.getBalanceInfo(
+        profiles[0].address.toString()
+      );
+
+      await expect(
+        walletTreasury.getCachedBalance(profiles[0])
+      ).resolves.toStrictEqual(expected);
+    });
+
+    it("should expose a method to get the cached sync info", async () => {
+      const expected = await walletCache.getSyncInfo();
+
+      await expect(walletTreasury.getCachedSyncInfo()).resolves.toStrictEqual(
+        expected
+      );
+    });
+
+    it("should expose a method to get the cached stake info", async () => {
+      const expected = await walletCache.getStakeInfo(
+        profiles[0].address.toString()
+      );
+
+      await expect(
+        walletTreasury.getCachedStakeInfo(profiles[0])
+      ).resolves.toStrictEqual(expected);
+    });
+
+    it("should expose a method to cache the balance of a given profile", async () => {
+      const address = profiles[0].address.toString();
+      const currentBalance = await walletCache.getBalanceInfo(address);
+      const newBalance = {
+        shielded: mapValues(currentBalance.shielded, multiplyBy(2n)),
+        unshielded: mapValues(currentBalance.unshielded, multiplyBy(3n)),
+      };
+
+      await walletTreasury.setCachedBalance(profiles[0], newBalance);
+
+      await expect(walletCache.getBalanceInfo(address)).resolves.toStrictEqual(
+        newBalance
+      );
+    });
+
+    it("should expose a method to cache the stake info", async () => {
+      // Right now we just call the cache method,
+      // so we won't repeat the wallet-cache test here.
+      const setCachedStakeInfoSpy = vi
+        .spyOn(walletCache, "setStakeInfo")
+        .mockResolvedValue(undefined);
+
+      const fakeStakeInfo = {};
+
+      // @ts-ignore we don't care to pass the correct type here
+      await walletTreasury.setCachedStakeInfo(profiles[0], fakeStakeInfo);
+
+      expect(setCachedStakeInfoSpy).toHaveBeenCalledTimes(1);
+      expect(setCachedStakeInfoSpy).toHaveBeenCalledWith(
+        profiles[0].account.toString(),
+        fakeStakeInfo
+      );
+
+      setCachedStakeInfoSpy.mockRestore();
+    });
+
+    it("should expose a method to add notes to the pending notes cache", async () => {
+      // Right now we just call the cache method,
+      // so we won't repeat the wallet-cache test here.
+      const setPendingNotesSpy = vi
+        .spyOn(walletCache, "setPendingNotesInfo")
+        .mockResolvedValue(undefined);
+
+      const nullifiers = [new Uint8Array().fill(0)];
+      const txHash = "some-tx-hash";
+
+      await walletTreasury.updateCachedPendingNotes(nullifiers, txHash);
+
+      expect(setPendingNotesSpy).toHaveBeenCalledTimes(1);
+      expect(setPendingNotesSpy).toHaveBeenCalledWith(nullifiers, txHash);
+
+      setPendingNotesSpy.mockRestore();
+    });
+
+    it("should expose a method to update the unshielded nonce", async () => {
+      const address = profiles[0].address.toString();
+      const currentlyCachedBalance = await walletCache.getBalanceInfo(address);
+      const newNonce = currentlyCachedBalance.unshielded.nonce + 1n;
+
+      await walletTreasury.updateCachedNonce(profiles[0], newNonce);
+
+      await expect(walletCache.getBalanceInfo(address)).resolves.toStrictEqual({
+        ...currentlyCachedBalance,
+        unshielded: {
+          ...currentlyCachedBalance.unshielded,
+          nonce: newNonce,
+        },
+      });
+    });
+  });
 });
diff --git a/web-wallet/src/routes/(app)/settings/__tests__/page.spec.js b/web-wallet/src/routes/(app)/settings/__tests__/page.spec.js
index 3ea5ef080c..33b797464a 100644
--- a/web-wallet/src/routes/(app)/settings/__tests__/page.spec.js
+++ b/web-wallet/src/routes/(app)/settings/__tests__/page.spec.js
@@ -93,7 +93,10 @@ describe("Settings", () => {
     await act(() => {
       mockedWalletStore.setMockedStoreValue({
         ...initialWalletStoreState,
-        syncStatus: { isInProgress: true },
+        syncStatus: {
+          ...initialWalletStoreState.syncStatus,
+          isInProgress: true,
+        },
       });
     });