@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.js
CHANGED
|
@@ -125,6 +125,14 @@ function hexToDecimal(hex, decimals = 6) {
|
|
|
125
125
|
const remainder = parsed % divisor;
|
|
126
126
|
return Number(whole) + Number(remainder) / Number(divisor);
|
|
127
127
|
}
|
|
128
|
+
function normalizeDecimalPlace(value, decimals = 6) {
|
|
129
|
+
const parsed = BigInt(value);
|
|
130
|
+
if (decimals <= 0) return Number(parsed);
|
|
131
|
+
const divisor = BigInt(10 ** decimals);
|
|
132
|
+
const whole = parsed / divisor;
|
|
133
|
+
const remainder = parsed % divisor;
|
|
134
|
+
return Number(whole) + Number(remainder) / Number(divisor);
|
|
135
|
+
}
|
|
128
136
|
var DayFilterMapping = {
|
|
129
137
|
"30D": 30,
|
|
130
138
|
"14D": 14,
|
|
@@ -183,10 +191,10 @@ function mapBalances(raw, _chainId, smartWallet) {
|
|
|
183
191
|
const tokens = [];
|
|
184
192
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
|
|
185
193
|
const positionChainId = Number(chainIdKey);
|
|
186
|
-
for (const [symbol, { balance, decimals }] of Object.entries(
|
|
194
|
+
for (const [symbol, { balance, balanceWithFee, decimals }] of Object.entries(
|
|
187
195
|
tokensBySymbol ?? {}
|
|
188
196
|
)) {
|
|
189
|
-
const amount = hexToDecimal(balance
|
|
197
|
+
const amount = hexToDecimal(balanceWithFee ?? balance ?? "0", decimals);
|
|
190
198
|
totalBalance += amount;
|
|
191
199
|
tokens.push({
|
|
192
200
|
chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
|
|
@@ -202,15 +210,23 @@ function mapBalances(raw, _chainId, smartWallet) {
|
|
|
202
210
|
protocolId: p.protocol_id,
|
|
203
211
|
pool: p.pool,
|
|
204
212
|
asset: p.token_symbol ?? "",
|
|
205
|
-
//
|
|
206
|
-
//
|
|
213
|
+
// Fee-adjusted underlying amounts are decimal base-unit strings. The
|
|
214
|
+
// legacy `amount` fallback is hex. Runtime
|
|
207
215
|
// getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
|
|
208
|
-
// when present, otherwise fall back to the per-symbol lookup
|
|
209
|
-
// non-6-decimal assets like WETH don't render 1e12 too large.
|
|
216
|
+
// when present, otherwise fall back to the per-symbol lookup.
|
|
210
217
|
amount: String(
|
|
211
|
-
|
|
218
|
+
p.underlyingAmountWithFee ?? p.underlyingAmount ? normalizeDecimalPlace(
|
|
219
|
+
p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0",
|
|
220
|
+
tokenDecimals(
|
|
221
|
+
p.token_symbol,
|
|
222
|
+
p.decimals
|
|
223
|
+
)
|
|
224
|
+
) : hexToDecimal(
|
|
212
225
|
p.amount || "0",
|
|
213
|
-
tokenDecimals(
|
|
226
|
+
tokenDecimals(
|
|
227
|
+
p.token_symbol,
|
|
228
|
+
p.decimals
|
|
229
|
+
)
|
|
214
230
|
)
|
|
215
231
|
),
|
|
216
232
|
apy: p.pool_apy,
|
|
@@ -234,7 +250,7 @@ function sumTokenEarnings(tokens) {
|
|
|
234
250
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
235
251
|
}
|
|
236
252
|
function mapEarnings(raw, smartWallet) {
|
|
237
|
-
const totalEarningsByChain = raw.data.totalEarningsByChain;
|
|
253
|
+
const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
|
|
238
254
|
const tokens = [];
|
|
239
255
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
240
256
|
totalEarningsByChain
|
|
@@ -252,7 +268,9 @@ function mapEarnings(raw, smartWallet) {
|
|
|
252
268
|
}
|
|
253
269
|
return {
|
|
254
270
|
smartWallet,
|
|
255
|
-
lifetimeEarnings: sumTokenEarnings(
|
|
271
|
+
lifetimeEarnings: sumTokenEarnings(
|
|
272
|
+
raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
|
|
273
|
+
),
|
|
256
274
|
tokens
|
|
257
275
|
};
|
|
258
276
|
}
|
|
@@ -380,7 +398,9 @@ function mapUserProfile(raw, userAddress) {
|
|
|
380
398
|
chains: raw.chains || [],
|
|
381
399
|
strategy: raw.strategy,
|
|
382
400
|
hasActiveSessionKey: raw.hasActiveSessionKey || false,
|
|
383
|
-
protocols: raw.protocols || []
|
|
401
|
+
protocols: raw.protocols || [],
|
|
402
|
+
splitting: raw.splitting,
|
|
403
|
+
minSplits: raw.minSplits
|
|
384
404
|
};
|
|
385
405
|
}
|
|
386
406
|
function mapApyByStrategy(raw) {
|
|
@@ -527,23 +547,23 @@ var readLegacySession = (store, address) => {
|
|
|
527
547
|
const legacyKeys = [];
|
|
528
548
|
try {
|
|
529
549
|
for (let i = 0; i < store.length; i++) {
|
|
530
|
-
const
|
|
531
|
-
if (
|
|
550
|
+
const key2 = store.key(i);
|
|
551
|
+
if (key2?.startsWith(prefix)) legacyKeys.push(key2);
|
|
532
552
|
}
|
|
533
553
|
} catch {
|
|
534
554
|
return null;
|
|
535
555
|
}
|
|
536
556
|
let adopted = null;
|
|
537
|
-
for (const
|
|
557
|
+
for (const key2 of legacyKeys) {
|
|
538
558
|
if (!adopted) {
|
|
539
559
|
try {
|
|
540
|
-
const parsed = JSON.parse(store.getItem(
|
|
560
|
+
const parsed = JSON.parse(store.getItem(key2) ?? "");
|
|
541
561
|
if (isFreshSession(parsed)) adopted = parsed;
|
|
542
562
|
} catch {
|
|
543
563
|
}
|
|
544
564
|
}
|
|
545
565
|
try {
|
|
546
|
-
store.removeItem(
|
|
566
|
+
store.removeItem(key2);
|
|
547
567
|
} catch {
|
|
548
568
|
}
|
|
549
569
|
}
|
|
@@ -551,11 +571,11 @@ var readLegacySession = (store, address) => {
|
|
|
551
571
|
};
|
|
552
572
|
var readSession = (address, _chainId) => {
|
|
553
573
|
if (typeof window === "undefined") return null;
|
|
554
|
-
const
|
|
574
|
+
const key2 = buildKey(address);
|
|
555
575
|
const store = storage();
|
|
556
576
|
let raw = null;
|
|
557
577
|
try {
|
|
558
|
-
raw = store?.getItem(
|
|
578
|
+
raw = store?.getItem(key2) ?? null;
|
|
559
579
|
} catch {
|
|
560
580
|
raw = null;
|
|
561
581
|
}
|
|
@@ -565,9 +585,9 @@ var readSession = (address, _chainId) => {
|
|
|
565
585
|
if (isFreshSession(parsed)) return parsed;
|
|
566
586
|
} catch {
|
|
567
587
|
}
|
|
568
|
-
memorySessions.delete(
|
|
588
|
+
memorySessions.delete(key2);
|
|
569
589
|
try {
|
|
570
|
-
store?.removeItem(
|
|
590
|
+
store?.removeItem(key2);
|
|
571
591
|
} catch {
|
|
572
592
|
}
|
|
573
593
|
return null;
|
|
@@ -577,27 +597,27 @@ var readSession = (address, _chainId) => {
|
|
|
577
597
|
writeSession(address, _chainId, legacy);
|
|
578
598
|
return legacy;
|
|
579
599
|
}
|
|
580
|
-
const cached = memorySessions.get(
|
|
600
|
+
const cached = memorySessions.get(key2);
|
|
581
601
|
if (isFreshSession(cached)) return cached;
|
|
582
|
-
if (cached) memorySessions.delete(
|
|
602
|
+
if (cached) memorySessions.delete(key2);
|
|
583
603
|
return null;
|
|
584
604
|
};
|
|
585
605
|
var writeSession = (address, _chainId, session) => {
|
|
586
606
|
if (typeof window === "undefined") return;
|
|
587
|
-
const
|
|
588
|
-
memorySessions.set(
|
|
607
|
+
const key2 = buildKey(address);
|
|
608
|
+
memorySessions.set(key2, session);
|
|
589
609
|
const store = storage();
|
|
590
610
|
try {
|
|
591
|
-
store?.setItem(
|
|
611
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
592
612
|
} catch {
|
|
593
613
|
}
|
|
594
614
|
};
|
|
595
615
|
var clearSession = (address, _chainId) => {
|
|
596
|
-
const
|
|
597
|
-
memorySessions.delete(
|
|
616
|
+
const key2 = buildKey(address);
|
|
617
|
+
memorySessions.delete(key2);
|
|
598
618
|
const store = storage();
|
|
599
619
|
try {
|
|
600
|
-
store?.removeItem(
|
|
620
|
+
store?.removeItem(key2);
|
|
601
621
|
} catch {
|
|
602
622
|
}
|
|
603
623
|
readLegacySession(store, address);
|
|
@@ -628,6 +648,149 @@ function buildProtocolMergeUpdate(available, current, asset) {
|
|
|
628
648
|
};
|
|
629
649
|
}
|
|
630
650
|
|
|
651
|
+
// src/agents/zyfai/splitting.ts
|
|
652
|
+
var MAX_MIN_SPLITS = 4;
|
|
653
|
+
var MIN_FORCE_SPLITS = 2;
|
|
654
|
+
var AUTOMATIC_MIN_SPLITS = 1;
|
|
655
|
+
function toZyfaiSplitting(policy) {
|
|
656
|
+
switch (policy.splittingMode) {
|
|
657
|
+
case "none":
|
|
658
|
+
return { splitting: false };
|
|
659
|
+
case "automatic":
|
|
660
|
+
return { splitting: true, minSplits: AUTOMATIC_MIN_SPLITS };
|
|
661
|
+
case "force":
|
|
662
|
+
return {
|
|
663
|
+
splitting: true,
|
|
664
|
+
minSplits: clampForceSplits(policy.minSplits)
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function clampForceSplits(minSplits) {
|
|
669
|
+
if (typeof minSplits !== "number" || !Number.isFinite(minSplits)) {
|
|
670
|
+
return MIN_FORCE_SPLITS;
|
|
671
|
+
}
|
|
672
|
+
const rounded = Math.round(minSplits);
|
|
673
|
+
if (rounded < MIN_FORCE_SPLITS) return MIN_FORCE_SPLITS;
|
|
674
|
+
if (rounded > MAX_MIN_SPLITS) return MAX_MIN_SPLITS;
|
|
675
|
+
return rounded;
|
|
676
|
+
}
|
|
677
|
+
function splittingNeedsUpdate(current, desired) {
|
|
678
|
+
if (!current) return true;
|
|
679
|
+
if (current.splitting !== desired.splitting) return true;
|
|
680
|
+
if (desired.minSplits === void 0) return false;
|
|
681
|
+
return current.minSplits !== desired.minSplits;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// src/agents/zyfai/pool-policy.ts
|
|
685
|
+
function toZyfaiCustomizations(policy, availableByProtocol, chainsByProtocol) {
|
|
686
|
+
const selectionByKey = /* @__PURE__ */ new Map();
|
|
687
|
+
for (const selection of policy.selections) {
|
|
688
|
+
selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
|
|
689
|
+
}
|
|
690
|
+
const customizations = [];
|
|
691
|
+
for (const [protocolId, chainIds] of Object.entries(chainsByProtocol)) {
|
|
692
|
+
const available = availableByProtocol.get(protocolId);
|
|
693
|
+
if (available === void 0) {
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
const availableSet = new Set(available);
|
|
697
|
+
for (const chainId of chainIds) {
|
|
698
|
+
const stored = selectionByKey.get(key(protocolId, chainId));
|
|
699
|
+
if (stored === void 0) {
|
|
700
|
+
customizations.push({
|
|
701
|
+
protocolId,
|
|
702
|
+
chainId,
|
|
703
|
+
pools: [],
|
|
704
|
+
autoselect: true
|
|
705
|
+
});
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
const pools = stored.pools.filter((pool) => availableSet.has(pool));
|
|
709
|
+
customizations.push({
|
|
710
|
+
protocolId,
|
|
711
|
+
chainId,
|
|
712
|
+
pools,
|
|
713
|
+
autoselect: false
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return customizations;
|
|
718
|
+
}
|
|
719
|
+
function deriveProtocols(policy, universe, chainsByProtocol) {
|
|
720
|
+
if (policy.selections.length === 0) {
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
723
|
+
const selectionByKey = /* @__PURE__ */ new Map();
|
|
724
|
+
for (const selection of policy.selections) {
|
|
725
|
+
selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
|
|
726
|
+
}
|
|
727
|
+
return universe.filter((protocolId) => {
|
|
728
|
+
const chainIds = chainsByProtocol[protocolId];
|
|
729
|
+
if (chainIds === void 0 || chainIds.length === 0) {
|
|
730
|
+
return true;
|
|
731
|
+
}
|
|
732
|
+
const fullyDisabled = chainIds.every((chainId) => {
|
|
733
|
+
const stored = selectionByKey.get(key(protocolId, chainId));
|
|
734
|
+
return stored !== void 0 && stored.pools.length === 0;
|
|
735
|
+
});
|
|
736
|
+
return !fullyDisabled;
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
function key(protocolId, chainId) {
|
|
740
|
+
return `${protocolId}:${chainId}`;
|
|
741
|
+
}
|
|
742
|
+
function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
743
|
+
const allowed = new Set(dashboardChainIds);
|
|
744
|
+
const result = {};
|
|
745
|
+
for (const protocol of protocols) {
|
|
746
|
+
result[protocol.id] = (protocol.chains ?? []).filter(
|
|
747
|
+
(chainId) => allowed.has(chainId)
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
return result;
|
|
751
|
+
}
|
|
752
|
+
function flattenAvailablePools(raw) {
|
|
753
|
+
const byChain = raw ?? {};
|
|
754
|
+
const names = [];
|
|
755
|
+
for (const byToken of Object.values(byChain ?? {})) {
|
|
756
|
+
for (const entry of Object.values(byToken ?? {})) {
|
|
757
|
+
for (const pool of entry?.pools ?? []) {
|
|
758
|
+
if (pool?.name) names.push(pool.name);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return names;
|
|
763
|
+
}
|
|
764
|
+
function protocolListsEqual(a, b) {
|
|
765
|
+
const left = [...a ?? []].sort();
|
|
766
|
+
const right = [...b ?? []].sort();
|
|
767
|
+
if (left.length !== right.length) return false;
|
|
768
|
+
return left.every((value, index) => value === right[index]);
|
|
769
|
+
}
|
|
770
|
+
function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect) {
|
|
771
|
+
if (!current) return true;
|
|
772
|
+
if (current.autoSelectProtocols !== desiredAutoSelect) return true;
|
|
773
|
+
if (desiredProtocols === null) return false;
|
|
774
|
+
return !protocolListsEqual(current.protocols, desiredProtocols);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// src/lib/debug.ts
|
|
778
|
+
var configuredDebug = false;
|
|
779
|
+
function setOwneyDebug(enabled) {
|
|
780
|
+
configuredDebug = enabled;
|
|
781
|
+
}
|
|
782
|
+
function isOwneyDebug() {
|
|
783
|
+
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
784
|
+
}
|
|
785
|
+
function debugLog(scope, message, data) {
|
|
786
|
+
if (!isOwneyDebug()) return;
|
|
787
|
+
if (data === void 0) {
|
|
788
|
+
console.log(`[${scope}] ${message}`);
|
|
789
|
+
} else {
|
|
790
|
+
console.log(`[${scope}] ${message}`, data);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
631
794
|
// src/agents/zyfai/zyfai.agent.ts
|
|
632
795
|
var DEFAULT_ZYFAI_RPC_URLS = {
|
|
633
796
|
8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
@@ -640,6 +803,8 @@ var WETH_ADDRESS_BY_CHAIN = {
|
|
|
640
803
|
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
641
804
|
};
|
|
642
805
|
var VIEM_CHAIN = { 8453: base, 42161: arbitrum, 1: mainnet };
|
|
806
|
+
var SPLITTING_ASSETS = ["USDC", "WETH"];
|
|
807
|
+
var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
|
|
643
808
|
var ZyfaiAgent = class {
|
|
644
809
|
id = "zyfai";
|
|
645
810
|
supportedChainIds = [8453, 42161, 1];
|
|
@@ -950,6 +1115,176 @@ var ZyfaiAgent = class {
|
|
|
950
1115
|
throw error;
|
|
951
1116
|
}
|
|
952
1117
|
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Apply the org's capital splitting policy to this user's Zyfai profile.
|
|
1120
|
+
*
|
|
1121
|
+
* Per ASSET, not per chain: Zyfai stores splitting under
|
|
1122
|
+
* `assetTypeSettings[asset]`, so one call would only configure USDC and leave a
|
|
1123
|
+
* WETH holder's real setting untouched.
|
|
1124
|
+
*
|
|
1125
|
+
* Non-fatal throughout. This is a side effect of connecting, so a Zyfai hiccup
|
|
1126
|
+
* must leave the user connected with their profile unchanged, never fail
|
|
1127
|
+
* activation.
|
|
1128
|
+
*/
|
|
1129
|
+
async applyOrgPolicy(_state, _chainId, policy) {
|
|
1130
|
+
debugLog("zyfai:splitting", "applying org policy", {
|
|
1131
|
+
policy,
|
|
1132
|
+
// Per asset because Zyfai stores splitting under assetTypeSettings[asset].
|
|
1133
|
+
assets: SPLITTING_ASSETS
|
|
1134
|
+
});
|
|
1135
|
+
await Promise.all(
|
|
1136
|
+
SPLITTING_ASSETS.map((asset) => this.applySplitting(asset, policy))
|
|
1137
|
+
);
|
|
1138
|
+
if (policy.poolPolicy) {
|
|
1139
|
+
await this.applyPoolPolicy(policy.poolPolicy);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
async applySplitting(asset, policy) {
|
|
1143
|
+
try {
|
|
1144
|
+
const desired = toZyfaiSplitting(policy);
|
|
1145
|
+
const current = await this.sdk.getUserDetails(asset);
|
|
1146
|
+
const currentSplitting = {
|
|
1147
|
+
splitting: current?.splitting,
|
|
1148
|
+
minSplits: current?.minSplits
|
|
1149
|
+
};
|
|
1150
|
+
const needsUpdate = splittingNeedsUpdate(current, desired);
|
|
1151
|
+
debugLog("zyfai:splitting", `${asset}: comparing`, {
|
|
1152
|
+
mode: policy.splittingMode,
|
|
1153
|
+
current: currentSplitting,
|
|
1154
|
+
desired,
|
|
1155
|
+
// Spelled out because the "why" is the non-obvious part: minSplits is
|
|
1156
|
+
// what separates automatic (1) from force (2-4), not `splitting`.
|
|
1157
|
+
decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
|
|
1158
|
+
});
|
|
1159
|
+
if (!needsUpdate) return;
|
|
1160
|
+
const result = await this.sdk.updateUserProfile({ asset, ...desired });
|
|
1161
|
+
debugLog("zyfai:splitting", `${asset}: wrote updateUserProfile`, {
|
|
1162
|
+
sent: { asset, ...desired },
|
|
1163
|
+
// updateUserProfile re-reads the profile after PATCHing, so this is
|
|
1164
|
+
// Zyfai's own view of the result rather than an echo of what we sent.
|
|
1165
|
+
zyfaiNowReports: {
|
|
1166
|
+
splitting: result?.splitting,
|
|
1167
|
+
minSplits: result?.minSplits
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
} catch (error) {
|
|
1171
|
+
console.warn(
|
|
1172
|
+
`[zyfai] applyOrgPolicy(${asset}) failed (non-fatal):`,
|
|
1173
|
+
error instanceof Error ? error.message : String(error)
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Apply the org's protocol/pool policy to this user's Zyfai profile.
|
|
1179
|
+
*
|
|
1180
|
+
* Two Zyfai writes, in a strict order:
|
|
1181
|
+
* 1. `updateUserProfile({ protocols, autoSelectProtocols })` per asset —
|
|
1182
|
+
* the explicit allow-list. Chain-agnostic, mirrors `applySplitting`.
|
|
1183
|
+
* 2. `customizeBatch(...)` once — per (protocol, chain), asset-agnostic
|
|
1184
|
+
* pool narrowing.
|
|
1185
|
+
*
|
|
1186
|
+
* Order matters: constraining a protocol's pools (step 2) while that
|
|
1187
|
+
* protocol is not yet in the allow-list (step 1 not yet applied) lands the
|
|
1188
|
+
* constraint on something the rebalance engine isn't considering.
|
|
1189
|
+
*
|
|
1190
|
+
* Wrapped so a failure anywhere is non-fatal — this runs as a side effect of
|
|
1191
|
+
* connecting, so a Zyfai hiccup must leave the user connected with their
|
|
1192
|
+
* profile unchanged, never fail activation.
|
|
1193
|
+
*/
|
|
1194
|
+
async applyPoolPolicy(policy) {
|
|
1195
|
+
try {
|
|
1196
|
+
const { protocols: universe } = await this.sdk.getAvailableProtocols(
|
|
1197
|
+
this.supportedChainIds[0]
|
|
1198
|
+
);
|
|
1199
|
+
const universeIds = universe.map((p) => p.id);
|
|
1200
|
+
const chainsByProtocol = buildChainsByProtocol(
|
|
1201
|
+
universe,
|
|
1202
|
+
POOL_POLICY_CHAIN_IDS
|
|
1203
|
+
);
|
|
1204
|
+
debugLog("zyfai:pools", "protocol universe", {
|
|
1205
|
+
universeIds,
|
|
1206
|
+
chainsByProtocol
|
|
1207
|
+
});
|
|
1208
|
+
let strategy;
|
|
1209
|
+
const desiredProtocols = deriveProtocols(
|
|
1210
|
+
policy,
|
|
1211
|
+
universeIds,
|
|
1212
|
+
chainsByProtocol
|
|
1213
|
+
);
|
|
1214
|
+
const desiredAutoSelect = policy.autoApproveProtocols;
|
|
1215
|
+
for (const asset of SPLITTING_ASSETS) {
|
|
1216
|
+
const current = await this.sdk.getUserDetails(asset);
|
|
1217
|
+
if (strategy === void 0 && (current.strategy === "conservative" || current.strategy === "aggressive")) {
|
|
1218
|
+
strategy = current.strategy;
|
|
1219
|
+
}
|
|
1220
|
+
const needsUpdate = protocolsPolicyNeedsUpdate(
|
|
1221
|
+
current,
|
|
1222
|
+
desiredProtocols,
|
|
1223
|
+
desiredAutoSelect
|
|
1224
|
+
);
|
|
1225
|
+
debugLog("zyfai:pools", `${asset}: comparing protocols`, {
|
|
1226
|
+
current: {
|
|
1227
|
+
protocols: current.protocols,
|
|
1228
|
+
autoSelectProtocols: current.autoSelectProtocols
|
|
1229
|
+
},
|
|
1230
|
+
desired: {
|
|
1231
|
+
protocols: desiredProtocols,
|
|
1232
|
+
autoSelectProtocols: desiredAutoSelect
|
|
1233
|
+
},
|
|
1234
|
+
decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
|
|
1235
|
+
});
|
|
1236
|
+
if (!needsUpdate) continue;
|
|
1237
|
+
const update = {
|
|
1238
|
+
asset,
|
|
1239
|
+
autoSelectProtocols: desiredAutoSelect
|
|
1240
|
+
};
|
|
1241
|
+
if (desiredProtocols !== null) update.protocols = desiredProtocols;
|
|
1242
|
+
await this.sdk.updateUserProfile(update);
|
|
1243
|
+
debugLog("zyfai:pools", `${asset}: wrote protocols update`, update);
|
|
1244
|
+
}
|
|
1245
|
+
const poolResults = await Promise.all(
|
|
1246
|
+
universe.map(async (protocol) => {
|
|
1247
|
+
try {
|
|
1248
|
+
const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1249
|
+
return [protocol.id, flattenAvailablePools(raw)];
|
|
1250
|
+
} catch (error) {
|
|
1251
|
+
console.warn(
|
|
1252
|
+
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
1253
|
+
error instanceof Error ? error.message : String(error)
|
|
1254
|
+
);
|
|
1255
|
+
return null;
|
|
1256
|
+
}
|
|
1257
|
+
})
|
|
1258
|
+
);
|
|
1259
|
+
const availableByProtocol = /* @__PURE__ */ new Map();
|
|
1260
|
+
for (const result of poolResults) {
|
|
1261
|
+
if (result) availableByProtocol.set(result[0], result[1]);
|
|
1262
|
+
}
|
|
1263
|
+
const customizations = toZyfaiCustomizations(
|
|
1264
|
+
policy,
|
|
1265
|
+
availableByProtocol,
|
|
1266
|
+
chainsByProtocol
|
|
1267
|
+
);
|
|
1268
|
+
debugLog("zyfai:pools", "derived customizeBatch payload", {
|
|
1269
|
+
strategy,
|
|
1270
|
+
availableProtocolCount: availableByProtocol.size,
|
|
1271
|
+
customizations
|
|
1272
|
+
});
|
|
1273
|
+
if (customizations.length === 0) {
|
|
1274
|
+
debugLog("zyfai:pools", "skipping customizeBatch \u2014 nothing to send");
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
await this.sdk.customizeBatch(customizations);
|
|
1278
|
+
debugLog("zyfai:pools", "wrote customizeBatch", {
|
|
1279
|
+
count: customizations.length
|
|
1280
|
+
});
|
|
1281
|
+
} catch (error) {
|
|
1282
|
+
console.warn(
|
|
1283
|
+
"[zyfai] applyPoolPolicy failed (non-fatal):",
|
|
1284
|
+
error instanceof Error ? error.message : String(error)
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
953
1288
|
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
954
1289
|
try {
|
|
955
1290
|
const validChainId = isValidChainId(chainId);
|
|
@@ -1070,6 +1405,16 @@ var ZyfaiAgent = class {
|
|
|
1070
1405
|
async getUserProfile(state, chainId) {
|
|
1071
1406
|
await this.connectAuth(state, chainId);
|
|
1072
1407
|
const raw = await this.sdk.getUserDetails();
|
|
1408
|
+
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
1409
|
+
asset: "USDC (default \u2014 no asset passed)",
|
|
1410
|
+
splitting: raw.splitting,
|
|
1411
|
+
minSplits: raw.minSplits,
|
|
1412
|
+
strategy: raw.strategy,
|
|
1413
|
+
chains: raw.chains,
|
|
1414
|
+
protocolCount: raw.protocols?.length,
|
|
1415
|
+
hasActiveSessionKey: raw.hasActiveSessionKey,
|
|
1416
|
+
smartWallet: raw.smartWallet
|
|
1417
|
+
});
|
|
1073
1418
|
return mapUserProfile(raw, this.connectedAddress);
|
|
1074
1419
|
}
|
|
1075
1420
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
@@ -1102,6 +1447,40 @@ var ZyfaiAgent = class {
|
|
|
1102
1447
|
|
|
1103
1448
|
// src/lib/routing-api.ts
|
|
1104
1449
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1450
|
+
async function fetchOrgAgentConfig(apiKey) {
|
|
1451
|
+
const url = `${ROUTING_API_BASE_URL}/api/v1/agent/org-config`;
|
|
1452
|
+
try {
|
|
1453
|
+
const res = await fetch(url, {
|
|
1454
|
+
method: "GET",
|
|
1455
|
+
headers: {
|
|
1456
|
+
"Content-Type": "application/json",
|
|
1457
|
+
"x-owney-api-key": `${apiKey}`
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1460
|
+
if (!res.ok) {
|
|
1461
|
+
if (res.status !== 404) {
|
|
1462
|
+
console.warn(
|
|
1463
|
+
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
return null;
|
|
1467
|
+
}
|
|
1468
|
+
const json = await res.json();
|
|
1469
|
+
const policy = json.success ? json.data ?? null : null;
|
|
1470
|
+
debugLog(
|
|
1471
|
+
"owney-sdk",
|
|
1472
|
+
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
1473
|
+
policy ?? void 0
|
|
1474
|
+
);
|
|
1475
|
+
return policy;
|
|
1476
|
+
} catch (error) {
|
|
1477
|
+
console.warn(
|
|
1478
|
+
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
1479
|
+
error instanceof Error ? error.message : String(error)
|
|
1480
|
+
);
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1105
1484
|
async function fetchAgentKeys(apiKey) {
|
|
1106
1485
|
const url = `${ROUTING_API_BASE_URL}/api/v1/agent/keys`;
|
|
1107
1486
|
const res = await fetch(url, {
|
|
@@ -1719,6 +2098,12 @@ var OwneySDK = class {
|
|
|
1719
2098
|
disabledAgents = /* @__PURE__ */ new Set();
|
|
1720
2099
|
state = null;
|
|
1721
2100
|
apiKey;
|
|
2101
|
+
// The org's agent policy, fetched once per client. Applied to each agent on
|
|
2102
|
+
// activation (ROUT-229). `undefined` means "not fetched yet", `null` means
|
|
2103
|
+
// "fetched, and the partner has no policy" — which is a real answer meaning
|
|
2104
|
+
// leave every user's agent profile alone.
|
|
2105
|
+
orgAgentConfig;
|
|
2106
|
+
orgAgentConfigPromise = null;
|
|
1722
2107
|
zyfaiRpcUrls;
|
|
1723
2108
|
routingApiBaseUrl;
|
|
1724
2109
|
referralSource;
|
|
@@ -1727,6 +2112,7 @@ var OwneySDK = class {
|
|
|
1727
2112
|
initializingAgentsPromise = null;
|
|
1728
2113
|
constructor(config) {
|
|
1729
2114
|
this.apiKey = config.apiKey;
|
|
2115
|
+
if (config.debug) setOwneyDebug(true);
|
|
1730
2116
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
1731
2117
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
1732
2118
|
this.referralSource = config.referralSource;
|
|
@@ -1888,14 +2274,57 @@ var OwneySDK = class {
|
|
|
1888
2274
|
}
|
|
1889
2275
|
return active;
|
|
1890
2276
|
}
|
|
2277
|
+
/**
|
|
2278
|
+
* Read the org's agent policy once and reuse it. fetchOrgAgentConfig never
|
|
2279
|
+
* throws — it degrades to null — so this cannot break activation.
|
|
2280
|
+
*/
|
|
2281
|
+
async getOrgAgentConfig() {
|
|
2282
|
+
if (this.orgAgentConfig !== void 0) return this.orgAgentConfig;
|
|
2283
|
+
if (!this.orgAgentConfigPromise) {
|
|
2284
|
+
this.orgAgentConfigPromise = fetchOrgAgentConfig(this.apiKey).then(
|
|
2285
|
+
(config) => {
|
|
2286
|
+
this.orgAgentConfig = config;
|
|
2287
|
+
return config;
|
|
2288
|
+
}
|
|
2289
|
+
);
|
|
2290
|
+
}
|
|
2291
|
+
return this.orgAgentConfigPromise;
|
|
2292
|
+
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Apply the org policy to one agent, after it has activated.
|
|
2295
|
+
*
|
|
2296
|
+
* Sequenced after activation rather than alongside it because applying the
|
|
2297
|
+
* policy needs the authenticated session activation establishes. Every failure
|
|
2298
|
+
* mode is swallowed: a partner's splitting preference is not worth failing a
|
|
2299
|
+
* user's connect over.
|
|
2300
|
+
*/
|
|
2301
|
+
async applyOrgPolicyTo(agent, state, chainId) {
|
|
2302
|
+
if (!agent.applyOrgPolicy) {
|
|
2303
|
+
debugLog(
|
|
2304
|
+
"owney-sdk",
|
|
2305
|
+
`${agent.id}: no applyOrgPolicy \u2014 agent has no org-level knobs, skipping`
|
|
2306
|
+
);
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
try {
|
|
2310
|
+
const policy = await this.getOrgAgentConfig();
|
|
2311
|
+
if (!policy) return;
|
|
2312
|
+
await agent.applyOrgPolicy(state, chainId, policy);
|
|
2313
|
+
} catch (error) {
|
|
2314
|
+
console.warn(
|
|
2315
|
+
`[owney-sdk] applyOrgPolicy(${agent.id}) failed (non-fatal):`,
|
|
2316
|
+
error instanceof Error ? error.message : String(error)
|
|
2317
|
+
);
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
1891
2320
|
async ensureAgentsInitialized() {
|
|
1892
2321
|
if (this.agents.size > 0) return;
|
|
1893
2322
|
if (!this.initializingAgentsPromise) {
|
|
1894
2323
|
this.initializingAgentsPromise = (async () => {
|
|
1895
2324
|
const agentKeys = await fetchAgentKeys(this.apiKey);
|
|
1896
2325
|
this.disabledAgents.clear();
|
|
1897
|
-
for (const { key, agent_type, is_enabled } of agentKeys) {
|
|
1898
|
-
const agent = this.createAgent(agent_type,
|
|
2326
|
+
for (const { key: key2, agent_type, is_enabled } of agentKeys) {
|
|
2327
|
+
const agent = this.createAgent(agent_type, key2);
|
|
1899
2328
|
if (!agent) continue;
|
|
1900
2329
|
this.agents.set(agent_type, agent);
|
|
1901
2330
|
if (is_enabled === false) {
|
|
@@ -1916,9 +2345,9 @@ var OwneySDK = class {
|
|
|
1916
2345
|
this.initializingAgentsPromise = null;
|
|
1917
2346
|
}
|
|
1918
2347
|
}
|
|
1919
|
-
createAgent(agentId,
|
|
2348
|
+
createAgent(agentId, key2) {
|
|
1920
2349
|
if (agentId === "zyfai") {
|
|
1921
|
-
return new ZyfaiAgent(
|
|
2350
|
+
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
1922
2351
|
}
|
|
1923
2352
|
return null;
|
|
1924
2353
|
}
|
|
@@ -1972,6 +2401,7 @@ var OwneySDK = class {
|
|
|
1972
2401
|
Promise.all(
|
|
1973
2402
|
agents.map(async (agent) => {
|
|
1974
2403
|
await agent.activateAgent(state, chainId);
|
|
2404
|
+
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
1975
2405
|
})
|
|
1976
2406
|
).catch((error) => {
|
|
1977
2407
|
console.error("activateAgent background init failed:", error);
|
|
@@ -1999,6 +2429,7 @@ var OwneySDK = class {
|
|
|
1999
2429
|
Promise.all(
|
|
2000
2430
|
enabledCompatible.map(async (agent) => {
|
|
2001
2431
|
await agent.activateAgent(state, chainId);
|
|
2432
|
+
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
2002
2433
|
})
|
|
2003
2434
|
).catch((error) => {
|
|
2004
2435
|
console.error("activateAgent background init failed:", error);
|
|
@@ -2813,22 +3244,22 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
2813
3244
|
const legacyKeys = [];
|
|
2814
3245
|
try {
|
|
2815
3246
|
for (let i = 0; i < store.length; i++) {
|
|
2816
|
-
const
|
|
2817
|
-
if (
|
|
3247
|
+
const key2 = store.key(i);
|
|
3248
|
+
if (key2?.startsWith(prefix)) legacyKeys.push(key2);
|
|
2818
3249
|
}
|
|
2819
3250
|
} catch {
|
|
2820
3251
|
return null;
|
|
2821
3252
|
}
|
|
2822
3253
|
let adopted = null;
|
|
2823
|
-
for (const
|
|
3254
|
+
for (const key2 of legacyKeys) {
|
|
2824
3255
|
if (!adopted) {
|
|
2825
3256
|
try {
|
|
2826
|
-
adopted = JSON.parse(store.getItem(
|
|
3257
|
+
adopted = JSON.parse(store.getItem(key2) ?? "");
|
|
2827
3258
|
} catch {
|
|
2828
3259
|
}
|
|
2829
3260
|
}
|
|
2830
3261
|
try {
|
|
2831
|
-
store.removeItem(
|
|
3262
|
+
store.removeItem(key2);
|
|
2832
3263
|
} catch {
|
|
2833
3264
|
}
|
|
2834
3265
|
}
|
|
@@ -2836,11 +3267,11 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
2836
3267
|
};
|
|
2837
3268
|
var readSiwxSession = (address, chainId) => {
|
|
2838
3269
|
if (typeof window === "undefined") return null;
|
|
2839
|
-
const
|
|
3270
|
+
const key2 = buildKey2(address);
|
|
2840
3271
|
const store = storage2();
|
|
2841
3272
|
let raw = null;
|
|
2842
3273
|
try {
|
|
2843
|
-
raw = store?.getItem(
|
|
3274
|
+
raw = store?.getItem(key2) ?? null;
|
|
2844
3275
|
} catch {
|
|
2845
3276
|
raw = null;
|
|
2846
3277
|
}
|
|
@@ -2848,9 +3279,9 @@ var readSiwxSession = (address, chainId) => {
|
|
|
2848
3279
|
try {
|
|
2849
3280
|
return JSON.parse(raw);
|
|
2850
3281
|
} catch {
|
|
2851
|
-
memorySiwxSessions.delete(
|
|
3282
|
+
memorySiwxSessions.delete(key2);
|
|
2852
3283
|
try {
|
|
2853
|
-
store?.removeItem(
|
|
3284
|
+
store?.removeItem(key2);
|
|
2854
3285
|
} catch {
|
|
2855
3286
|
}
|
|
2856
3287
|
return null;
|
|
@@ -2861,24 +3292,24 @@ var readSiwxSession = (address, chainId) => {
|
|
|
2861
3292
|
writeSiwxSession(address, chainId, legacy);
|
|
2862
3293
|
return legacy;
|
|
2863
3294
|
}
|
|
2864
|
-
return memorySiwxSessions.get(
|
|
3295
|
+
return memorySiwxSessions.get(key2) ?? null;
|
|
2865
3296
|
};
|
|
2866
3297
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
2867
3298
|
if (typeof window === "undefined") return;
|
|
2868
|
-
const
|
|
2869
|
-
memorySiwxSessions.set(
|
|
3299
|
+
const key2 = buildKey2(address);
|
|
3300
|
+
memorySiwxSessions.set(key2, session);
|
|
2870
3301
|
const store = storage2();
|
|
2871
3302
|
try {
|
|
2872
|
-
store?.setItem(
|
|
3303
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
2873
3304
|
} catch {
|
|
2874
3305
|
}
|
|
2875
3306
|
};
|
|
2876
3307
|
var clearSiwxSession = (address, _chainId) => {
|
|
2877
|
-
const
|
|
2878
|
-
memorySiwxSessions.delete(
|
|
3308
|
+
const key2 = buildKey2(address);
|
|
3309
|
+
memorySiwxSessions.delete(key2);
|
|
2879
3310
|
const store = storage2();
|
|
2880
3311
|
try {
|
|
2881
|
-
store?.removeItem(
|
|
3312
|
+
store?.removeItem(key2);
|
|
2882
3313
|
} catch {
|
|
2883
3314
|
}
|
|
2884
3315
|
readLegacySiwxSession(store, address);
|
|
@@ -3007,5 +3438,6 @@ export {
|
|
|
3007
3438
|
NotConnectedError,
|
|
3008
3439
|
OwneyError,
|
|
3009
3440
|
OwneySDK,
|
|
3010
|
-
createOwneySIWX
|
|
3441
|
+
createOwneySIWX,
|
|
3442
|
+
setOwneyDebug
|
|
3011
3443
|
};
|