@pear-protocol/symmio-client 0.3.2 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -35,16 +35,15 @@ type UseSymmAuthParams = {
35
35
  siweDomain?: string;
36
36
  };
37
37
  /**
38
- * Use case: Manage SYMM auth token lifecycle (read, refresh, clear) for an address and chain.
38
+ * Manages SYMM auth token state. Reads cached / persisted tokens on mount
39
+ * without any side effects — the wallet signature flow is only triggered
40
+ * when the caller explicitly invokes `signIn()`.
39
41
  */
40
42
  declare function useSymmAuth(params?: UseSymmAuthParams): {
41
43
  accessToken: string | null;
42
- authToken: string | null;
43
44
  isAuthenticated: boolean;
44
- isLoading: boolean;
45
- isFetching: boolean;
46
45
  error: Error | null;
47
- refresh: (accountAddress?: Address, options?: {
46
+ signIn: (accountAddress?: Address, options?: {
48
47
  force?: boolean;
49
48
  }) => Promise<string | null>;
50
49
  refreshAuth: (accountAddress?: Address, options?: {
@@ -35,16 +35,15 @@ type UseSymmAuthParams = {
35
35
  siweDomain?: string;
36
36
  };
37
37
  /**
38
- * Use case: Manage SYMM auth token lifecycle (read, refresh, clear) for an address and chain.
38
+ * Manages SYMM auth token state. Reads cached / persisted tokens on mount
39
+ * without any side effects — the wallet signature flow is only triggered
40
+ * when the caller explicitly invokes `signIn()`.
39
41
  */
40
42
  declare function useSymmAuth(params?: UseSymmAuthParams): {
41
43
  accessToken: string | null;
42
- authToken: string | null;
43
44
  isAuthenticated: boolean;
44
- isLoading: boolean;
45
- isFetching: boolean;
46
45
  error: Error | null;
47
- refresh: (accountAddress?: Address, options?: {
46
+ signIn: (accountAddress?: Address, options?: {
48
47
  force?: boolean;
49
48
  }) => Promise<string | null>;
50
49
  refreshAuth: (accountAddress?: Address, options?: {
@@ -677,6 +677,9 @@ function useSymmWs(params = {}) {
677
677
  }, [symmCoreClient, accountAddress, chainId, queryClient, setConnected]);
678
678
  return { isConnected };
679
679
  }
680
+ function trimTrailingSlashes(value) {
681
+ return value.replace(/\/+$/, "");
682
+ }
680
683
  function SymmProvider({
681
684
  chainId = 42161,
682
685
  address,
@@ -688,13 +691,21 @@ function SymmProvider({
688
691
  symmioConfig,
689
692
  children
690
693
  }) {
694
+ const normalizedApiUrl = react.useMemo(
695
+ () => trimTrailingSlashes(symmCoreConfig.apiUrl),
696
+ [symmCoreConfig.apiUrl]
697
+ );
698
+ const normalizedWsUrl = react.useMemo(
699
+ () => symmCoreConfig.wsUrl ? trimTrailingSlashes(symmCoreConfig.wsUrl) : void 0,
700
+ [symmCoreConfig.wsUrl]
701
+ );
691
702
  const symmCoreClient = react.useMemo(() => {
692
703
  return symmCore.createSymmSDK({
693
- apiUrl: symmCoreConfig.apiUrl,
694
- wsUrl: symmCoreConfig.wsUrl,
704
+ apiUrl: normalizedApiUrl,
705
+ wsUrl: normalizedWsUrl,
695
706
  defaultChainId: chainId
696
707
  });
697
- }, [chainId, symmCoreConfig.apiUrl, symmCoreConfig.wsUrl]);
708
+ }, [chainId, normalizedApiUrl, normalizedWsUrl]);
698
709
  const value = react.useMemo(
699
710
  () => ({
700
711
  symmCoreClient,
@@ -895,27 +906,116 @@ async function fetchAccessTokenEntry(walletClient, signerAddress, accountAddress
895
906
  writeStoredToken(accountAddress, chainId, cachedToken);
896
907
  return cachedToken;
897
908
  }
909
+ function authStoreKey(accountAddress, chainId, signerAddress) {
910
+ return `${accountAddress}:${chainId}:${signerAddress ?? ""}`;
911
+ }
912
+ function getEntryFromSnapshot(state, accountAddress, chainId, signerAddress) {
913
+ const signerScoped = state.entries[authStoreKey(accountAddress, chainId, signerAddress)];
914
+ if (signerScoped) {
915
+ return signerScoped;
916
+ }
917
+ return state.entries[authStoreKey(accountAddress, chainId)] ?? null;
918
+ }
919
+ function getStatusFromSnapshot(state, accountAddress, chainId, signerAddress) {
920
+ const signerScoped = state.status[authStoreKey(accountAddress, chainId, signerAddress)];
921
+ if (signerScoped) {
922
+ return signerScoped;
923
+ }
924
+ return state.status[authStoreKey(accountAddress, chainId)] ?? {
925
+ error: null,
926
+ isLoading: false
927
+ };
928
+ }
929
+ var useSymmAuthStore = zustand.create((set) => ({
930
+ entries: {},
931
+ status: {},
932
+ setEntry: (accountAddress, chainId, signerAddress, entry) => set((state) => ({
933
+ entries: {
934
+ ...state.entries,
935
+ [authStoreKey(accountAddress, chainId, signerAddress)]: entry
936
+ },
937
+ status: {
938
+ ...state.status,
939
+ [authStoreKey(accountAddress, chainId, signerAddress)]: {
940
+ error: null,
941
+ isLoading: false
942
+ }
943
+ }
944
+ })),
945
+ clearEntry: (accountAddress, chainId, signerAddress) => set((state) => {
946
+ const nextEntries = { ...state.entries };
947
+ const nextStatus = { ...state.status };
948
+ delete nextEntries[authStoreKey(accountAddress, chainId, signerAddress)];
949
+ delete nextStatus[authStoreKey(accountAddress, chainId, signerAddress)];
950
+ return {
951
+ entries: nextEntries,
952
+ status: nextStatus
953
+ };
954
+ }),
955
+ setStatus: (accountAddress, chainId, signerAddress, status) => set((state) => ({
956
+ status: {
957
+ ...state.status,
958
+ [authStoreKey(accountAddress, chainId, signerAddress)]: {
959
+ error: null,
960
+ isLoading: false,
961
+ ...state.status[authStoreKey(accountAddress, chainId, signerAddress)],
962
+ ...status
963
+ }
964
+ }
965
+ })),
966
+ reset: () => set({
967
+ entries: {},
968
+ status: {}
969
+ })
970
+ }));
971
+ function getAuthStoreEntry(accountAddress, chainId, signerAddress) {
972
+ return getEntryFromSnapshot(
973
+ useSymmAuthStore.getState(),
974
+ accountAddress,
975
+ chainId,
976
+ signerAddress
977
+ );
978
+ }
979
+ function selectAuthStoreEntry(state, accountAddress, chainId, signerAddress) {
980
+ if (!accountAddress || chainId == null) {
981
+ return null;
982
+ }
983
+ return getEntryFromSnapshot(state, accountAddress, chainId, signerAddress);
984
+ }
985
+ function selectAuthStoreStatus(state, accountAddress, chainId, signerAddress) {
986
+ if (!accountAddress || chainId == null) {
987
+ return {
988
+ error: null,
989
+ isLoading: false
990
+ };
991
+ }
992
+ return getStatusFromSnapshot(state, accountAddress, chainId, signerAddress);
993
+ }
994
+ function setAuthStoreEntry(accountAddress, chainId, signerAddress, entry) {
995
+ useSymmAuthStore.getState().setEntry(accountAddress, chainId, signerAddress, entry);
996
+ }
997
+ function setAuthStoreStatus(accountAddress, chainId, signerAddress, status) {
998
+ useSymmAuthStore.getState().setStatus(accountAddress, chainId, signerAddress, status);
999
+ }
1000
+ function clearAuthStoreEntry(accountAddress, chainId, signerAddress) {
1001
+ useSymmAuthStore.getState().clearEntry(accountAddress, chainId, signerAddress);
1002
+ }
898
1003
 
899
1004
  // src/react/auth-cache.ts
900
- function getAuthQueryData(queryClient, accountAddress, chainId, signerAddress) {
901
- return queryClient.getQueryData(
902
- symmKeys.auth(accountAddress, chainId, signerAddress)
903
- ) ?? null;
1005
+ function getAuthQueryData(accountAddress, chainId, signerAddress) {
1006
+ return getAuthStoreEntry(accountAddress, chainId, signerAddress);
904
1007
  }
905
- function setAuthQueryData(queryClient, accountAddress, chainId, signerAddress, entry) {
906
- queryClient.setQueryData(symmKeys.auth(accountAddress, chainId, signerAddress), entry);
1008
+ function setAuthQueryData(accountAddress, chainId, signerAddress, entry) {
1009
+ setAuthStoreEntry(accountAddress, chainId, signerAddress, entry);
907
1010
  }
908
- function clearAuthQueryData(queryClient, accountAddress, chainId, signerAddress) {
909
- queryClient.removeQueries({
910
- queryKey: symmKeys.auth(accountAddress, chainId, signerAddress),
911
- exact: true
912
- });
1011
+ function clearAuthQueryData(accountAddress, chainId, signerAddress) {
1012
+ clearAuthStoreEntry(accountAddress, chainId, signerAddress);
913
1013
  }
914
1014
  function clearPersistedAuthState(accountAddress, chainId) {
915
1015
  clearCachedToken(accountAddress, chainId);
916
1016
  }
917
- function getAuthTokenFromRuntimeCache(queryClient, accountAddress, chainId, signerAddress) {
918
- const inQuery = getAuthQueryData(queryClient, accountAddress, chainId, signerAddress) ?? getAuthQueryData(queryClient, accountAddress, chainId);
1017
+ function getAuthTokenFromRuntimeCache(accountAddress, chainId, signerAddress) {
1018
+ const inQuery = getAuthQueryData(accountAddress, chainId, signerAddress) ?? getAuthQueryData(accountAddress, chainId);
919
1019
  if (inQuery && inQuery.expiresAt > Date.now()) {
920
1020
  return inQuery.token;
921
1021
  }
@@ -923,11 +1023,10 @@ function getAuthTokenFromRuntimeCache(queryClient, accountAddress, chainId, sign
923
1023
  if (!persisted) {
924
1024
  return null;
925
1025
  }
926
- setAuthQueryData(queryClient, accountAddress, chainId, signerAddress, persisted);
1026
+ setAuthQueryData(accountAddress, chainId, signerAddress, persisted);
927
1027
  return persisted.token;
928
1028
  }
929
1029
  async function resolveAuthTokenEntry({
930
- queryClient,
931
1030
  walletClient,
932
1031
  signerAddress,
933
1032
  accountAddress,
@@ -939,13 +1038,13 @@ async function resolveAuthTokenEntry({
939
1038
  return null;
940
1039
  }
941
1040
  if (!force) {
942
- const inQuery = getAuthQueryData(queryClient, accountAddress, chainId, signerAddress);
1041
+ const inQuery = getAuthQueryData(accountAddress, chainId, signerAddress);
943
1042
  if (inQuery && inQuery.expiresAt > Date.now()) {
944
1043
  return inQuery;
945
1044
  }
946
1045
  const persisted = getCachedTokenEntry(accountAddress, chainId);
947
1046
  if (persisted) {
948
- setAuthQueryData(queryClient, accountAddress, chainId, signerAddress, persisted);
1047
+ setAuthQueryData(accountAddress, chainId, signerAddress, persisted);
949
1048
  return persisted;
950
1049
  }
951
1050
  }
@@ -956,25 +1055,33 @@ async function resolveAuthTokenEntry({
956
1055
  chainId,
957
1056
  siweDomain
958
1057
  );
959
- setAuthQueryData(queryClient, accountAddress, chainId, signerAddress, fresh);
1058
+ setAuthQueryData(accountAddress, chainId, signerAddress, fresh);
960
1059
  return fresh;
961
1060
  }
962
- function clearAuthState(queryClient, accountAddress, chainId, signerAddress) {
1061
+ function clearAuthState(accountAddress, chainId, signerAddress) {
963
1062
  clearPersistedAuthState(accountAddress, chainId);
964
- clearAuthQueryData(queryClient, accountAddress, chainId, signerAddress);
1063
+ clearAuthQueryData(accountAddress, chainId, signerAddress);
965
1064
  }
966
1065
 
967
1066
  // src/react/hooks/use-symm-auth.ts
968
1067
  function useSymmAuth(params) {
969
1068
  const context = useSymmContext();
970
- const queryClient = reactQuery.useQueryClient();
971
1069
  const address = params?.address ?? context.address;
972
1070
  const chainId = params?.chainId ?? context.chainId ?? 42161;
973
1071
  const walletClient = params?.walletClient ?? context.walletClient;
974
1072
  const siweDomain = params?.siweDomain;
975
1073
  const activeAccountAddress = params?.activeAccountAddress;
976
- const canBootstrap = !!walletClient && !!address && !!activeAccountAddress;
977
- const refreshAuth = react.useCallback(
1074
+ const authEntry = useSymmAuthStore(
1075
+ (state) => selectAuthStoreEntry(state, activeAccountAddress, chainId, address)
1076
+ );
1077
+ useSymmAuthStore(
1078
+ (state) => selectAuthStoreStatus(state, activeAccountAddress, chainId, address).isLoading
1079
+ );
1080
+ const error = useSymmAuthStore(
1081
+ (state) => selectAuthStoreStatus(state, activeAccountAddress, chainId, address).error
1082
+ );
1083
+ const persistedEntry = !authEntry && activeAccountAddress ? getCachedTokenEntry(activeAccountAddress, chainId) : null;
1084
+ const signIn = react.useCallback(
978
1085
  async (accountAddress, options) => {
979
1086
  const resolvedAccountAddress = accountAddress ?? activeAccountAddress;
980
1087
  if (!resolvedAccountAddress) {
@@ -982,8 +1089,11 @@ function useSymmAuth(params) {
982
1089
  }
983
1090
  if (!walletClient || !address) return null;
984
1091
  try {
985
- const tokenEntry = await resolveAuthTokenEntry({
986
- queryClient,
1092
+ setAuthStoreStatus(resolvedAccountAddress, chainId, address, {
1093
+ error: null,
1094
+ isLoading: true
1095
+ });
1096
+ const tokenEntry2 = await resolveAuthTokenEntry({
987
1097
  walletClient,
988
1098
  signerAddress: address,
989
1099
  accountAddress: resolvedAccountAddress,
@@ -991,10 +1101,16 @@ function useSymmAuth(params) {
991
1101
  siweDomain,
992
1102
  force: options?.force
993
1103
  });
994
- const token2 = tokenEntry?.token ?? null;
995
- return token2;
996
- } catch (error) {
997
- clearPersistedAuthState(resolvedAccountAddress, chainId);
1104
+ setAuthStoreStatus(resolvedAccountAddress, chainId, address, {
1105
+ error: null,
1106
+ isLoading: false
1107
+ });
1108
+ return tokenEntry2?.token ?? null;
1109
+ } catch (error2) {
1110
+ clearAuthState(resolvedAccountAddress, chainId, address);
1111
+ setAuthStoreStatus(resolvedAccountAddress, chainId, address, {
1112
+ error: error2 instanceof Error ? error2 : new Error("failed to sign in")
1113
+ });
998
1114
  return null;
999
1115
  }
1000
1116
  },
@@ -1003,46 +1119,22 @@ function useSymmAuth(params) {
1003
1119
  address,
1004
1120
  activeAccountAddress,
1005
1121
  chainId,
1006
- siweDomain,
1007
- queryClient
1122
+ siweDomain
1008
1123
  ]
1009
1124
  );
1010
- const authQuery = reactQuery.useQuery({
1011
- queryKey: symmKeys.auth(activeAccountAddress, chainId, address),
1012
- queryFn: async () => {
1013
- const tokenEntry = await resolveAuthTokenEntry({
1014
- queryClient,
1015
- walletClient,
1016
- signerAddress: address,
1017
- accountAddress: activeAccountAddress,
1018
- chainId,
1019
- siweDomain
1020
- });
1021
- if (!tokenEntry) {
1022
- return null;
1023
- }
1024
- return tokenEntry;
1025
- },
1026
- enabled: canBootstrap,
1027
- retry: false,
1028
- refetchOnWindowFocus: false,
1029
- refetchOnReconnect: false
1030
- });
1031
1125
  const clearAuth = react.useCallback(() => {
1032
1126
  if (activeAccountAddress) {
1033
- clearAuthState(queryClient, activeAccountAddress, chainId, address);
1127
+ clearAuthState(activeAccountAddress, chainId, address);
1034
1128
  }
1035
- }, [activeAccountAddress, address, chainId, queryClient]);
1036
- const token = authQuery.data?.token ?? null;
1129
+ }, [activeAccountAddress, address, chainId]);
1130
+ const tokenEntry = authEntry ?? persistedEntry;
1131
+ const token = tokenEntry?.token ?? null;
1037
1132
  return {
1038
1133
  accessToken: token,
1039
- authToken: token,
1040
1134
  isAuthenticated: !!token,
1041
- isLoading: authQuery.isLoading,
1042
- isFetching: authQuery.isFetching,
1043
- error: authQuery.error,
1044
- refresh: refreshAuth,
1045
- refreshAuth,
1135
+ error,
1136
+ signIn,
1137
+ refreshAuth: signIn,
1046
1138
  clear: clearAuth
1047
1139
  };
1048
1140
  }
@@ -1846,7 +1938,6 @@ async function ensureInstantTradeReady(deps, queryClient, request) {
1846
1938
  throw new Error("at least one delegation selector is required");
1847
1939
  }
1848
1940
  const accessToken = getAuthTokenFromRuntimeCache(
1849
- queryClient,
1850
1941
  accountAddress,
1851
1942
  chainId,
1852
1943
  signerAddress
@@ -2256,7 +2347,6 @@ function splitTradeHookArgs(paramsOrOptions, options) {
2256
2347
  }
2257
2348
  function useResolveTradeAuthToken(params = {}) {
2258
2349
  const context = useSymmContext();
2259
- const queryClient = reactQuery.useQueryClient();
2260
2350
  const address = params.address ?? context.address;
2261
2351
  const chainId = params.chainId ?? context.chainId;
2262
2352
  return react.useCallback(
@@ -2270,7 +2360,6 @@ function useResolveTradeAuthToken(params = {}) {
2270
2360
  return null;
2271
2361
  }
2272
2362
  const inMemoryToken = getAuthTokenFromRuntimeCache(
2273
- queryClient,
2274
2363
  resolvedAccountAddress,
2275
2364
  resolvedChainId,
2276
2365
  address
@@ -2280,7 +2369,7 @@ function useResolveTradeAuthToken(params = {}) {
2280
2369
  }
2281
2370
  return null;
2282
2371
  },
2283
- [address, chainId, queryClient]
2372
+ [address, chainId]
2284
2373
  );
2285
2374
  }
2286
2375
 
@@ -26242,7 +26331,6 @@ function useSymmPendingInstantOpens(params) {
26242
26331
  chainId: ctxChainId,
26243
26332
  address
26244
26333
  } = useSymmContext();
26245
- const queryClient = reactQuery.useQueryClient();
26246
26334
  const { accountAddress, authToken: providedAuthToken } = params;
26247
26335
  const chainId = params.chainId ?? ctxChainId;
26248
26336
  const internalEnabled = !!symmCoreClient && !!accountAddress;
@@ -26251,7 +26339,6 @@ function useSymmPendingInstantOpens(params) {
26251
26339
  queryKey: symmKeys.pendingInstantOpens(accountAddress, chainId),
26252
26340
  queryFn: async () => {
26253
26341
  const authToken = providedAuthToken ?? getAuthTokenFromRuntimeCache(
26254
- queryClient,
26255
26342
  accountAddress,
26256
26343
  chainId,
26257
26344
  address