@owney/sdk 0.7.0 → 0.7.2

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
  };
@@ -26,7 +26,8 @@ __export(index_exports, {
26
26
  NotConnectedError: () => NotConnectedError,
27
27
  OwneyError: () => OwneyError,
28
28
  OwneySDK: () => OwneySDK,
29
- createOwneySIWX: () => createOwneySIWX
29
+ createOwneySIWX: () => createOwneySIWX,
30
+ setOwneyDebug: () => setOwneyDebug
30
31
  });
31
32
  module.exports = __toCommonJS(index_exports);
32
33
 
@@ -157,6 +158,14 @@ function hexToDecimal(hex, decimals = 6) {
157
158
  const remainder = parsed % divisor;
158
159
  return Number(whole) + Number(remainder) / Number(divisor);
159
160
  }
161
+ function normalizeDecimalPlace(value, decimals = 6) {
162
+ const parsed = BigInt(value);
163
+ if (decimals <= 0) return Number(parsed);
164
+ const divisor = BigInt(10 ** decimals);
165
+ const whole = parsed / divisor;
166
+ const remainder = parsed % divisor;
167
+ return Number(whole) + Number(remainder) / Number(divisor);
168
+ }
160
169
  var DayFilterMapping = {
161
170
  "30D": 30,
162
171
  "14D": 14,
@@ -215,10 +224,10 @@ function mapBalances(raw, _chainId, smartWallet) {
215
224
  const tokens = [];
216
225
  for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
217
226
  const positionChainId = Number(chainIdKey);
218
- for (const [symbol, { balance, decimals }] of Object.entries(
227
+ for (const [symbol, { balance, balanceWithFee, decimals }] of Object.entries(
219
228
  tokensBySymbol ?? {}
220
229
  )) {
221
- const amount = hexToDecimal(balance || "0", decimals);
230
+ const amount = hexToDecimal(balanceWithFee ?? balance ?? "0", decimals);
222
231
  totalBalance += amount;
223
232
  tokens.push({
224
233
  chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
@@ -234,15 +243,23 @@ function mapBalances(raw, _chainId, smartWallet) {
234
243
  protocolId: p.protocol_id,
235
244
  pool: p.pool,
236
245
  asset: p.token_symbol ?? "",
237
- // Position amount is hex-encoded base units (like the token balances
238
- // above). `PositionSlot` doesn't type a `decimals` field, but runtime
246
+ // Fee-adjusted underlying amounts are decimal base-unit strings. The
247
+ // legacy `amount` fallback is hex. Runtime
239
248
  // getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
240
- // when present, otherwise fall back to the per-symbol lookup so
241
- // non-6-decimal assets like WETH don't render 1e12 too large.
249
+ // when present, otherwise fall back to the per-symbol lookup.
242
250
  amount: String(
243
- hexToDecimal(
251
+ p.underlyingAmountWithFee ?? p.underlyingAmount ? normalizeDecimalPlace(
252
+ p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0",
253
+ tokenDecimals(
254
+ p.token_symbol,
255
+ p.decimals
256
+ )
257
+ ) : hexToDecimal(
244
258
  p.amount || "0",
245
- tokenDecimals(p.token_symbol, p.decimals)
259
+ tokenDecimals(
260
+ p.token_symbol,
261
+ p.decimals
262
+ )
246
263
  )
247
264
  ),
248
265
  apy: p.pool_apy,
@@ -266,7 +283,7 @@ function sumTokenEarnings(tokens) {
266
283
  return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
267
284
  }
268
285
  function mapEarnings(raw, smartWallet) {
269
- const totalEarningsByChain = raw.data.totalEarningsByChain;
286
+ const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
270
287
  const tokens = [];
271
288
  for (const [chainIdKey, tokensBySymbol] of Object.entries(
272
289
  totalEarningsByChain
@@ -284,7 +301,9 @@ function mapEarnings(raw, smartWallet) {
284
301
  }
285
302
  return {
286
303
  smartWallet,
287
- lifetimeEarnings: sumTokenEarnings(raw.data.totalEarningsByToken),
304
+ lifetimeEarnings: sumTokenEarnings(
305
+ raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
306
+ ),
288
307
  tokens
289
308
  };
290
309
  }
@@ -412,7 +431,9 @@ function mapUserProfile(raw, userAddress) {
412
431
  chains: raw.chains || [],
413
432
  strategy: raw.strategy,
414
433
  hasActiveSessionKey: raw.hasActiveSessionKey || false,
415
- protocols: raw.protocols || []
434
+ protocols: raw.protocols || [],
435
+ splitting: raw.splitting,
436
+ minSplits: raw.minSplits
416
437
  };
417
438
  }
418
439
  function mapApyByStrategy(raw) {
@@ -559,23 +580,23 @@ var readLegacySession = (store, address) => {
559
580
  const legacyKeys = [];
560
581
  try {
561
582
  for (let i = 0; i < store.length; i++) {
562
- const key = store.key(i);
563
- if (key?.startsWith(prefix)) legacyKeys.push(key);
583
+ const key2 = store.key(i);
584
+ if (key2?.startsWith(prefix)) legacyKeys.push(key2);
564
585
  }
565
586
  } catch {
566
587
  return null;
567
588
  }
568
589
  let adopted = null;
569
- for (const key of legacyKeys) {
590
+ for (const key2 of legacyKeys) {
570
591
  if (!adopted) {
571
592
  try {
572
- const parsed = JSON.parse(store.getItem(key) ?? "");
593
+ const parsed = JSON.parse(store.getItem(key2) ?? "");
573
594
  if (isFreshSession(parsed)) adopted = parsed;
574
595
  } catch {
575
596
  }
576
597
  }
577
598
  try {
578
- store.removeItem(key);
599
+ store.removeItem(key2);
579
600
  } catch {
580
601
  }
581
602
  }
@@ -583,11 +604,11 @@ var readLegacySession = (store, address) => {
583
604
  };
584
605
  var readSession = (address, _chainId) => {
585
606
  if (typeof window === "undefined") return null;
586
- const key = buildKey(address);
607
+ const key2 = buildKey(address);
587
608
  const store = storage();
588
609
  let raw = null;
589
610
  try {
590
- raw = store?.getItem(key) ?? null;
611
+ raw = store?.getItem(key2) ?? null;
591
612
  } catch {
592
613
  raw = null;
593
614
  }
@@ -597,9 +618,9 @@ var readSession = (address, _chainId) => {
597
618
  if (isFreshSession(parsed)) return parsed;
598
619
  } catch {
599
620
  }
600
- memorySessions.delete(key);
621
+ memorySessions.delete(key2);
601
622
  try {
602
- store?.removeItem(key);
623
+ store?.removeItem(key2);
603
624
  } catch {
604
625
  }
605
626
  return null;
@@ -609,27 +630,27 @@ var readSession = (address, _chainId) => {
609
630
  writeSession(address, _chainId, legacy);
610
631
  return legacy;
611
632
  }
612
- const cached = memorySessions.get(key);
633
+ const cached = memorySessions.get(key2);
613
634
  if (isFreshSession(cached)) return cached;
614
- if (cached) memorySessions.delete(key);
635
+ if (cached) memorySessions.delete(key2);
615
636
  return null;
616
637
  };
617
638
  var writeSession = (address, _chainId, session) => {
618
639
  if (typeof window === "undefined") return;
619
- const key = buildKey(address);
620
- memorySessions.set(key, session);
640
+ const key2 = buildKey(address);
641
+ memorySessions.set(key2, session);
621
642
  const store = storage();
622
643
  try {
623
- store?.setItem(key, JSON.stringify(session));
644
+ store?.setItem(key2, JSON.stringify(session));
624
645
  } catch {
625
646
  }
626
647
  };
627
648
  var clearSession = (address, _chainId) => {
628
- const key = buildKey(address);
629
- memorySessions.delete(key);
649
+ const key2 = buildKey(address);
650
+ memorySessions.delete(key2);
630
651
  const store = storage();
631
652
  try {
632
- store?.removeItem(key);
653
+ store?.removeItem(key2);
633
654
  } catch {
634
655
  }
635
656
  readLegacySession(store, address);
@@ -660,6 +681,149 @@ function buildProtocolMergeUpdate(available, current, asset) {
660
681
  };
661
682
  }
662
683
 
684
+ // src/agents/zyfai/splitting.ts
685
+ var MAX_MIN_SPLITS = 4;
686
+ var MIN_FORCE_SPLITS = 2;
687
+ var AUTOMATIC_MIN_SPLITS = 1;
688
+ function toZyfaiSplitting(policy) {
689
+ switch (policy.splittingMode) {
690
+ case "none":
691
+ return { splitting: false };
692
+ case "automatic":
693
+ return { splitting: true, minSplits: AUTOMATIC_MIN_SPLITS };
694
+ case "force":
695
+ return {
696
+ splitting: true,
697
+ minSplits: clampForceSplits(policy.minSplits)
698
+ };
699
+ }
700
+ }
701
+ function clampForceSplits(minSplits) {
702
+ if (typeof minSplits !== "number" || !Number.isFinite(minSplits)) {
703
+ return MIN_FORCE_SPLITS;
704
+ }
705
+ const rounded = Math.round(minSplits);
706
+ if (rounded < MIN_FORCE_SPLITS) return MIN_FORCE_SPLITS;
707
+ if (rounded > MAX_MIN_SPLITS) return MAX_MIN_SPLITS;
708
+ return rounded;
709
+ }
710
+ function splittingNeedsUpdate(current, desired) {
711
+ if (!current) return true;
712
+ if (current.splitting !== desired.splitting) return true;
713
+ if (desired.minSplits === void 0) return false;
714
+ return current.minSplits !== desired.minSplits;
715
+ }
716
+
717
+ // src/agents/zyfai/pool-policy.ts
718
+ function toZyfaiCustomizations(policy, availableByProtocol, chainsByProtocol) {
719
+ const selectionByKey = /* @__PURE__ */ new Map();
720
+ for (const selection of policy.selections) {
721
+ selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
722
+ }
723
+ const customizations = [];
724
+ for (const [protocolId, chainIds] of Object.entries(chainsByProtocol)) {
725
+ const available = availableByProtocol.get(protocolId);
726
+ if (available === void 0) {
727
+ continue;
728
+ }
729
+ const availableSet = new Set(available);
730
+ for (const chainId of chainIds) {
731
+ const stored = selectionByKey.get(key(protocolId, chainId));
732
+ if (stored === void 0) {
733
+ customizations.push({
734
+ protocolId,
735
+ chainId,
736
+ pools: [],
737
+ autoselect: true
738
+ });
739
+ continue;
740
+ }
741
+ const pools = stored.pools.filter((pool) => availableSet.has(pool));
742
+ customizations.push({
743
+ protocolId,
744
+ chainId,
745
+ pools,
746
+ autoselect: false
747
+ });
748
+ }
749
+ }
750
+ return customizations;
751
+ }
752
+ function deriveProtocols(policy, universe, chainsByProtocol) {
753
+ if (policy.selections.length === 0) {
754
+ return null;
755
+ }
756
+ const selectionByKey = /* @__PURE__ */ new Map();
757
+ for (const selection of policy.selections) {
758
+ selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
759
+ }
760
+ return universe.filter((protocolId) => {
761
+ const chainIds = chainsByProtocol[protocolId];
762
+ if (chainIds === void 0 || chainIds.length === 0) {
763
+ return true;
764
+ }
765
+ const fullyDisabled = chainIds.every((chainId) => {
766
+ const stored = selectionByKey.get(key(protocolId, chainId));
767
+ return stored !== void 0 && stored.pools.length === 0;
768
+ });
769
+ return !fullyDisabled;
770
+ });
771
+ }
772
+ function key(protocolId, chainId) {
773
+ return `${protocolId}:${chainId}`;
774
+ }
775
+ function buildChainsByProtocol(protocols, dashboardChainIds) {
776
+ const allowed = new Set(dashboardChainIds);
777
+ const result = {};
778
+ for (const protocol of protocols) {
779
+ result[protocol.id] = (protocol.chains ?? []).filter(
780
+ (chainId) => allowed.has(chainId)
781
+ );
782
+ }
783
+ return result;
784
+ }
785
+ function flattenAvailablePools(raw) {
786
+ const byChain = raw ?? {};
787
+ const names = [];
788
+ for (const byToken of Object.values(byChain ?? {})) {
789
+ for (const entry of Object.values(byToken ?? {})) {
790
+ for (const pool of entry?.pools ?? []) {
791
+ if (pool?.name) names.push(pool.name);
792
+ }
793
+ }
794
+ }
795
+ return names;
796
+ }
797
+ function protocolListsEqual(a, b) {
798
+ const left = [...a ?? []].sort();
799
+ const right = [...b ?? []].sort();
800
+ if (left.length !== right.length) return false;
801
+ return left.every((value, index) => value === right[index]);
802
+ }
803
+ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect) {
804
+ if (!current) return true;
805
+ if (current.autoSelectProtocols !== desiredAutoSelect) return true;
806
+ if (desiredProtocols === null) return false;
807
+ return !protocolListsEqual(current.protocols, desiredProtocols);
808
+ }
809
+
810
+ // src/lib/debug.ts
811
+ var configuredDebug = false;
812
+ function setOwneyDebug(enabled) {
813
+ configuredDebug = enabled;
814
+ }
815
+ function isOwneyDebug() {
816
+ return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
817
+ }
818
+ function debugLog(scope, message, data) {
819
+ if (!isOwneyDebug()) return;
820
+ if (data === void 0) {
821
+ console.log(`[${scope}] ${message}`);
822
+ } else {
823
+ console.log(`[${scope}] ${message}`, data);
824
+ }
825
+ }
826
+
663
827
  // src/agents/zyfai/zyfai.agent.ts
664
828
  var DEFAULT_ZYFAI_RPC_URLS = {
665
829
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
@@ -672,6 +836,8 @@ var WETH_ADDRESS_BY_CHAIN = {
672
836
  1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
673
837
  };
674
838
  var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum, 1: import_chains.mainnet };
839
+ var SPLITTING_ASSETS = ["USDC", "WETH"];
840
+ var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
675
841
  var ZyfaiAgent = class {
676
842
  id = "zyfai";
677
843
  supportedChainIds = [8453, 42161, 1];
@@ -982,6 +1148,176 @@ var ZyfaiAgent = class {
982
1148
  throw error;
983
1149
  }
984
1150
  }
1151
+ /**
1152
+ * Apply the org's capital splitting policy to this user's Zyfai profile.
1153
+ *
1154
+ * Per ASSET, not per chain: Zyfai stores splitting under
1155
+ * `assetTypeSettings[asset]`, so one call would only configure USDC and leave a
1156
+ * WETH holder's real setting untouched.
1157
+ *
1158
+ * Non-fatal throughout. This is a side effect of connecting, so a Zyfai hiccup
1159
+ * must leave the user connected with their profile unchanged, never fail
1160
+ * activation.
1161
+ */
1162
+ async applyOrgPolicy(_state, _chainId, policy) {
1163
+ debugLog("zyfai:splitting", "applying org policy", {
1164
+ policy,
1165
+ // Per asset because Zyfai stores splitting under assetTypeSettings[asset].
1166
+ assets: SPLITTING_ASSETS
1167
+ });
1168
+ await Promise.all(
1169
+ SPLITTING_ASSETS.map((asset) => this.applySplitting(asset, policy))
1170
+ );
1171
+ if (policy.poolPolicy) {
1172
+ await this.applyPoolPolicy(policy.poolPolicy);
1173
+ }
1174
+ }
1175
+ async applySplitting(asset, policy) {
1176
+ try {
1177
+ const desired = toZyfaiSplitting(policy);
1178
+ const current = await this.sdk.getUserDetails(asset);
1179
+ const currentSplitting = {
1180
+ splitting: current?.splitting,
1181
+ minSplits: current?.minSplits
1182
+ };
1183
+ const needsUpdate = splittingNeedsUpdate(current, desired);
1184
+ debugLog("zyfai:splitting", `${asset}: comparing`, {
1185
+ mode: policy.splittingMode,
1186
+ current: currentSplitting,
1187
+ desired,
1188
+ // Spelled out because the "why" is the non-obvious part: minSplits is
1189
+ // what separates automatic (1) from force (2-4), not `splitting`.
1190
+ decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
1191
+ });
1192
+ if (!needsUpdate) return;
1193
+ const result = await this.sdk.updateUserProfile({ asset, ...desired });
1194
+ debugLog("zyfai:splitting", `${asset}: wrote updateUserProfile`, {
1195
+ sent: { asset, ...desired },
1196
+ // updateUserProfile re-reads the profile after PATCHing, so this is
1197
+ // Zyfai's own view of the result rather than an echo of what we sent.
1198
+ zyfaiNowReports: {
1199
+ splitting: result?.splitting,
1200
+ minSplits: result?.minSplits
1201
+ }
1202
+ });
1203
+ } catch (error) {
1204
+ console.warn(
1205
+ `[zyfai] applyOrgPolicy(${asset}) failed (non-fatal):`,
1206
+ error instanceof Error ? error.message : String(error)
1207
+ );
1208
+ }
1209
+ }
1210
+ /**
1211
+ * Apply the org's protocol/pool policy to this user's Zyfai profile.
1212
+ *
1213
+ * Two Zyfai writes, in a strict order:
1214
+ * 1. `updateUserProfile({ protocols, autoSelectProtocols })` per asset —
1215
+ * the explicit allow-list. Chain-agnostic, mirrors `applySplitting`.
1216
+ * 2. `customizeBatch(...)` once — per (protocol, chain), asset-agnostic
1217
+ * pool narrowing.
1218
+ *
1219
+ * Order matters: constraining a protocol's pools (step 2) while that
1220
+ * protocol is not yet in the allow-list (step 1 not yet applied) lands the
1221
+ * constraint on something the rebalance engine isn't considering.
1222
+ *
1223
+ * Wrapped so a failure anywhere is non-fatal — this runs as a side effect of
1224
+ * connecting, so a Zyfai hiccup must leave the user connected with their
1225
+ * profile unchanged, never fail activation.
1226
+ */
1227
+ async applyPoolPolicy(policy) {
1228
+ try {
1229
+ const { protocols: universe } = await this.sdk.getAvailableProtocols(
1230
+ this.supportedChainIds[0]
1231
+ );
1232
+ const universeIds = universe.map((p) => p.id);
1233
+ const chainsByProtocol = buildChainsByProtocol(
1234
+ universe,
1235
+ POOL_POLICY_CHAIN_IDS
1236
+ );
1237
+ debugLog("zyfai:pools", "protocol universe", {
1238
+ universeIds,
1239
+ chainsByProtocol
1240
+ });
1241
+ let strategy;
1242
+ const desiredProtocols = deriveProtocols(
1243
+ policy,
1244
+ universeIds,
1245
+ chainsByProtocol
1246
+ );
1247
+ const desiredAutoSelect = policy.autoApproveProtocols;
1248
+ for (const asset of SPLITTING_ASSETS) {
1249
+ const current = await this.sdk.getUserDetails(asset);
1250
+ if (strategy === void 0 && (current.strategy === "conservative" || current.strategy === "aggressive")) {
1251
+ strategy = current.strategy;
1252
+ }
1253
+ const needsUpdate = protocolsPolicyNeedsUpdate(
1254
+ current,
1255
+ desiredProtocols,
1256
+ desiredAutoSelect
1257
+ );
1258
+ debugLog("zyfai:pools", `${asset}: comparing protocols`, {
1259
+ current: {
1260
+ protocols: current.protocols,
1261
+ autoSelectProtocols: current.autoSelectProtocols
1262
+ },
1263
+ desired: {
1264
+ protocols: desiredProtocols,
1265
+ autoSelectProtocols: desiredAutoSelect
1266
+ },
1267
+ decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
1268
+ });
1269
+ if (!needsUpdate) continue;
1270
+ const update = {
1271
+ asset,
1272
+ autoSelectProtocols: desiredAutoSelect
1273
+ };
1274
+ if (desiredProtocols !== null) update.protocols = desiredProtocols;
1275
+ await this.sdk.updateUserProfile(update);
1276
+ debugLog("zyfai:pools", `${asset}: wrote protocols update`, update);
1277
+ }
1278
+ const poolResults = await Promise.all(
1279
+ universe.map(async (protocol) => {
1280
+ try {
1281
+ const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1282
+ return [protocol.id, flattenAvailablePools(raw)];
1283
+ } catch (error) {
1284
+ console.warn(
1285
+ `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
1286
+ error instanceof Error ? error.message : String(error)
1287
+ );
1288
+ return null;
1289
+ }
1290
+ })
1291
+ );
1292
+ const availableByProtocol = /* @__PURE__ */ new Map();
1293
+ for (const result of poolResults) {
1294
+ if (result) availableByProtocol.set(result[0], result[1]);
1295
+ }
1296
+ const customizations = toZyfaiCustomizations(
1297
+ policy,
1298
+ availableByProtocol,
1299
+ chainsByProtocol
1300
+ );
1301
+ debugLog("zyfai:pools", "derived customizeBatch payload", {
1302
+ strategy,
1303
+ availableProtocolCount: availableByProtocol.size,
1304
+ customizations
1305
+ });
1306
+ if (customizations.length === 0) {
1307
+ debugLog("zyfai:pools", "skipping customizeBatch \u2014 nothing to send");
1308
+ return;
1309
+ }
1310
+ await this.sdk.customizeBatch(customizations);
1311
+ debugLog("zyfai:pools", "wrote customizeBatch", {
1312
+ count: customizations.length
1313
+ });
1314
+ } catch (error) {
1315
+ console.warn(
1316
+ "[zyfai] applyPoolPolicy failed (non-fatal):",
1317
+ error instanceof Error ? error.message : String(error)
1318
+ );
1319
+ }
1320
+ }
985
1321
  async deposit(state, chainId, amount, asset, depositCallback) {
986
1322
  try {
987
1323
  const validChainId = isValidChainId(chainId);
@@ -1102,6 +1438,16 @@ var ZyfaiAgent = class {
1102
1438
  async getUserProfile(state, chainId) {
1103
1439
  await this.connectAuth(state, chainId);
1104
1440
  const raw = await this.sdk.getUserDetails();
1441
+ debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
1442
+ asset: "USDC (default \u2014 no asset passed)",
1443
+ splitting: raw.splitting,
1444
+ minSplits: raw.minSplits,
1445
+ strategy: raw.strategy,
1446
+ chains: raw.chains,
1447
+ protocolCount: raw.protocols?.length,
1448
+ hasActiveSessionKey: raw.hasActiveSessionKey,
1449
+ smartWallet: raw.smartWallet
1450
+ });
1105
1451
  return mapUserProfile(raw, this.connectedAddress);
1106
1452
  }
1107
1453
  async ensureAutoSelectProtocols(state, chainId, asset) {
@@ -1134,6 +1480,40 @@ var ZyfaiAgent = class {
1134
1480
 
1135
1481
  // src/lib/routing-api.ts
1136
1482
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1483
+ async function fetchOrgAgentConfig(apiKey) {
1484
+ const url = `${ROUTING_API_BASE_URL}/api/v1/agent/org-config`;
1485
+ try {
1486
+ const res = await fetch(url, {
1487
+ method: "GET",
1488
+ headers: {
1489
+ "Content-Type": "application/json",
1490
+ "x-owney-api-key": `${apiKey}`
1491
+ }
1492
+ });
1493
+ if (!res.ok) {
1494
+ if (res.status !== 404) {
1495
+ console.warn(
1496
+ `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
1497
+ );
1498
+ }
1499
+ return null;
1500
+ }
1501
+ const json = await res.json();
1502
+ const policy = json.success ? json.data ?? null : null;
1503
+ debugLog(
1504
+ "owney-sdk",
1505
+ policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
1506
+ policy ?? void 0
1507
+ );
1508
+ return policy;
1509
+ } catch (error) {
1510
+ console.warn(
1511
+ "[owney-sdk] Could not read org agent config (non-fatal):",
1512
+ error instanceof Error ? error.message : String(error)
1513
+ );
1514
+ return null;
1515
+ }
1516
+ }
1137
1517
  async function fetchAgentKeys(apiKey) {
1138
1518
  const url = `${ROUTING_API_BASE_URL}/api/v1/agent/keys`;
1139
1519
  const res = await fetch(url, {
@@ -1747,6 +2127,12 @@ var OwneySDK = class {
1747
2127
  disabledAgents = /* @__PURE__ */ new Set();
1748
2128
  state = null;
1749
2129
  apiKey;
2130
+ // The org's agent policy, fetched once per client. Applied to each agent on
2131
+ // activation (ROUT-229). `undefined` means "not fetched yet", `null` means
2132
+ // "fetched, and the partner has no policy" — which is a real answer meaning
2133
+ // leave every user's agent profile alone.
2134
+ orgAgentConfig;
2135
+ orgAgentConfigPromise = null;
1750
2136
  zyfaiRpcUrls;
1751
2137
  routingApiBaseUrl;
1752
2138
  referralSource;
@@ -1755,6 +2141,7 @@ var OwneySDK = class {
1755
2141
  initializingAgentsPromise = null;
1756
2142
  constructor(config) {
1757
2143
  this.apiKey = config.apiKey;
2144
+ if (config.debug) setOwneyDebug(true);
1758
2145
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
1759
2146
  this.routingApiBaseUrl = config.routingApiBaseUrl;
1760
2147
  this.referralSource = config.referralSource;
@@ -1916,14 +2303,57 @@ var OwneySDK = class {
1916
2303
  }
1917
2304
  return active;
1918
2305
  }
2306
+ /**
2307
+ * Read the org's agent policy once and reuse it. fetchOrgAgentConfig never
2308
+ * throws — it degrades to null — so this cannot break activation.
2309
+ */
2310
+ async getOrgAgentConfig() {
2311
+ if (this.orgAgentConfig !== void 0) return this.orgAgentConfig;
2312
+ if (!this.orgAgentConfigPromise) {
2313
+ this.orgAgentConfigPromise = fetchOrgAgentConfig(this.apiKey).then(
2314
+ (config) => {
2315
+ this.orgAgentConfig = config;
2316
+ return config;
2317
+ }
2318
+ );
2319
+ }
2320
+ return this.orgAgentConfigPromise;
2321
+ }
2322
+ /**
2323
+ * Apply the org policy to one agent, after it has activated.
2324
+ *
2325
+ * Sequenced after activation rather than alongside it because applying the
2326
+ * policy needs the authenticated session activation establishes. Every failure
2327
+ * mode is swallowed: a partner's splitting preference is not worth failing a
2328
+ * user's connect over.
2329
+ */
2330
+ async applyOrgPolicyTo(agent, state, chainId) {
2331
+ if (!agent.applyOrgPolicy) {
2332
+ debugLog(
2333
+ "owney-sdk",
2334
+ `${agent.id}: no applyOrgPolicy \u2014 agent has no org-level knobs, skipping`
2335
+ );
2336
+ return;
2337
+ }
2338
+ try {
2339
+ const policy = await this.getOrgAgentConfig();
2340
+ if (!policy) return;
2341
+ await agent.applyOrgPolicy(state, chainId, policy);
2342
+ } catch (error) {
2343
+ console.warn(
2344
+ `[owney-sdk] applyOrgPolicy(${agent.id}) failed (non-fatal):`,
2345
+ error instanceof Error ? error.message : String(error)
2346
+ );
2347
+ }
2348
+ }
1919
2349
  async ensureAgentsInitialized() {
1920
2350
  if (this.agents.size > 0) return;
1921
2351
  if (!this.initializingAgentsPromise) {
1922
2352
  this.initializingAgentsPromise = (async () => {
1923
2353
  const agentKeys = await fetchAgentKeys(this.apiKey);
1924
2354
  this.disabledAgents.clear();
1925
- for (const { key, agent_type, is_enabled } of agentKeys) {
1926
- const agent = this.createAgent(agent_type, key);
2355
+ for (const { key: key2, agent_type, is_enabled } of agentKeys) {
2356
+ const agent = this.createAgent(agent_type, key2);
1927
2357
  if (!agent) continue;
1928
2358
  this.agents.set(agent_type, agent);
1929
2359
  if (is_enabled === false) {
@@ -1944,9 +2374,9 @@ var OwneySDK = class {
1944
2374
  this.initializingAgentsPromise = null;
1945
2375
  }
1946
2376
  }
1947
- createAgent(agentId, key) {
2377
+ createAgent(agentId, key2) {
1948
2378
  if (agentId === "zyfai") {
1949
- return new ZyfaiAgent(key, this.zyfaiRpcUrls, this.referralSource);
2379
+ return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
1950
2380
  }
1951
2381
  return null;
1952
2382
  }
@@ -2000,6 +2430,7 @@ var OwneySDK = class {
2000
2430
  Promise.all(
2001
2431
  agents.map(async (agent) => {
2002
2432
  await agent.activateAgent(state, chainId);
2433
+ await this.applyOrgPolicyTo(agent, state, chainId);
2003
2434
  })
2004
2435
  ).catch((error) => {
2005
2436
  console.error("activateAgent background init failed:", error);
@@ -2027,6 +2458,7 @@ var OwneySDK = class {
2027
2458
  Promise.all(
2028
2459
  enabledCompatible.map(async (agent) => {
2029
2460
  await agent.activateAgent(state, chainId);
2461
+ await this.applyOrgPolicyTo(agent, state, chainId);
2030
2462
  })
2031
2463
  ).catch((error) => {
2032
2464
  console.error("activateAgent background init failed:", error);
@@ -2841,22 +3273,22 @@ var readLegacySiwxSession = (store, address) => {
2841
3273
  const legacyKeys = [];
2842
3274
  try {
2843
3275
  for (let i = 0; i < store.length; i++) {
2844
- const key = store.key(i);
2845
- if (key?.startsWith(prefix)) legacyKeys.push(key);
3276
+ const key2 = store.key(i);
3277
+ if (key2?.startsWith(prefix)) legacyKeys.push(key2);
2846
3278
  }
2847
3279
  } catch {
2848
3280
  return null;
2849
3281
  }
2850
3282
  let adopted = null;
2851
- for (const key of legacyKeys) {
3283
+ for (const key2 of legacyKeys) {
2852
3284
  if (!adopted) {
2853
3285
  try {
2854
- adopted = JSON.parse(store.getItem(key) ?? "");
3286
+ adopted = JSON.parse(store.getItem(key2) ?? "");
2855
3287
  } catch {
2856
3288
  }
2857
3289
  }
2858
3290
  try {
2859
- store.removeItem(key);
3291
+ store.removeItem(key2);
2860
3292
  } catch {
2861
3293
  }
2862
3294
  }
@@ -2864,11 +3296,11 @@ var readLegacySiwxSession = (store, address) => {
2864
3296
  };
2865
3297
  var readSiwxSession = (address, chainId) => {
2866
3298
  if (typeof window === "undefined") return null;
2867
- const key = buildKey2(address);
3299
+ const key2 = buildKey2(address);
2868
3300
  const store = storage2();
2869
3301
  let raw = null;
2870
3302
  try {
2871
- raw = store?.getItem(key) ?? null;
3303
+ raw = store?.getItem(key2) ?? null;
2872
3304
  } catch {
2873
3305
  raw = null;
2874
3306
  }
@@ -2876,9 +3308,9 @@ var readSiwxSession = (address, chainId) => {
2876
3308
  try {
2877
3309
  return JSON.parse(raw);
2878
3310
  } catch {
2879
- memorySiwxSessions.delete(key);
3311
+ memorySiwxSessions.delete(key2);
2880
3312
  try {
2881
- store?.removeItem(key);
3313
+ store?.removeItem(key2);
2882
3314
  } catch {
2883
3315
  }
2884
3316
  return null;
@@ -2889,24 +3321,24 @@ var readSiwxSession = (address, chainId) => {
2889
3321
  writeSiwxSession(address, chainId, legacy);
2890
3322
  return legacy;
2891
3323
  }
2892
- return memorySiwxSessions.get(key) ?? null;
3324
+ return memorySiwxSessions.get(key2) ?? null;
2893
3325
  };
2894
3326
  var writeSiwxSession = (address, _chainId, session) => {
2895
3327
  if (typeof window === "undefined") return;
2896
- const key = buildKey2(address);
2897
- memorySiwxSessions.set(key, session);
3328
+ const key2 = buildKey2(address);
3329
+ memorySiwxSessions.set(key2, session);
2898
3330
  const store = storage2();
2899
3331
  try {
2900
- store?.setItem(key, JSON.stringify(session));
3332
+ store?.setItem(key2, JSON.stringify(session));
2901
3333
  } catch {
2902
3334
  }
2903
3335
  };
2904
3336
  var clearSiwxSession = (address, _chainId) => {
2905
- const key = buildKey2(address);
2906
- memorySiwxSessions.delete(key);
3337
+ const key2 = buildKey2(address);
3338
+ memorySiwxSessions.delete(key2);
2907
3339
  const store = storage2();
2908
3340
  try {
2909
- store?.removeItem(key);
3341
+ store?.removeItem(key2);
2910
3342
  } catch {
2911
3343
  }
2912
3344
  readLegacySiwxSession(store, address);
@@ -3036,5 +3468,6 @@ function createOwneySIWX(config) {
3036
3468
  NotConnectedError,
3037
3469
  OwneyError,
3038
3470
  OwneySDK,
3039
- createOwneySIWX
3471
+ createOwneySIWX,
3472
+ setOwneyDebug
3040
3473
  });