@owney/sdk 0.6.6-beta.5 → 0.6.6-beta.7

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.
package/dist/index.cjs CHANGED
@@ -599,33 +599,17 @@ var clearSession = (address, chainId) => {
599
599
  } catch {
600
600
  }
601
601
  };
602
- var SESSION_KEY_PREFIX = "owney.zyfai.sessionkey";
603
- var buildSessionKeyKey = (address, chainId) => `${SESSION_KEY_PREFIX}:${address.toLowerCase()}:${chainId}`;
604
- var wasSessionKeyActivated = (address, chainId) => {
605
- const store = storage();
606
- if (!store) return false;
607
- try {
608
- return store.getItem(buildSessionKeyKey(address, chainId)) === "1";
609
- } catch {
610
- return false;
611
- }
612
- };
613
- var markSessionKeyActivated = (address, chainId) => {
614
- const store = storage();
615
- if (!store) return;
616
- try {
617
- store.setItem(buildSessionKeyKey(address, chainId), "1");
618
- } catch {
619
- }
620
- };
621
602
 
622
603
  // src/agents/zyfai/autoSelect.ts
623
- function buildAutoSelectUpdate(current, asset) {
624
- if (current.autoSelectProtocols === true) return null;
604
+ function buildAutoSelectUpdate(current, asset, chainId) {
605
+ const chains = current.chains ?? [];
606
+ const chainMissing = chainId !== void 0 && !chains.includes(chainId);
607
+ if (current.autoSelectProtocols === true && !chainMissing) return null;
608
+ const nextChains = chainMissing ? [...chains, chainId] : chains;
625
609
  return {
626
610
  asset,
627
611
  autoSelectProtocols: true,
628
- ...current.chains ? { chains: current.chains } : {}
612
+ ...nextChains.length ? { chains: nextChains } : {}
629
613
  };
630
614
  }
631
615
 
@@ -641,6 +625,10 @@ var WETH_ADDRESS_BY_CHAIN = {
641
625
  1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
642
626
  };
643
627
  var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum, 1: import_chains.mainnet };
628
+ function isAlreadyActiveSessionKeyError(error) {
629
+ const message = error instanceof Error ? error.message : String(error);
630
+ return /already has an active session key/i.test(message);
631
+ }
644
632
  var ZyfaiAgent = class {
645
633
  id = "zyfai";
646
634
  supportedChainIds = [8453, 42161, 1];
@@ -889,33 +877,45 @@ var ZyfaiAgent = class {
889
877
  );
890
878
  }
891
879
  }
892
- await this.ensureSessionKey(address, validChainId);
880
+ await this.ensureSessionKey(state, address, validChainId);
893
881
  }
894
882
  /**
895
- * Ensures a session key is active for THIS chain.
883
+ * Ensures the user has an active session key, creating one if absent.
896
884
  *
897
- * The Zyfai SDK tracks session-key state with a single global boolean — the
898
- * in-memory `hasActiveSessionKey` and the `/users/me` field of the same name
899
- * are neither chain-scoped. Once a key is activated on one chain, both
900
- * `getUserDetails()` and the SDK's own `createSessionKey` guard report
901
- * "active" on every other chain, so a deposit on a freshly-used chain runs
902
- * without a valid session key and reverts. See [[zyfai-per-chain-session-key]].
885
+ * The Zyfai session key is a single **per-user** record, not per-chain: its
886
+ * config (`/session-keys/config`) spans every supported chain and is signed
887
+ * in one shot, and the backend rejects `POST /session-keys/add` with 400
888
+ * "User already has an active session key" if one exists. So we must NOT try
889
+ * to (re)create a key per chain — that 400s and blocks the deposit. We only
890
+ * create when the user has none.
903
891
  *
904
- * We keep our own per-(address, chainId) record of activated chains (in
905
- * localStorage). On a miss we clear the SDK's global guard so
906
- * `createSessionKey` actually signs and activates for THIS chain.
892
+ * The one wrapper-side hardening that IS needed: a chain/account switch can
893
+ * fire the provider's `accountsChanged` → `disconnectAccount`, tearing down
894
+ * the zyfai connection (walletClient + authenticatedUserId both null) while
895
+ * our own `connectAuth` guard still believes this chain is connected and
896
+ * early-returns without reconnecting. `createSessionKey` then fails with
897
+ * "No account connected". Detect that and reconnect first.
907
898
  *
908
- * Cost: on a chain we've never recorded (e.g. a key created on another device
909
- * or a prior app version), this forces one `createSessionKey` — an extra
910
- * wallet signature, idempotent on the backend. A prompt-free variant would
911
- * read the session-validator module state on-chain instead of self-tracking.
899
+ * NOTE: whether a globally-active session key actually authorizes activity on
900
+ * a freshly-deployed Safe on a new chain is a backend/zyf question — the
901
+ * wrapper cannot force per-chain activation given the global 400 guard.
902
+ * See [[zyfai-per-chain-session-key]].
912
903
  */
