@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/README.md +2 -0
- package/dist/index.cjs +486 -53
- package/dist/index.d.cts +108 -1
- package/dist/index.d.ts +108 -1
- package/dist/index.js +481 -49
- package/package.json +2 -2
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
|
|
13
|
-
if (!__hasOwnProp.call(to,
|
|
14
|
-
__defProp(to,
|
|
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
|
|
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
|
-
//
|
|
238
|
-
//
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
563
|
-
if (
|
|
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
|
|
590
|
+
for (const key2 of legacyKeys) {
|
|
570
591
|
if (!adopted) {
|
|
571
592
|
try {
|
|
572
|
-
const parsed = JSON.parse(store.getItem(
|
|
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(
|
|
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
|
|
607
|
+
const key2 = buildKey(address);
|
|
587
608
|
const store = storage();
|
|
588
609
|
let raw = null;
|
|
589
610
|
try {
|
|
590
|
-
raw = store?.getItem(
|
|
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(
|
|
621
|
+
memorySessions.delete(key2);
|
|
601
622
|
try {
|
|
602
|
-
store?.removeItem(
|
|
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(
|
|
633
|
+
const cached = memorySessions.get(key2);
|
|
613
634
|
if (isFreshSession(cached)) return cached;
|
|
614
|
-
if (cached) memorySessions.delete(
|
|
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
|
|
620
|
-
memorySessions.set(
|
|
640
|
+
const key2 = buildKey(address);
|
|
641
|
+
memorySessions.set(key2, session);
|
|
621
642
|
const store = storage();
|
|
622
643
|
try {
|
|
623
|
-
store?.setItem(
|
|
644
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
624
645
|
} catch {
|
|
625
646
|
}
|
|
626
647
|
};
|
|
627
648
|
var clearSession = (address, _chainId) => {
|
|
628
|
-
const
|
|
629
|
-
memorySessions.delete(
|
|
649
|
+
const key2 = buildKey(address);
|
|
650
|
+
memorySessions.delete(key2);
|
|
630
651
|
const store = storage();
|
|
631
652
|
try {
|
|
632
|
-
store?.removeItem(
|
|
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,
|
|
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,
|
|
2377
|
+
createAgent(agentId, key2) {
|
|
1948
2378
|
if (agentId === "zyfai") {
|
|
1949
|
-
return new ZyfaiAgent(
|
|
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
|
|
2845
|
-
if (
|
|
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
|
|
3283
|
+
for (const key2 of legacyKeys) {
|
|
2852
3284
|
if (!adopted) {
|
|
2853
3285
|
try {
|
|
2854
|
-
adopted = JSON.parse(store.getItem(
|
|
3286
|
+
adopted = JSON.parse(store.getItem(key2) ?? "");
|
|
2855
3287
|
} catch {
|
|
2856
3288
|
}
|
|
2857
3289
|
}
|
|
2858
3290
|
try {
|
|
2859
|
-
store.removeItem(
|
|
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
|
|
3299
|
+
const key2 = buildKey2(address);
|
|
2868
3300
|
const store = storage2();
|
|
2869
3301
|
let raw = null;
|
|
2870
3302
|
try {
|
|
2871
|
-
raw = store?.getItem(
|
|
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(
|
|
3311
|
+
memorySiwxSessions.delete(key2);
|
|
2880
3312
|
try {
|
|
2881
|
-
store?.removeItem(
|
|
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(
|
|
3324
|
+
return memorySiwxSessions.get(key2) ?? null;
|
|
2893
3325
|
};
|
|
2894
3326
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
2895
3327
|
if (typeof window === "undefined") return;
|
|
2896
|
-
const
|
|
2897
|
-
memorySiwxSessions.set(
|
|
3328
|
+
const key2 = buildKey2(address);
|
|
3329
|
+
memorySiwxSessions.set(key2, session);
|
|
2898
3330
|
const store = storage2();
|
|
2899
3331
|
try {
|
|
2900
|
-
store?.setItem(
|
|
3332
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
2901
3333
|
} catch {
|
|
2902
3334
|
}
|
|
2903
3335
|
};
|
|
2904
3336
|
var clearSiwxSession = (address, _chainId) => {
|
|
2905
|
-
const
|
|
2906
|
-
memorySiwxSessions.delete(
|
|
3337
|
+
const key2 = buildKey2(address);
|
|
3338
|
+
memorySiwxSessions.delete(key2);
|
|
2907
3339
|
const store = storage2();
|
|
2908
3340
|
try {
|
|
2909
|
-
store?.removeItem(
|
|
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
|
});
|