@owney/sdk 0.6.9-beta.3 → 0.6.9-beta.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.
package/dist/index.cjs CHANGED
@@ -9,9 +9,9 @@ var __export = (target, all) => {
9
9
  };
10
10
  var __copyProps = (to, from, except, desc) => {
11
11
  if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
12
+ for (let key2 of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key2) && key2 !== except)
14
+ __defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
15
15
  }
16
16
  return to;
17
17
  };
@@ -562,23 +562,23 @@ var readLegacySession = (store, address) => {
562
562
  const legacyKeys = [];
563
563
  try {
564
564
  for (let i = 0; i < store.length; i++) {
565
- const key = store.key(i);
566
- if (key?.startsWith(prefix)) legacyKeys.push(key);
565
+ const key2 = store.key(i);
566
+ if (key2?.startsWith(prefix)) legacyKeys.push(key2);
567
567
  }
568
568
  } catch {
569
569
  return null;
570
570
  }
571
571
  let adopted = null;
572
- for (const key of legacyKeys) {
572
+ for (const key2 of legacyKeys) {
573
573
  if (!adopted) {
574
574
  try {
575
- const parsed = JSON.parse(store.getItem(key) ?? "");
575
+ const parsed = JSON.parse(store.getItem(key2) ?? "");
576
576
  if (isFreshSession(parsed)) adopted = parsed;
577
577
  } catch {
578
578
  }
579
579
  }
580
580
  try {
581
- store.removeItem(key);
581
+ store.removeItem(key2);
582
582
  } catch {
583
583
  }
584
584
  }
@@ -586,11 +586,11 @@ var readLegacySession = (store, address) => {
586
586
  };
587
587
  var readSession = (address, _chainId) => {
588
588
  if (typeof window === "undefined") return null;
589
- const key = buildKey(address);
589
+ const key2 = buildKey(address);
590
590
  const store = storage();
591
591
  let raw = null;
592
592
  try {
593
- raw = store?.getItem(key) ?? null;
593
+ raw = store?.getItem(key2) ?? null;
594
594
  } catch {
595
595
  raw = null;
596
596
  }
@@ -600,9 +600,9 @@ var readSession = (address, _chainId) => {
600
600
  if (isFreshSession(parsed)) return parsed;
601
601
  } catch {
602
602
  }
603
- memorySessions.delete(key);
603
+ memorySessions.delete(key2);
604
604
  try {
605
- store?.removeItem(key);
605
+ store?.removeItem(key2);
606
606
  } catch {
607
607
  }
608
608
  return null;
@@ -612,27 +612,27 @@ var readSession = (address, _chainId) => {
612
612
  writeSession(address, _chainId, legacy);
613
613
  return legacy;
614
614
  }
615
- const cached = memorySessions.get(key);
615
+ const cached = memorySessions.get(key2);
616
616
  if (isFreshSession(cached)) return cached;
617
- if (cached) memorySessions.delete(key);
617
+ if (cached) memorySessions.delete(key2);
618
618
  return null;
619
619
  };
620
620
  var writeSession = (address, _chainId, session) => {
621
621
  if (typeof window === "undefined") return;
622
- const key = buildKey(address);
623
- memorySessions.set(key, session);
622
+ const key2 = buildKey(address);
623
+ memorySessions.set(key2, session);
624
624
  const store = storage();
625
625
  try {
626
- store?.setItem(key, JSON.stringify(session));
626
+ store?.setItem(key2, JSON.stringify(session));
627
627
  } catch {
628
628
  }
629
629
  };
630
630
  var clearSession = (address, _chainId) => {
631
- const key = buildKey(address);
632
- memorySessions.delete(key);
631
+ const key2 = buildKey(address);
632
+ memorySessions.delete(key2);
633
633
  const store = storage();
634
634
  try {
635
- store?.removeItem(key);
635
+ store?.removeItem(key2);
636
636
  } catch {
637
637
  }
638
638
  readLegacySession(store, address);
@@ -696,6 +696,99 @@ function splittingNeedsUpdate(current, desired) {
696
696
  return current.minSplits !== desired.minSplits;
697
697
  }
698
698
 
699
+ // src/agents/zyfai/pool-policy.ts
700
+ function toZyfaiCustomizations(policy, availableByProtocol, chainsByProtocol) {
701
+ const selectionByKey = /* @__PURE__ */ new Map();
702
+ for (const selection of policy.selections) {
703
+ selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
704
+ }
705
+ const customizations = [];
706
+ for (const [protocolId, chainIds] of Object.entries(chainsByProtocol)) {
707
+ const available = availableByProtocol.get(protocolId);
708
+ if (available === void 0) {
709
+ continue;
710
+ }
711
+ const availableSet = new Set(available);
712
+ for (const chainId of chainIds) {
713
+ const stored = selectionByKey.get(key(protocolId, chainId));
714
+ if (stored === void 0) {
715
+ customizations.push({
716
+ protocolId,
717
+ chainId,
718
+ pools: [],
719
+ autoselect: true
720
+ });
721
+ continue;
722
+ }
723
+ const pools = stored.pools.filter((pool) => availableSet.has(pool));
724
+ customizations.push({
725
+ protocolId,
726
+ chainId,
727
+ pools,
728
+ autoselect: false
729
+ });
730
+ }
731
+ }
732
+ return customizations;
733
+ }
734
+ function deriveProtocols(policy, universe, chainsByProtocol) {
735
+ if (policy.selections.length === 0) {
736
+ return null;
737
+ }
738
+ const selectionByKey = /* @__PURE__ */ new Map();
739
+ for (const selection of policy.selections) {
740
+ selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
741
+ }
742
+ return universe.filter((protocolId) => {
743
+ const chainIds = chainsByProtocol[protocolId];
744
+ if (chainIds === void 0 || chainIds.length === 0) {
745
+ return true;
746
+ }
747
+ const fullyDisabled = chainIds.every((chainId) => {
748
+ const stored = selectionByKey.get(key(protocolId, chainId));
749
+ return stored !== void 0 && stored.pools.length === 0;
750
+ });
751
+ return !fullyDisabled;
752
+ });
753
+ }
754
+ function key(protocolId, chainId) {
755
+ return `${protocolId}:${chainId}`;
756
+ }
757
+ function buildChainsByProtocol(protocols, dashboardChainIds) {
758
+ const allowed = new Set(dashboardChainIds);
759
+ const result = {};
760
+ for (const protocol of protocols) {
761
+ result[protocol.id] = (protocol.chains ?? []).filter(
762
+ (chainId) => allowed.has(chainId)
763
+ );
764
+ }
765
+ return result;
766
+ }
767
+ function flattenAvailablePools(raw) {
768
+ const byChain = raw ?? {};
769
+ const names = [];
770
+ for (const byToken of Object.values(byChain ?? {})) {
771
+ for (const entry of Object.values(byToken ?? {})) {
772
+ for (const pool of entry?.pools ?? []) {
773
+ if (pool?.name) names.push(pool.name);
774
+ }
775
+ }
776
+ }
777
+ return names;
778
+ }
779
+ function protocolListsEqual(a, b) {
780
+ const left = [...a ?? []].sort();
781
+ const right = [...b ?? []].sort();
782
+ if (left.length !== right.length) return false;
783
+ return left.every((value, index) => value === right[index]);
784
+ }
785
+ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect) {
786
+ if (!current) return true;
787
+ if (current.autoSelectProtocols !== desiredAutoSelect) return true;
788
+ if (desiredProtocols === null) return false;
789
+ return !protocolListsEqual(current.protocols, desiredProtocols);
790
+ }
791
+
699
792
  // src/lib/debug.ts
700
793
  var configuredDebug = false;
701
794
  function setOwneyDebug(enabled) {
@@ -726,6 +819,7 @@ var WETH_ADDRESS_BY_CHAIN = {
726
819
  };
727
820
  var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum, 1: import_chains.mainnet };
728
821
  var SPLITTING_ASSETS = ["USDC", "WETH"];
822
+ var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
729
823
  function isAlreadyActiveSessionKeyError(error) {
730
824
  const message = error instanceof Error ? error.message : String(error);
731
825
  return /already has an active session key/i.test(message);
@@ -1110,6 +1204,9 @@ var ZyfaiAgent = class {
1110
1204
  await Promise.all(
1111
1205
  SPLITTING_ASSETS.map((asset) => this.applySplitting(asset, policy))
1112
1206
  );
1207
+ if (policy.poolPolicy) {
1208
+ await this.applyPoolPolicy(policy.poolPolicy);
1209
+ }
1113
1210
  }
1114
1211
  async applySplitting(asset, policy) {
1115
1212
  try {
@@ -1146,6 +1243,117 @@ var ZyfaiAgent = class {
1146
1243
  );
1147
1244
  }
1148
1245
  }
1246
+ /**
1247
+ * Apply the org's protocol/pool policy to this user's Zyfai profile.
1248
+ *
1249
+ * Two Zyfai writes, in a strict order:
1250
+ * 1. `updateUserProfile({ protocols, autoSelectProtocols })` per asset —
1251
+ * the explicit allow-list. Chain-agnostic, mirrors `applySplitting`.
1252
+ * 2. `customizeBatch(...)` once — per (protocol, chain), asset-agnostic
1253
+ * pool narrowing.
1254
+ *
1255
+ * Order matters: constraining a protocol's pools (step 2) while that
1256
+ * protocol is not yet in the allow-list (step 1 not yet applied) lands the
1257
+ * constraint on something the rebalance engine isn't considering.
1258
+ *
1259
+ * Wrapped so a failure anywhere is non-fatal — this runs as a side effect of
1260
+ * connecting, so a Zyfai hiccup must leave the user connected with their
1261
+ * profile unchanged, never fail activation.
1262
+ */
1263
+ async applyPoolPolicy(policy) {
1264
+ try {
1265
+ const { protocols: universe } = await this.sdk.getAvailableProtocols(
1266
+ this.supportedChainIds[0]
1267
+ );
1268
+ const universeIds = universe.map((p) => p.id);
1269
+ const chainsByProtocol = buildChainsByProtocol(
1270
+ universe,
1271
+ POOL_POLICY_CHAIN_IDS
1272
+ );
1273
+ debugLog("zyfai:pools", "protocol universe", {
1274
+ universeIds,
1275
+ chainsByProtocol
1276
+ });
1277
+ let strategy;
1278
+ const desiredProtocols = deriveProtocols(
1279
+ policy,
1280
+ universeIds,
1281
+ chainsByProtocol
1282
+ );
1283
+ const desiredAutoSelect = policy.autoApproveProtocols;
1284
+ for (const asset of SPLITTING_ASSETS) {
1285
+ const current = await this.sdk.getUserDetails(asset);
1286
+ if (strategy === void 0 && (current.strategy === "conservative" || current.strategy === "aggressive")) {
1287
+ strategy = current.strategy;
1288
+ }
1289
+ const needsUpdate = protocolsPolicyNeedsUpdate(
1290
+ current,
1291
+ desiredProtocols,
1292
+ desiredAutoSelect
1293
+ );
1294
+ debugLog("zyfai:pools", `${asset}: comparing protocols`, {
1295
+ current: {
1296
+ protocols: current.protocols,
1297
+ autoSelectProtocols: current.autoSelectProtocols
1298
+ },
1299
+ desired: {
1300
+ protocols: desiredProtocols,
1301
+ autoSelectProtocols: desiredAutoSelect
1302
+ },
1303
+ decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
1304
+ });
1305
+ if (!needsUpdate) continue;
1306
+ const update = {
1307
+ asset,
1308
+ autoSelectProtocols: desiredAutoSelect
1309
+ };
1310
+ if (desiredProtocols !== null) update.protocols = desiredProtocols;
1311
+ await this.sdk.updateUserProfile(update);
1312
+ debugLog("zyfai:pools", `${asset}: wrote protocols update`, update);
1313
+ }
1314
+ const poolResults = await Promise.all(
1315
+ universe.map(async (protocol) => {
1316
+ try {
1317
+ const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1318
+ return [protocol.id, flattenAvailablePools(raw)];
1319
+ } catch (error) {
1320
+ console.warn(
1321
+ `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
1322
+ error instanceof Error ? error.message : String(error)
1323
+ );
1324
+ return null;
1325
+ }
1326
+ })
1327
+ );
1328
+ const availableByProtocol = /* @__PURE__ */ new Map();
1329
+ for (const result of poolResults) {
1330
+ if (result) availableByProtocol.set(result[0], result[1]);
1331
+ }
1332
+ const customizations = toZyfaiCustomizations(
1333
+ policy,
1334
+ availableByProtocol,
1335
+ chainsByProtocol
1336
+ );
1337
+ debugLog("zyfai:pools", "derived customizeBatch payload", {
1338
+ strategy,
1339
+ availableProtocolCount: availableByProtocol.size,
1340
+ customizations
1341
+ });
1342
+ if (customizations.length === 0) {
1343
+ debugLog("zyfai:pools", "skipping customizeBatch \u2014 nothing to send");
1344
+ return;
1345
+ }
1346
+ await this.sdk.customizeBatch(customizations);
1347
+ debugLog("zyfai:pools", "wrote customizeBatch", {
1348
+ count: customizations.length
1349
+ });
1350
+ } catch (error) {
1351
+ console.warn(
1352
+ "[zyfai] applyPoolPolicy failed (non-fatal):",
1353
+ error instanceof Error ? error.message : String(error)
1354
+ );
1355
+ }
1356
+ }
1149
1357
  async deposit(state, chainId, amount, asset, depositCallback) {
1150
1358
  try {
1151
1359
  await this.connectFull(state, chainId);
@@ -2181,8 +2389,8 @@ var OwneySDK = class {
2181
2389
  this.initializingAgentsPromise = (async () => {
2182
2390
  const agentKeys = await fetchAgentKeys(this.apiKey);
2183
2391
  this.disabledAgents.clear();
2184
- for (const { key, agent_type, is_enabled } of agentKeys) {
2185
- const agent = this.createAgent(agent_type, key);
2392
+ for (const { key: key2, agent_type, is_enabled } of agentKeys) {
2393
+ const agent = this.createAgent(agent_type, key2);
2186
2394
  if (!agent) continue;
2187
2395
  this.agents.set(agent_type, agent);
2188
2396
  if (is_enabled === false) {
@@ -2203,9 +2411,9 @@ var OwneySDK = class {
2203
2411
  this.initializingAgentsPromise = null;
2204
2412
  }
2205
2413
  }
2206
- createAgent(agentId, key) {
2414
+ createAgent(agentId, key2) {
2207
2415
  if (agentId === "zyfai") {
2208
- return new ZyfaiAgent(key, this.zyfaiRpcUrls, this.referralSource);
2416
+ return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2209
2417
  }
2210
2418
  return null;
2211
2419
  }
@@ -3102,22 +3310,22 @@ var readLegacySiwxSession = (store, address) => {
3102
3310
  const legacyKeys = [];
3103
3311
  try {
3104
3312
  for (let i = 0; i < store.length; i++) {
3105
- const key = store.key(i);
3106
- if (key?.startsWith(prefix)) legacyKeys.push(key);
3313
+ const key2 = store.key(i);
3314
+ if (key2?.startsWith(prefix)) legacyKeys.push(key2);
3107
3315
  }
3108
3316
  } catch {
3109
3317
  return null;
3110
3318
  }
3111
3319
  let adopted = null;
3112
- for (const key of legacyKeys) {
3320
+ for (const key2 of legacyKeys) {
3113
3321
  if (!adopted) {
3114
3322
  try {
3115
- adopted = JSON.parse(store.getItem(key) ?? "");
3323
+ adopted = JSON.parse(store.getItem(key2) ?? "");
3116
3324
  } catch {
3117
3325
  }
3118
3326
  }
3119
3327
  try {
3120
- store.removeItem(key);
3328
+ store.removeItem(key2);
3121
3329
  } catch {
3122
3330
  }
3123
3331
  }
@@ -3125,11 +3333,11 @@ var readLegacySiwxSession = (store, address) => {
3125
3333
  };
3126
3334
  var readSiwxSession = (address, chainId) => {
3127
3335
  if (typeof window === "undefined") return null;
3128
- const key = buildKey2(address);
3336
+ const key2 = buildKey2(address);
3129
3337
  const store = storage2();
3130
3338
  let raw = null;
3131
3339
  try {
3132
- raw = store?.getItem(key) ?? null;
3340
+ raw = store?.getItem(key2) ?? null;
3133
3341
  } catch {
3134
3342
  raw = null;
3135
3343
  }
@@ -3137,9 +3345,9 @@ var readSiwxSession = (address, chainId) => {
3137
3345
  try {
3138
3346
  return JSON.parse(raw);
3139
3347
  } catch {
3140
- memorySiwxSessions.delete(key);
3348
+ memorySiwxSessions.delete(key2);
3141
3349
  try {
3142
- store?.removeItem(key);
3350
+ store?.removeItem(key2);
3143
3351
  } catch {
3144
3352
  }
3145
3353
  return null;
@@ -3150,24 +3358,24 @@ var readSiwxSession = (address, chainId) => {
3150
3358
  writeSiwxSession(address, chainId, legacy);
3151
3359
  return legacy;
3152
3360
  }
3153
- return memorySiwxSessions.get(key) ?? null;
3361
+ return memorySiwxSessions.get(key2) ?? null;
3154
3362
  };
3155
3363
  var writeSiwxSession = (address, _chainId, session) => {
3156
3364
  if (typeof window === "undefined") return;
3157
- const key = buildKey2(address);
3158
- memorySiwxSessions.set(key, session);
3365
+ const key2 = buildKey2(address);
3366
+ memorySiwxSessions.set(key2, session);
3159
3367
  const store = storage2();
3160
3368
  try {
3161
- store?.setItem(key, JSON.stringify(session));
3369
+ store?.setItem(key2, JSON.stringify(session));
3162
3370
  } catch {
3163
3371
  }
3164
3372
  };
3165
3373
  var clearSiwxSession = (address, _chainId) => {
3166
- const key = buildKey2(address);
3167
- memorySiwxSessions.delete(key);
3374
+ const key2 = buildKey2(address);
3375
+ memorySiwxSessions.delete(key2);
3168
3376
  const store = storage2();
3169
3377
  try {
3170
- store?.removeItem(key);
3378
+ store?.removeItem(key2);
3171
3379
  } catch {
3172
3380
  }
3173
3381
  readLegacySiwxSession(store, address);
package/dist/index.d.cts CHANGED
@@ -110,14 +110,37 @@ type DepositOptions = {
110
110
  onApproved?: () => void;
111
111
  };
112
112
 
113
+ /**
114
+ * One protocol's pool selection on one chain. `pools` holds Zyfai pool NAMES
115
+ * verbatim — matched case-sensitively by customizeBatch, and an unrecognised
116
+ * name makes Zyfai's rebalance engine skip the whole protocol, so these strings
117
+ * must not be normalised anywhere.
118
+ *
119
+ * An empty array means "no usable pools on this chain".
120
+ */
121
+ type OrgPoolSelection = {
122
+ protocolId: string;
123
+ chainId: number;
124
+ pools: string[];
125
+ };
126
+ /**
127
+ * The partner's protocol/pool policy (ROUT-224). Sparse — only protocols they
128
+ * narrowed appear. Optional so an older routing API, which does not return the
129
+ * field, still parses; undefined and null both mean "change nothing".
130
+ */
131
+ type OrgPoolPolicy = {
132
+ autoApproveProtocols: boolean;
133
+ autoApprovePools: boolean;
134
+ selections: OrgPoolSelection[];
135
+ };
113
136
  /**
114
137
  * The organization's agent execution policy, or null when the partner has never
115
138
  * configured one — in which case the agents leave the user's profile alone.
116
- * Currently capital splitting only (ROUT-229).
117
139
  */
118
140
  type OrgAgentConfig = {
119
141
  splittingMode: "none" | "automatic" | "force";
120
142
  minSplits: number | null;
143
+ poolPolicy?: OrgPoolPolicy | null;
121
144
  };
122
145
 
123
146
  interface OwneyDepositResult {
package/dist/index.d.ts CHANGED
@@ -110,14 +110,37 @@ type DepositOptions = {
110
110
  onApproved?: () => void;
111
111
  };
112
112
 
113
+ /**
114
+ * One protocol's pool selection on one chain. `pools` holds Zyfai pool NAMES
115
+ * verbatim — matched case-sensitively by customizeBatch, and an unrecognised
116
+ * name makes Zyfai's rebalance engine skip the whole protocol, so these strings
117
+ * must not be normalised anywhere.
118
+ *
119
+ * An empty array means "no usable pools on this chain".
120
+ */
121
+ type OrgPoolSelection = {
122
+ protocolId: string;
123
+ chainId: number;
124
+ pools: string[];
125
+ };
126
+ /**
127
+ * The partner's protocol/pool policy (ROUT-224). Sparse — only protocols they
128
+ * narrowed appear. Optional so an older routing API, which does not return the
129
+ * field, still parses; undefined and null both mean "change nothing".
130
+ */
131
+ type OrgPoolPolicy = {
132
+ autoApproveProtocols: boolean;
133
+ autoApprovePools: boolean;
134
+ selections: OrgPoolSelection[];
135
+ };
113
136
  /**
114
137
  * The organization's agent execution policy, or null when the partner has never
115
138
  * configured one — in which case the agents leave the user's profile alone.
116
- * Currently capital splitting only (ROUT-229).
117
139
  */
118
140
  type OrgAgentConfig = {
119
141
  splittingMode: "none" | "automatic" | "force";
120
142
  minSplits: number | null;
143
+ poolPolicy?: OrgPoolPolicy | null;
121
144
  };
122
145
 
123
146
  interface OwneyDepositResult {
package/dist/index.js CHANGED
@@ -529,23 +529,23 @@ var readLegacySession = (store, address) => {
529
529
  const legacyKeys = [];
530
530
  try {
531
531
  for (let i = 0; i < store.length; i++) {
532
- const key = store.key(i);
533
- if (key?.startsWith(prefix)) legacyKeys.push(key);
532
+ const key2 = store.key(i);
533
+ if (key2?.startsWith(prefix)) legacyKeys.push(key2);
534
534
  }
535
535
  } catch {
536
536
  return null;
537
537
  }
538
538
  let adopted = null;
539
- for (const key of legacyKeys) {
539
+ for (const key2 of legacyKeys) {
540
540
  if (!adopted) {
541
541
  try {
542
- const parsed = JSON.parse(store.getItem(key) ?? "");
542
+ const parsed = JSON.parse(store.getItem(key2) ?? "");
543
543
  if (isFreshSession(parsed)) adopted = parsed;
544
544
  } catch {
545
545
  }
546
546
  }
547
547
  try {
548
- store.removeItem(key);
548
+ store.removeItem(key2);
549
549
  } catch {
550
550
  }
551
551
  }
@@ -553,11 +553,11 @@ var readLegacySession = (store, address) => {
553
553
  };
554
554
  var readSession = (address, _chainId) => {
555
555
  if (typeof window === "undefined") return null;
556
- const key = buildKey(address);
556
+ const key2 = buildKey(address);
557
557
  const store = storage();
558
558
  let raw = null;
559
559
  try {
560
- raw = store?.getItem(key) ?? null;
560
+ raw = store?.getItem(key2) ?? null;
561
561
  } catch {
562
562
  raw = null;
563
563
  }
@@ -567,9 +567,9 @@ var readSession = (address, _chainId) => {
567
567
  if (isFreshSession(parsed)) return parsed;
568
568
  } catch {
569
569
  }
570
- memorySessions.delete(key);
570
+ memorySessions.delete(key2);
571
571
  try {
572
- store?.removeItem(key);
572
+ store?.removeItem(key2);
573
573
  } catch {
574
574
  }
575
575
  return null;
@@ -579,27 +579,27 @@ var readSession = (address, _chainId) => {
579
579
  writeSession(address, _chainId, legacy);
580
580
  return legacy;
581
581
  }
582
- const cached = memorySessions.get(key);
582
+ const cached = memorySessions.get(key2);
583
583
  if (isFreshSession(cached)) return cached;
584
- if (cached) memorySessions.delete(key);
584
+ if (cached) memorySessions.delete(key2);
585
585
  return null;
586
586
  };
587
587
  var writeSession = (address, _chainId, session) => {
588
588
  if (typeof window === "undefined") return;
589
- const key = buildKey(address);
590
- memorySessions.set(key, session);
589
+ const key2 = buildKey(address);
590
+ memorySessions.set(key2, session);
591
591
  const store = storage();
592
592
  try {
593
- store?.setItem(key, JSON.stringify(session));
593
+ store?.setItem(key2, JSON.stringify(session));
594
594
  } catch {
595
595
  }
596
596
  };
597
597
  var clearSession = (address, _chainId) => {
598
- const key = buildKey(address);
599
- memorySessions.delete(key);
598
+ const key2 = buildKey(address);
599
+ memorySessions.delete(key2);
600
600
  const store = storage();
601
601
  try {
602
- store?.removeItem(key);
602
+ store?.removeItem(key2);
603
603
  } catch {
604
604
  }
605
605
  readLegacySession(store, address);
@@ -663,6 +663,99 @@ function splittingNeedsUpdate(current, desired) {
663
663
  return current.minSplits !== desired.minSplits;
664
664
  }
665
665
 
666
+ // src/agents/zyfai/pool-policy.ts
667
+ function toZyfaiCustomizations(policy, availableByProtocol, chainsByProtocol) {
668
+ const selectionByKey = /* @__PURE__ */ new Map();
669
+ for (const selection of policy.selections) {
670
+ selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
671
+ }
672
+ const customizations = [];
673
+ for (const [protocolId, chainIds] of Object.entries(chainsByProtocol)) {
674
+ const available = availableByProtocol.get(protocolId);
675
+ if (available === void 0) {
676
+ continue;
677
+ }
678
+ const availableSet = new Set(available);
679
+ for (const chainId of chainIds) {
680
+ const stored = selectionByKey.get(key(protocolId, chainId));
681
+ if (stored === void 0) {
682
+ customizations.push({
683
+ protocolId,
684
+ chainId,
685
+ pools: [],
686
+ autoselect: true
687
+ });
688
+ continue;
689
+ }
690
+ const pools = stored.pools.filter((pool) => availableSet.has(pool));
691
+ customizations.push({
692
+ protocolId,
693
+ chainId,
694
+ pools,
695
+ autoselect: false
696
+ });
697
+ }
698
+ }
699
+ return customizations;
700
+ }
701
+ function deriveProtocols(policy, universe, chainsByProtocol) {
702
+ if (policy.selections.length === 0) {
703
+ return null;
704
+ }
705
+ const selectionByKey = /* @__PURE__ */ new Map();
706
+ for (const selection of policy.selections) {
707
+ selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
708
+ }
709
+ return universe.filter((protocolId) => {
710
+ const chainIds = chainsByProtocol[protocolId];
711
+ if (chainIds === void 0 || chainIds.length === 0) {
712
+ return true;
713
+ }
714
+ const fullyDisabled = chainIds.every((chainId) => {
715
+ const stored = selectionByKey.get(key(protocolId, chainId));
716
+ return stored !== void 0 && stored.pools.length === 0;
717
+ });
718
+ return !fullyDisabled;
719
+ });
720
+ }
721
+ function key(protocolId, chainId) {
722
+ return `${protocolId}:${chainId}`;
723
+ }
724
+ function buildChainsByProtocol(protocols, dashboardChainIds) {
725
+ const allowed = new Set(dashboardChainIds);
726
+ const result = {};
727
+ for (const protocol of protocols) {
728
+ result[protocol.id] = (protocol.chains ?? []).filter(
729
+ (chainId) => allowed.has(chainId)
730
+ );
731
+ }
732
+ return result;
733
+ }
734
+ function flattenAvailablePools(raw) {
735
+ const byChain = raw ?? {};
736
+ const names = [];
737
+ for (const byToken of Object.values(byChain ?? {})) {
738
+ for (const entry of Object.values(byToken ?? {})) {
739
+ for (const pool of entry?.pools ?? []) {
740
+ if (pool?.name) names.push(pool.name);
741
+ }
742
+ }
743
+ }
744
+ return names;
745
+ }
746
+ function protocolListsEqual(a, b) {
747
+ const left = [...a ?? []].sort();
748
+ const right = [...b ?? []].sort();
749
+ if (left.length !== right.length) return false;
750
+ return left.every((value, index) => value === right[index]);
751
+ }
752
+ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect) {
753
+ if (!current) return true;
754
+ if (current.autoSelectProtocols !== desiredAutoSelect) return true;
755
+ if (desiredProtocols === null) return false;
756
+ return !protocolListsEqual(current.protocols, desiredProtocols);
757
+ }
758
+
666
759
  // src/lib/debug.ts
667
760
  var configuredDebug = false;
668
761
  function setOwneyDebug(enabled) {
@@ -693,6 +786,7 @@ var WETH_ADDRESS_BY_CHAIN = {
693
786
  };
694
787
  var VIEM_CHAIN = { 8453: base, 42161: arbitrum, 1: mainnet };
695
788
  var SPLITTING_ASSETS = ["USDC", "WETH"];
789
+ var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
696
790
  function isAlreadyActiveSessionKeyError(error) {
697
791
  const message = error instanceof Error ? error.message : String(error);
698
792
  return /already has an active session key/i.test(message);
@@ -1077,6 +1171,9 @@ var ZyfaiAgent = class {
1077
1171
  await Promise.all(
1078
1172
  SPLITTING_ASSETS.map((asset) => this.applySplitting(asset, policy))
1079
1173
  );
1174
+ if (policy.poolPolicy) {
1175
+ await this.applyPoolPolicy(policy.poolPolicy);
1176
+ }
1080
1177
  }
1081
1178
  async applySplitting(asset, policy) {
1082
1179
  try {
@@ -1113,6 +1210,117 @@ var ZyfaiAgent = class {
1113
1210
  );
1114
1211
  }
1115
1212
  }
1213
+ /**
1214
+ * Apply the org's protocol/pool policy to this user's Zyfai profile.
1215
+ *
1216
+ * Two Zyfai writes, in a strict order:
1217
+ * 1. `updateUserProfile({ protocols, autoSelectProtocols })` per asset —
1218
+ * the explicit allow-list. Chain-agnostic, mirrors `applySplitting`.
1219
+ * 2. `customizeBatch(...)` once — per (protocol, chain), asset-agnostic
1220
+ * pool narrowing.
1221
+ *
1222
+ * Order matters: constraining a protocol's pools (step 2) while that
1223
+ * protocol is not yet in the allow-list (step 1 not yet applied) lands the
1224
+ * constraint on something the rebalance engine isn't considering.
1225
+ *
1226
+ * Wrapped so a failure anywhere is non-fatal — this runs as a side effect of
1227
+ * connecting, so a Zyfai hiccup must leave the user connected with their
1228
+ * profile unchanged, never fail activation.
1229
+ */
1230
+ async applyPoolPolicy(policy) {
1231
+ try {
1232
+ const { protocols: universe } = await this.sdk.getAvailableProtocols(
1233
+ this.supportedChainIds[0]
1234
+ );
1235
+ const universeIds = universe.map((p) => p.id);
1236
+ const chainsByProtocol = buildChainsByProtocol(
1237
+ universe,
1238
+ POOL_POLICY_CHAIN_IDS
1239
+ );
1240
+ debugLog("zyfai:pools", "protocol universe", {
1241
+ universeIds,
1242
+ chainsByProtocol
1243
+ });
1244
+ let strategy;
1245
+ const desiredProtocols = deriveProtocols(
1246
+ policy,
1247
+ universeIds,
1248
+ chainsByProtocol
1249
+ );
1250
+ const desiredAutoSelect = policy.autoApproveProtocols;
1251
+ for (const asset of SPLITTING_ASSETS) {
1252
+ const current = await this.sdk.getUserDetails(asset);
1253
+ if (strategy === void 0 && (current.strategy === "conservative" || current.strategy === "aggressive")) {
1254
+ strategy = current.strategy;
1255
+ }
1256
+ const needsUpdate = protocolsPolicyNeedsUpdate(
1257
+ current,
1258
+ desiredProtocols,
1259
+ desiredAutoSelect
1260
+ );
1261
+ debugLog("zyfai:pools", `${asset}: comparing protocols`, {
1262
+ current: {
1263
+ protocols: current.protocols,
1264
+ autoSelectProtocols: current.autoSelectProtocols
1265
+ },
1266
+ desired: {
1267
+ protocols: desiredProtocols,
1268
+ autoSelectProtocols: desiredAutoSelect
1269
+ },
1270
+ decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
1271
+ });
1272
+ if (!needsUpdate) continue;
1273
+ const update = {
1274
+ asset,
1275
+ autoSelectProtocols: desiredAutoSelect
1276
+ };
1277
+ if (desiredProtocols !== null) update.protocols = desiredProtocols;
1278
+ await this.sdk.updateUserProfile(update);
1279
+ debugLog("zyfai:pools", `${asset}: wrote protocols update`, update);
1280
+ }
1281
+ const poolResults = await Promise.all(
1282
+ universe.map(async (protocol) => {
1283
+ try {
1284
+ const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1285
+ return [protocol.id, flattenAvailablePools(raw)];
1286
+ } catch (error) {
1287
+ console.warn(
1288
+ `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
1289
+ error instanceof Error ? error.message : String(error)
1290
+ );
1291
+ return null;
1292
+ }
1293
+ })
1294
+ );
1295
+ const availableByProtocol = /* @__PURE__ */ new Map();
1296
+ for (const result of poolResults) {
1297
+ if (result) availableByProtocol.set(result[0], result[1]);
1298
+ }
1299
+ const customizations = toZyfaiCustomizations(
1300
+ policy,
1301
+ availableByProtocol,
1302
+ chainsByProtocol
1303
+ );
1304
+ debugLog("zyfai:pools", "derived customizeBatch payload", {
1305
+ strategy,
1306
+ availableProtocolCount: availableByProtocol.size,
1307
+ customizations
1308
+ });
1309
+ if (customizations.length === 0) {
1310
+ debugLog("zyfai:pools", "skipping customizeBatch \u2014 nothing to send");
1311
+ return;
1312
+ }
1313
+ await this.sdk.customizeBatch(customizations);
1314
+ debugLog("zyfai:pools", "wrote customizeBatch", {
1315
+ count: customizations.length
1316
+ });
1317
+ } catch (error) {
1318
+ console.warn(
1319
+ "[zyfai] applyPoolPolicy failed (non-fatal):",
1320
+ error instanceof Error ? error.message : String(error)
1321
+ );
1322
+ }
1323
+ }
1116
1324
  async deposit(state, chainId, amount, asset, depositCallback) {
1117
1325
  try {
1118
1326
  await this.connectFull(state, chainId);
@@ -2152,8 +2360,8 @@ var OwneySDK = class {
2152
2360
  this.initializingAgentsPromise = (async () => {
2153
2361
  const agentKeys = await fetchAgentKeys(this.apiKey);
2154
2362
  this.disabledAgents.clear();
2155
- for (const { key, agent_type, is_enabled } of agentKeys) {
2156
- const agent = this.createAgent(agent_type, key);
2363
+ for (const { key: key2, agent_type, is_enabled } of agentKeys) {
2364
+ const agent = this.createAgent(agent_type, key2);
2157
2365
  if (!agent) continue;
2158
2366
  this.agents.set(agent_type, agent);
2159
2367
  if (is_enabled === false) {
@@ -2174,9 +2382,9 @@ var OwneySDK = class {
2174
2382
  this.initializingAgentsPromise = null;
2175
2383
  }
2176
2384
  }
2177
- createAgent(agentId, key) {
2385
+ createAgent(agentId, key2) {
2178
2386
  if (agentId === "zyfai") {
2179
- return new ZyfaiAgent(key, this.zyfaiRpcUrls, this.referralSource);
2387
+ return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2180
2388
  }
2181
2389
  return null;
2182
2390
  }
@@ -3073,22 +3281,22 @@ var readLegacySiwxSession = (store, address) => {
3073
3281
  const legacyKeys = [];
3074
3282
  try {
3075
3283
  for (let i = 0; i < store.length; i++) {
3076
- const key = store.key(i);
3077
- if (key?.startsWith(prefix)) legacyKeys.push(key);
3284
+ const key2 = store.key(i);
3285
+ if (key2?.startsWith(prefix)) legacyKeys.push(key2);
3078
3286
  }
3079
3287
  } catch {
3080
3288
  return null;
3081
3289
  }
3082
3290
  let adopted = null;
3083
- for (const key of legacyKeys) {
3291
+ for (const key2 of legacyKeys) {
3084
3292
  if (!adopted) {
3085
3293
  try {
3086
- adopted = JSON.parse(store.getItem(key) ?? "");
3294
+ adopted = JSON.parse(store.getItem(key2) ?? "");
3087
3295
  } catch {
3088
3296
  }
3089
3297
  }
3090
3298
  try {
3091
- store.removeItem(key);
3299
+ store.removeItem(key2);
3092
3300
  } catch {
3093
3301
  }
3094
3302
  }
@@ -3096,11 +3304,11 @@ var readLegacySiwxSession = (store, address) => {
3096
3304
  };
3097
3305
  var readSiwxSession = (address, chainId) => {
3098
3306
  if (typeof window === "undefined") return null;
3099
- const key = buildKey2(address);
3307
+ const key2 = buildKey2(address);
3100
3308
  const store = storage2();
3101
3309
  let raw = null;
3102
3310
  try {
3103
- raw = store?.getItem(key) ?? null;
3311
+ raw = store?.getItem(key2) ?? null;
3104
3312
  } catch {
3105
3313
  raw = null;
3106
3314
  }
@@ -3108,9 +3316,9 @@ var readSiwxSession = (address, chainId) => {
3108
3316
  try {
3109
3317
  return JSON.parse(raw);
3110
3318
  } catch {
3111
- memorySiwxSessions.delete(key);
3319
+ memorySiwxSessions.delete(key2);
3112
3320
  try {
3113
- store?.removeItem(key);
3321
+ store?.removeItem(key2);
3114
3322
  } catch {
3115
3323
  }
3116
3324
  return null;
@@ -3121,24 +3329,24 @@ var readSiwxSession = (address, chainId) => {
3121
3329
  writeSiwxSession(address, chainId, legacy);
3122
3330
  return legacy;
3123
3331
  }
3124
- return memorySiwxSessions.get(key) ?? null;
3332
+ return memorySiwxSessions.get(key2) ?? null;
3125
3333
  };
3126
3334
  var writeSiwxSession = (address, _chainId, session) => {
3127
3335
  if (typeof window === "undefined") return;
3128
- const key = buildKey2(address);
3129
- memorySiwxSessions.set(key, session);
3336
+ const key2 = buildKey2(address);
3337
+ memorySiwxSessions.set(key2, session);
3130
3338
  const store = storage2();
3131
3339
  try {
3132
- store?.setItem(key, JSON.stringify(session));
3340
+ store?.setItem(key2, JSON.stringify(session));
3133
3341
  } catch {
3134
3342
  }
3135
3343
  };
3136
3344
  var clearSiwxSession = (address, _chainId) => {
3137
- const key = buildKey2(address);
3138
- memorySiwxSessions.delete(key);
3345
+ const key2 = buildKey2(address);
3346
+ memorySiwxSessions.delete(key2);
3139
3347
  const store = storage2();
3140
3348
  try {
3141
- store?.removeItem(key);
3349
+ store?.removeItem(key2);
3142
3350
  } catch {
3143
3351
  }
3144
3352
  readLegacySiwxSession(store, address);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.6.9-beta.3",
3
+ "version": "0.6.9-beta.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",