913
- async ensureSessionKey(address, chainId) {
914
- if (wasSessionKeyActivated(address, chainId)) return;
904
+ async ensureSessionKey(state, address, chainId) {
915
905
  const internals = this.sdk;
916
- internals.hasActiveSessionKey = false;
917
- await this.sdk.createSessionKey(address, chainId);
918
- markSessionKeyActivated(address, chainId);
906
+ if (!internals.walletClient || internals.authenticatedUserId === null) {
907
+ this.connectedChainId = null;
908
+ await this.connectAuth(state, chainId);
909
+ }
910
+ const user = await this.sdk.getUserDetails();
911
+ if (!user.hasActiveSessionKey) {
912
+ try {
913
+ await this.sdk.createSessionKey(address, chainId);
914
+ } catch (error) {
915
+ if (!isAlreadyActiveSessionKeyError(error)) throw error;
916
+ internals.hasActiveSessionKey = true;
917
+ }
918
+ }
919
919
  }
920
920
  /** Connect + resolve smart wallet address for read operations. */
921
921
  async resolveSmartWallet(state, chainId) {
@@ -1065,7 +1065,7 @@ var ZyfaiAgent = class {
1065
1065
  await this.connectAuth(state, chainId);
1066
1066
  const validChainId = isValidChainId(chainId);
1067
1067
  const raw = await this.sdk.getUserDetails();
1068
- await this.ensureSessionKey(this.getAddress(), validChainId);
1068
+ await this.ensureSessionKey(state, this.getAddress(), validChainId);
1069
1069
  return mapUserProfile(raw, this.connectedAddress);
1070
1070
  }
1071
1071
  async ensureAutoSelectProtocols(state, chainId, asset) {
@@ -1076,7 +1076,8 @@ var ZyfaiAgent = class {
1076
1076
  autoSelectProtocols: current.autoSelectProtocols,
1077
1077
  chains: current.chains
1078
1078
  },
1079
- asset
1079
+ asset,
1080
+ isValidChainId(chainId)
1080
1081
  );
1081
1082
  if (!update) return false;
1082
1083
  await this.sdk.updateUserProfile(update);
package/dist/index.js CHANGED
@@ -567,33 +567,17 @@ var clearSession = (address, chainId) => {
567
567
  } catch {
568
568
  }
569
569
  };
570
- var SESSION_KEY_PREFIX = "owney.zyfai.sessionkey";
571
- var buildSessionKeyKey = (address, chainId) => `${SESSION_KEY_PREFIX}:${address.toLowerCase()}:${chainId}`;
572
- var wasSessionKeyActivated = (address, chainId) => {
573
- const store = storage();
574
- if (!store) return false;
575
- try {
576
- return store.getItem(buildSessionKeyKey(address, chainId)) === "1";
577
- } catch {
578
- return false;
579
- }
580
- };
581
- var markSessionKeyActivated = (address, chainId) => {
582
- const store = storage();
583
- if (!store) return;
584
- try {
585
- store.setItem(buildSessionKeyKey(address, chainId), "1");
586
- } catch {
587
- }
588
- };
589
570
 
590
571
  // src/agents/zyfai/autoSelect.ts
591
- function buildAutoSelectUpdate(current, asset) {
592
- if (current.autoSelectProtocols === true) return null;
572
+ function buildAutoSelectUpdate(current, asset, chainId) {
573
+ const chains = current.chains ?? [];
574
+ const chainMissing = chainId !== void 0 && !chains.includes(chainId);
575
+ if (current.autoSelectProtocols === true && !chainMissing) return null;
576
+ const nextChains = chainMissing ? [...chains, chainId] : chains;
593
577
  return {
594
578
  asset,
595
579
  autoSelectProtocols: true,
596
- ...current.chains ? { chains: current.chains } : {}
580
+ ...nextChains.length ? { chains: nextChains } : {}
597
581
  };
598
582
  }
599
583
 
@@ -609,6 +593,10 @@ var WETH_ADDRESS_BY_CHAIN = {
609
593
  1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
610
594
  };
611
595
  var VIEM_CHAIN = { 8453: base, 42161: arbitrum, 1: mainnet };
596
+ function isAlreadyActiveSessionKeyError(error) {
597
+ const message = error instanceof Error ? error.message : String(error);
598
+ return /already has an active session key/i.test(message);
599
+ }
612
600
  var ZyfaiAgent = class {
613
601
  id = "zyfai";
614
602
  supportedChainIds = [8453, 42161, 1];
@@ -857,33 +845,45 @@ var ZyfaiAgent = class {
857
845
  );
858
846
  }
859
847
  }
860
- await this.ensureSessionKey(address, validChainId);
848
+ await this.ensureSessionKey(state, address, validChainId);
861
849
  }
