Skip to content

Commit eccb7dc

Browse files
authored
refactor: extract query from hook files & implement proper query lifecycle + tests (#58)
* refactor: extract tanstack queries from hooks fix(test): fix activity-feed decryptHandles mock setup refactor token(react-sdk): remove local query key aliases fix(query): align query exports and fee manager key typing refactor(sdk): deduplicate ZERO_HANDLE source refactor(react-sdk): dedupe optimistic callback context unwrapping 🐛 fix(tests): extend timeouts for PBKDF2-heavy credential tests Credentials-manager tests with 2+ allow() calls and events.test.ts CredentialsExpired test run PBKDF2 with 600k iterations per encrypt/decrypt operation, which can exceed the default 5000ms vitest timeout on CI. Increase affected tests to 30000ms to prevent flaky timeouts. chore: api fix missing invalidation chore: bump size limits fix: jsdoc fix: address query hook findings fix query lifecycle and public surfaces * minify bundle size * chore: rebase fixes * chore: address review * chore: rename normalizeAddress * refactor: align QueryFactoryOptions with TanStack types Derive QueryFactoryOptions from QueryObserverOptions (wagmi pattern) instead of a custom hand-rolled interface. This removes the `as unknown as UseQueryOptions` double-cast from every React hook. - Rewrite QueryFactoryOptions to wrap QueryObserverOptions from @tanstack/query-core, making queryKey required and queryFn non-optional - Delete QueryContext (replaced by TanStack's QueryFunctionContext) - Add shared useQuery/useSuspenseQuery wrapper in react-sdk that injects queryKeyHashFn in one place (single `as any`, wagmi pattern) - Remove all `as unknown as UseQueryOptions<...>` casts from 11 hooks - Add mockQueryContext helper and update 15 test files to pass real TanStack context shape (client, signal, meta) without `as any` * chore: api-report
1 parent a830251 commit eccb7dc

190 files changed

Lines changed: 10385 additions & 4036 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.size-limit.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
"name": "@zama-fhe/sdk (all JS)",
44
"path": "packages/sdk/dist/**/*.js",
5-
"limit": "40 KB"
5+
"limit": "42 KB"
66
},
77
{
88
"name": "@zama-fhe/react-sdk (all JS)",

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ The token is refreshed before each encrypt/decrypt call.
633633
| `DecryptionFailedError` after page reload | Unshield interrupted mid-flow | Use `loadPendingUnshield()` on mount to detect and `resumeUnshield()` to complete the finalize step. |
634634
| Duplicate wallet popups | Credentials not cached or expired | Call `useTokenAllow(tokenAddresses)` once on load to batch-allow all tokens in a single signature. |
635635
| `TransactionRevertedError` on unshield finalize | Unwrap event not found or already finalized | Check the unwrap tx hash — if the tx was already finalized, clear the pending state with `clearPendingUnshield()`. |
636-
| Balance not updating after transfer | Handle polling interval too long | Mutation hooks auto-invalidate caches, but if using direct contract calls, manually invalidate `confidentialBalanceQueryKeys`. |
636+
| Balance not updating after transfer | Handle polling interval too long | Mutation hooks auto-invalidate caches, but if using direct contract calls, manually invalidate `zamaQueryKeys.confidentialBalance`. |
637637
| "Decrypting..." spinner on every page reload | Storage backend not persisting | Decrypted balances are cached in storage. Ensure you're using `indexedDBStorage` (browser) or `asyncLocalStorage` (Node.js), not `memoryStorage`. |
638638
| `"Cannot find module @zama-fhe/sdk"` | Missing or unbuilt dependency | Run `pnpm build` from the monorepo root — `react-sdk` depends on built `sdk` output. |
639639
| React hydration mismatch | Server tried to render FHE hooks | Add `"use client"` directive to any component using SDK hooks. FHE operations require browser APIs. |

docs/gitbook/src/guides/react-sdk/hooks.md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,15 @@ Mutations automatically invalidate balance caches, but if you need manual contro
3737

3838
```tsx
3939
import { useQueryClient } from "@tanstack/react-query";
40-
import { confidentialBalanceQueryKeys } from "@zama-fhe/react-sdk";
40+
import { zamaQueryKeys } from "@zama-fhe/react-sdk";
4141

4242
const queryClient = useQueryClient();
4343

4444
// Invalidate everything
45-
queryClient.invalidateQueries({ queryKey: confidentialBalanceQueryKeys.all });
45+
queryClient.invalidateQueries({ queryKey: zamaQueryKeys.confidentialBalance.all });
4646

4747
// Invalidate one token
48-
queryClient.invalidateQueries({
49-
queryKey: confidentialBalanceQueryKeys.token("0xToken"),
50-
});
48+
queryClient.invalidateQueries({ queryKey: zamaQueryKeys.confidentialBalance.token("0xToken") });
5149
```
5250

5351
## Transfers
@@ -365,14 +363,14 @@ function ErrorMessage({ error }: { error: Error | null }) {
365363

366364
For manual cache control (prefetching, invalidation, removal):
367365

368-
| Factory | Keys | What it controls |
369-
| ------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------ |
370-
| `confidentialBalanceQueryKeys` | `.all`, `.token(addr)`, `.owner(addr, owner)` | Single-token decrypted balance |
371-
| `confidentialBalancesQueryKeys` | `.all`, `.tokens(addrs, owner)` | Multi-token batch balances |
372-
| `confidentialHandleQueryKeys` | `.all`, `.token(addr)`, `.owner(addr, owner)` | Single-token encrypted handle |
373-
| `confidentialHandlesQueryKeys` | `.all`, `.tokens(addrs, owner)` | Multi-token batch handles |
374-
| `isAllowedQueryKeys` | `.all`, `.token(addr)` | Session signature status |
375-
| `underlyingAllowanceQueryKeys` | `.all`, `.token(addr, wrapper)` | ERC-20 allowance |
376-
| `activityFeedQueryKeys` | `.all`, `.token(addr)` | Activity feed |
377-
| `feeQueryKeys` | `.shieldFee(...)`, `.unshieldFee(...)`, `.batchTransferFee(...)`, `.feeRecipient(...)` | Fee queries |
378-
| `decryptionKeys` | `.value(handle)` | Cached decrypted values |
366+
| Factory | Keys | What it controls |
367+
| ------------------------------------ | -------------------------------------------------------------------------------------- | ------------------------------ |
368+
| `zamaQueryKeys.confidentialBalance` | `.all`, `.token(addr)`, `.owner(addr, owner)` | Single-token decrypted balance |
369+
| `zamaQueryKeys.confidentialBalances` | `.all`, `.tokens(addrs, owner)` | Multi-token batch balances |
370+
| `zamaQueryKeys.confidentialHandle` | `.all`, `.token(addr)`, `.owner(addr, owner)` | Single-token encrypted handle |
371+
| `zamaQueryKeys.confidentialHandles` | `.all`, `.tokens(addrs, owner)` | Multi-token batch handles |
372+
| `zamaQueryKeys.isAllowed` | `.all` | Session signature status |
373+
| `zamaQueryKeys.underlyingAllowance` | `.all`, `.token(addr)`, `.scope(addr, owner, wrapper)` | ERC-20 allowance |
374+
| `zamaQueryKeys.activityFeed` | `.all`, `.token(addr)`, `.scope(addr, userAddress, logsKey, decrypt)` | Activity feed |
375+
| `zamaQueryKeys.fees` | `.shieldFee(...)`, `.unshieldFee(...)`, `.batchTransferFee(...)`, `.feeRecipient(...)` | Fee queries |
376+
| `decryptionKeys` | `.value(handle)` | Cached decrypted values |

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@
4141
"docs:build": "mdbook build docs/gitbook",
4242
"typedoc": "typedoc",
4343
"api-report": "pnpm build && pnpm api-report:sdk && pnpm api-report:react-sdk",
44-
"api-report:sdk": "cd packages/sdk && api-extractor run --local -c api-extractor.json && api-extractor run --local -c api-extractor.viem.json && api-extractor run --local -c api-extractor.ethers.json && api-extractor run --local -c api-extractor.node.json",
44+
"api-report:sdk": "cd packages/sdk && api-extractor run --local -c api-extractor.json && api-extractor run --local -c api-extractor.viem.json && api-extractor run --local -c api-extractor.ethers.json && api-extractor run --local -c api-extractor.node.json && api-extractor run --local -c api-extractor.query.json",
4545
"api-report:react-sdk": "cd packages/react-sdk && api-extractor run --local -c api-extractor.json && api-extractor run --local -c api-extractor.wagmi.json",
4646
"api-report:check": "pnpm build && pnpm api-report:check:sdk && pnpm api-report:check:react-sdk",
47-
"api-report:check:sdk": "cd packages/sdk && api-extractor run -c api-extractor.json && api-extractor run -c api-extractor.viem.json && api-extractor run -c api-extractor.ethers.json && api-extractor run -c api-extractor.node.json",
47+
"api-report:check:sdk": "cd packages/sdk && api-extractor run -c api-extractor.json && api-extractor run -c api-extractor.viem.json && api-extractor run -c api-extractor.ethers.json && api-extractor run -c api-extractor.node.json && api-extractor run -c api-extractor.query.json",
4848
"api-report:check:react-sdk": "cd packages/react-sdk && api-extractor run -c api-extractor.json && api-extractor run -c api-extractor.wagmi.json",
4949
"size": "size-limit",
5050
"size:json": "size-limit --json"

packages/react-sdk/README.md

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ function useConfidentialIsApproved(
598598

599599
#### `useUnderlyingAllowance`
600600

601-
Read the ERC-20 allowance of the underlying token for the wrapper.
601+
Read the underlying ERC-20 allowance granted to the wrapper.
602602

603603
```ts
604604
function useUnderlyingAllowance(
@@ -794,46 +794,36 @@ const { data: value } = useUserDecryptedValue("0xHandleHash");
794794

795795
## Query Keys
796796

797-
Exported query key factories for manual cache management (invalidation, prefetching, removal).
797+
Use `zamaQueryKeys` for manual cache management (invalidation, prefetching, removal).
798798

799799
```ts
800-
import {
801-
confidentialBalanceQueryKeys,
802-
confidentialBalancesQueryKeys,
803-
confidentialHandleQueryKeys,
804-
confidentialHandlesQueryKeys,
805-
isAllowedQueryKeys,
806-
underlyingAllowanceQueryKeys,
807-
activityFeedQueryKeys,
808-
feeQueryKeys,
809-
decryptionKeys,
810-
} from "@zama-fhe/react-sdk";
800+
import { zamaQueryKeys, decryptionKeys } from "@zama-fhe/react-sdk";
811801
```
812802

813-
| Factory | Keys | Description |
814-
| ------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------- |
815-
| `confidentialBalanceQueryKeys` | `.all`, `.token(address)`, `.owner(address, owner)` | Single-token decrypted balance. |
816-
| `confidentialBalancesQueryKeys` | `.all`, `.tokens(addresses, owner)` | Multi-token batch balances. |
817-
| `confidentialHandleQueryKeys` | `.all`, `.token(address)`, `.owner(address, owner)` | Single-token encrypted handle. |
818-
| `confidentialHandlesQueryKeys` | `.all`, `.tokens(addresses, owner)` | Multi-token batch handles. |
819-
| `isAllowedQueryKeys` | `.all`, `.token(address)` | Session signature status. |
820-
| `underlyingAllowanceQueryKeys` | `.all`, `.token(address, wrapper)` | Underlying ERC-20 allowance. |
821-
| `activityFeedQueryKeys` | `.all`, `.token(address)` | Activity feed items. |
822-
| `feeQueryKeys` | `.shieldFee(...)`, `.unshieldFee(...)`, `.batchTransferFee(addr)`, `.feeRecipient(addr)` | Fee manager queries. |
823-
| `decryptionKeys` | `.value(handle)` | Individual decrypted handle values. |
803+
| Factory | Keys | Description |
804+
| ------------------------------------ | ---------------------------------------------------------------------------------------- | ----------------------------------- |
805+
| `zamaQueryKeys.confidentialBalance` | `.all`, `.token(address)`, `.owner(address, owner)` | Single-token decrypted balance. |
806+
| `zamaQueryKeys.confidentialBalances` | `.all`, `.tokens(addresses, owner)` | Multi-token batch balances. |
807+
| `zamaQueryKeys.confidentialHandle` | `.all`, `.token(address)`, `.owner(address, owner)` | Single-token encrypted handle. |
808+
| `zamaQueryKeys.confidentialHandles` | `.all`, `.tokens(addresses, owner)` | Multi-token batch handles. |
809+
| `zamaQueryKeys.isAllowed` | `.all` | Session signature status. |
810+
| `zamaQueryKeys.underlyingAllowance` | `.all`, `.token(address)`, `.scope(address, owner, wrapper)` | Underlying ERC-20 allowance. |
811+
| `zamaQueryKeys.activityFeed` | `.all`, `.token(address)`, `.scope(address, userAddress, logsKey, decrypt)` | Activity feed items. |
812+
| `zamaQueryKeys.fees` | `.shieldFee(...)`, `.unshieldFee(...)`, `.batchTransferFee(addr)`, `.feeRecipient(addr)` | Fee manager queries. |
813+
| `decryptionKeys` | `.value(handle)` | Individual decrypted handle values. |
824814

825815
```tsx
826816
import { useQueryClient } from "@tanstack/react-query";
827-
import { confidentialBalanceQueryKeys } from "@zama-fhe/react-sdk";
817+
import { zamaQueryKeys } from "@zama-fhe/react-sdk";
828818

829819
const queryClient = useQueryClient();
830820

831821
// Invalidate all balances
832-
queryClient.invalidateQueries({ queryKey: confidentialBalanceQueryKeys.all });
822+
queryClient.invalidateQueries({ queryKey: zamaQueryKeys.confidentialBalance.all });
833823

834824
// Invalidate a specific token's balance
835825
queryClient.invalidateQueries({
836-
queryKey: confidentialBalanceQueryKeys.token("0xTokenAddress"),
826+
queryKey: zamaQueryKeys.confidentialBalance.token("0xTokenAddress"),
837827
});
838828
```
839829

@@ -984,7 +974,7 @@ To force a refresh:
984974

985975
```tsx
986976
const queryClient = useQueryClient();
987-
queryClient.invalidateQueries({ queryKey: confidentialBalanceQueryKeys.all });
977+
queryClient.invalidateQueries({ queryKey: zamaQueryKeys.confidentialBalance.all });
988978
```
989979

990980
## Re-exports from Core SDK

0 commit comments

Comments
 (0)