862
850
  /**
863
- * Ensures a session key is active for THIS chain.
851
+ * Ensures the user has an active session key, creating one if absent.
864
852
  *
865
- * The Zyfai SDK tracks session-key state with a single global boolean — the
866
- * in-memory `hasActiveSessionKey` and the `/users/me` field of the same name
867
- * are neither chain-scoped. Once a key is activated on one chain, both
868
- * `getUserDetails()` and the SDK's own `createSessionKey` guard report
869
- * "active" on every other chain, so a deposit on a freshly-used chain runs
870
- * without a valid session key and reverts. See [[zyfai-per-chain-session-key]].
853
+ * The Zyfai session key is a single **per-user** record, not per-chain: its
854
+ * config (`/session-keys/config`) spans every supported chain and is signed
855
+ * in one shot, and the backend rejects `POST /session-keys/add` with 400
856
+ * "User already has an active session key" if one exists. So we must NOT try
857
+ * to (re)create a key per chain — that 400s and blocks the deposit. We only
858
+ * create when the user has none.
871
859
  *
872
- * We keep our own per-(address, chainId) record of activated chains (in
873
- * localStorage). On a miss we clear the SDK's global guard so
874
- * `createSessionKey` actually signs and activates for THIS chain.
860
+ * The one wrapper-side hardening that IS needed: a chain/account switch can
861
+ * fire the provider's `accountsChanged` → `disconnectAccount`, tearing down
862
+ * the zyfai connection (walletClient + authenticatedUserId both null) while
863
+ * our own `connectAuth` guard still believes this chain is connected and
864
+ * early-returns without reconnecting. `createSessionKey` then fails with
865
+ * "No account connected". Detect that and reconnect first.
875
866
  *
876
- * Cost: on a chain we've never recorded (e.g. a key created on another device
877
- * or a prior app version), this forces one `createSessionKey` — an extra
878
- * wallet signature, idempotent on the backend. A prompt-free variant would
879
- * read the session-validator module state on-chain instead of self-tracking.
867
+ * NOTE: whether a globally-active session key actually authorizes activity on
868
+ * a freshly-deployed Safe on a new chain is a backend/zyf question — the
869
+ * wrapper cannot force per-chain activation given the global 400 guard.
870
+ * See [[zyfai-per-chain-session-key]].
880
871
  */
881
- async ensureSessionKey(address, chainId) {
882
- if (wasSessionKeyActivated(address, chainId)) return;
872
+ async ensureSessionKey(state, address, chainId) {
883
873
  const internals = this.sdk;
884
- internals.hasActiveSessionKey = false;
885
- await this.sdk.createSessionKey(address, chainId);
886
- markSessionKeyActivated(address, chainId);
874
+ if (!internals.walletClient || internals.authenticatedUserId === null) {
875
+ this.connectedChainId = null;
876
+ await this.connectAuth(state, chainId);
877
+ }
878
+ const user = await this.sdk.getUserDetails();
879
+ if (!user.hasActiveSessionKey) {
880
+ try {
881
+ await this.sdk.createSessionKey(address, chainId);
882
+ } catch (error) {
883
+ if (!isAlreadyActiveSessionKeyError(error)) throw error;
884
+ internals.hasActiveSessionKey = true;
885
+ }
886
+ }
887
887
  }
888
888
  /** Connect + resolve smart wallet address for read operations. */
889
889
  async resolveSmartWallet(state, chainId) {
@@ -1033,7 +1033,7 @@ var ZyfaiAgent = class {
1033
1033
  await this.connectAuth(state, chainId);
1034
1034
  const validChainId = isValidChainId(chainId);
1035
1035
  const raw = await this.sdk.getUserDetails();
1036
- await this.ensureSessionKey(this.getAddress(), validChainId);
1036
+ await this.ensureSessionKey(state, this.getAddress(), validChainId);
1037
1037
  return mapUserProfile(raw, this.connectedAddress);
1038
1038
  }
1039
1039
  async ensureAutoSelectProtocols(state, chainId, asset) {
@@ -1044,7 +1044,8 @@ var ZyfaiAgent = class {
1044
1044
  autoSelectProtocols: current.autoSelectProtocols,
1045
1045
  chains: current.chains
1046
1046
  },
1047
- asset
1047
+ asset,
1048
+ isValidChainId(chainId)
1048
1049
  );
1049
1050
  if (!update) return false;
1050
1051
  await this.sdk.updateUserProfile(update);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.6.6-beta.5",
3
+ "version": "0.6.6-beta.7",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",