@owney/sdk 0.6.9-beta.8 → 0.7.0-beta.1
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 +1 -1
- package/dist/index.cjs +255 -1362
- package/dist/index.d.cts +3 -138
- package/dist/index.d.ts +3 -138
- package/dist/index.js +226 -679
- package/package.json +3 -5
- package/dist/chunk-PWIKIQOX.js +0 -140
- package/dist/surfliquid.agent-VNGQUSXN.js +0 -473
package/dist/index.js
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AgentChainIncompatibleError,
|
|
3
|
-
AgentNotFoundError,
|
|
4
|
-
ERC20_ALLOWANCE_ABI,
|
|
5
|
-
MAX_UINT256,
|
|
6
|
-
NotConnectedError,
|
|
7
|
-
OwneyError,
|
|
8
|
-
PERMIT2_ADDRESS,
|
|
9
|
-
buildPermitTransferFromTypedData,
|
|
10
|
-
randomPermit2Nonce,
|
|
11
|
-
readErc20Balance,
|
|
12
|
-
readPermit2Allowance
|
|
13
|
-
} from "./chunk-PWIKIQOX.js";
|
|
14
|
-
|
|
15
1
|
// src/agents/zyfai/zyfai.agent.ts
|
|
16
2
|
import { ZyfaiSDK } from "@zyfai/sdk";
|
|
17
3
|
import { createPublicClient, http } from "viem";
|
|
@@ -70,6 +56,55 @@ var SupportedAssets = [
|
|
|
70
56
|
}
|
|
71
57
|
];
|
|
72
58
|
|
|
59
|
+
// src/errors.ts
|
|
60
|
+
var OwneyError = class extends Error {
|
|
61
|
+
code;
|
|
62
|
+
details;
|
|
63
|
+
agentId;
|
|
64
|
+
constructor(code, message, details, agentId) {
|
|
65
|
+
const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
|
|
66
|
+
super(`${prefix} ${message}`);
|
|
67
|
+
this.name = "OwneyError";
|
|
68
|
+
this.code = code;
|
|
69
|
+
this.details = details;
|
|
70
|
+
this.agentId = agentId;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var AgentNotFoundError = class extends OwneyError {
|
|
74
|
+
constructor(agentId, available) {
|
|
75
|
+
super(
|
|
76
|
+
"AGENT_NOT_FOUND",
|
|
77
|
+
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
78
|
+
{ agentId, available },
|
|
79
|
+
agentId
|
|
80
|
+
);
|
|
81
|
+
this.name = "AgentNotFoundError";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var NotConnectedError = class extends OwneyError {
|
|
85
|
+
constructor() {
|
|
86
|
+
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
87
|
+
this.name = "NotConnectedError";
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var AgentChainIncompatibleError = class extends OwneyError {
|
|
91
|
+
incompatibleAgents;
|
|
92
|
+
connectedChainId;
|
|
93
|
+
constructor(incompatibleAgents, connectedChainId) {
|
|
94
|
+
const details = incompatibleAgents.map(
|
|
95
|
+
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
96
|
+
).join("; ");
|
|
97
|
+
super(
|
|
98
|
+
"AGENT_CHAIN_INCOMPATIBLE",
|
|
99
|
+
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
100
|
+
{ incompatibleAgents, connectedChainId }
|
|
101
|
+
);
|
|
102
|
+
this.name = "AgentChainIncompatibleError";
|
|
103
|
+
this.incompatibleAgents = incompatibleAgents;
|
|
104
|
+
this.connectedChainId = connectedChainId;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
73
108
|
// src/lib/utils.ts
|
|
74
109
|
var isValidChainId = (chainId) => {
|
|
75
110
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
|
|
@@ -81,12 +116,6 @@ var isValidChainId = (chainId) => {
|
|
|
81
116
|
}
|
|
82
117
|
return chainId;
|
|
83
118
|
};
|
|
84
|
-
function isSameAsset(tokenSymbol, asset) {
|
|
85
|
-
const token = tokenSymbol.toLowerCase();
|
|
86
|
-
const target = asset.toLowerCase();
|
|
87
|
-
if (token === target) return true;
|
|
88
|
-
return target === "weth" && token === "eth" || target === "eth" && token === "weth";
|
|
89
|
-
}
|
|
90
119
|
function hexToDecimal(hex, decimals = 6) {
|
|
91
120
|
const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
|
|
92
121
|
const parsed = BigInt(normalized);
|
|
@@ -150,46 +179,46 @@ function resolveChainId(chain) {
|
|
|
150
179
|
function mapBalances(raw, _chainId, smartWallet) {
|
|
151
180
|
const portfolio = raw.portfolio;
|
|
152
181
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
153
|
-
const rawPositions = portfolio.positions ?? [];
|
|
154
|
-
const detailByKey = /* @__PURE__ */ new Map();
|
|
155
|
-
for (const p of rawPositions) {
|
|
156
|
-
const cid = resolveChainId(p.chain ?? "");
|
|
157
|
-
const symbol = (p.token_symbol ?? "").toUpperCase();
|
|
158
|
-
if (Number.isFinite(cid) && symbol) detailByKey.set(`${cid}:${symbol}`, p);
|
|
159
|
-
}
|
|
160
182
|
let totalBalance = 0;
|
|
161
183
|
const tokens = [];
|
|
162
|
-
const positions = [];
|
|
163
184
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
|
|
164
185
|
const positionChainId = Number(chainIdKey);
|
|
165
|
-
const chainName = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
|
|
166
186
|
for (const [symbol, { balance, decimals }] of Object.entries(
|
|
167
187
|
tokensBySymbol ?? {}
|
|
168
188
|
)) {
|
|
169
|
-
const amount = hexToDecimal(balance || "0",
|
|
189
|
+
const amount = hexToDecimal(balance || "0", decimals);
|
|
170
190
|
totalBalance += amount;
|
|
171
191
|
tokens.push({
|
|
172
|
-
chain:
|
|
192
|
+
chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
|
|
173
193
|
chainId: positionChainId,
|
|
174
194
|
asset: symbol,
|
|
175
195
|
amount: String(amount)
|
|
176
196
|
});
|
|
177
|
-
const detail = detailByKey.get(`${positionChainId}:${symbol.toUpperCase()}`);
|
|
178
|
-
positions.push({
|
|
179
|
-
chain: chainName,
|
|
180
|
-
protocol: detail?.protocol_name ?? detail?.protocol_id ?? "",
|
|
181
|
-
protocolId: detail?.protocol_id,
|
|
182
|
-
pool: detail?.pool,
|
|
183
|
-
asset: symbol,
|
|
184
|
-
amount: String(amount),
|
|
185
|
-
apy: detail?.pool_apy,
|
|
186
|
-
tvl: detail?.pool_tvl,
|
|
187
|
-
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
188
|
-
// structural read keeps it undefined today and auto-populates later.
|
|
189
|
-
liquidity: detail?.liquidity
|
|
190
|
-
});
|
|
191
197
|
}
|
|
192
198
|
}
|
|
199
|
+
const positions = (portfolio.positions ?? []).map((p) => ({
|
|
200
|
+
chain: p.chain ?? "",
|
|
201
|
+
protocol: p.protocol_name ?? p.protocol_id ?? "",
|
|
202
|
+
protocolId: p.protocol_id,
|
|
203
|
+
pool: p.pool,
|
|
204
|
+
asset: p.token_symbol ?? "",
|
|
205
|
+
// Position amount is hex-encoded base units (like the token balances
|
|
206
|
+
// above). `PositionSlot` doesn't type a `decimals` field, but runtime
|
|
207
|
+
// getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
|
|
208
|
+
// when present, otherwise fall back to the per-symbol lookup so
|
|
209
|
+
// non-6-decimal assets like WETH don't render 1e12 too large.
|
|
210
|
+
amount: String(
|
|
211
|
+
hexToDecimal(
|
|
212
|
+
p.amount || "0",
|
|
213
|
+
tokenDecimals(p.token_symbol, p.decimals)
|
|
214
|
+
)
|
|
215
|
+
),
|
|
216
|
+
apy: p.pool_apy,
|
|
217
|
+
tvl: p.pool_tvl,
|
|
218
|
+
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
219
|
+
// structural read keeps it undefined today and auto-populates later.
|
|
220
|
+
liquidity: p.liquidity
|
|
221
|
+
}));
|
|
193
222
|
return {
|
|
194
223
|
smartWallet,
|
|
195
224
|
totalBalance: String(totalBalance),
|
|
@@ -351,9 +380,7 @@ function mapUserProfile(raw, userAddress) {
|
|
|
351
380
|
chains: raw.chains || [],
|
|
352
381
|
strategy: raw.strategy,
|
|
353
382
|
hasActiveSessionKey: raw.hasActiveSessionKey || false,
|
|
354
|
-
protocols: raw.protocols || []
|
|
355
|
-
splitting: raw.splitting,
|
|
356
|
-
minSplits: raw.minSplits
|
|
383
|
+
protocols: raw.protocols || []
|
|
357
384
|
};
|
|
358
385
|
}
|
|
359
386
|
function mapApyByStrategy(raw) {
|
|
@@ -500,23 +527,23 @@ var readLegacySession = (store, address) => {
|
|
|
500
527
|
const legacyKeys = [];
|
|
501
528
|
try {
|
|
502
529
|
for (let i = 0; i < store.length; i++) {
|
|
503
|
-
const
|
|
504
|
-
if (
|
|
530
|
+
const key = store.key(i);
|
|
531
|
+
if (key?.startsWith(prefix)) legacyKeys.push(key);
|
|
505
532
|
}
|
|
506
533
|
} catch {
|
|
507
534
|
return null;
|
|
508
535
|
}
|
|
509
536
|
let adopted = null;
|
|
510
|
-
for (const
|
|
537
|
+
for (const key of legacyKeys) {
|
|
511
538
|
if (!adopted) {
|
|
512
539
|
try {
|
|
513
|
-
const parsed = JSON.parse(store.getItem(
|
|
540
|
+
const parsed = JSON.parse(store.getItem(key) ?? "");
|
|
514
541
|
if (isFreshSession(parsed)) adopted = parsed;
|
|
515
542
|
} catch {
|
|
516
543
|
}
|
|
517
544
|
}
|
|
518
545
|
try {
|
|
519
|
-
store.removeItem(
|
|
546
|
+
store.removeItem(key);
|
|
520
547
|
} catch {
|
|
521
548
|
}
|
|
522
549
|
}
|
|
@@ -524,11 +551,11 @@ var readLegacySession = (store, address) => {
|
|
|
524
551
|
};
|
|
525
552
|
var readSession = (address, _chainId) => {
|
|
526
553
|
if (typeof window === "undefined") return null;
|
|
527
|
-
const
|
|
554
|
+
const key = buildKey(address);
|
|
528
555
|
const store = storage();
|
|
529
556
|
let raw = null;
|
|
530
557
|
try {
|
|
531
|
-
raw = store?.getItem(
|
|
558
|
+
raw = store?.getItem(key) ?? null;
|
|
532
559
|
} catch {
|
|
533
560
|
raw = null;
|
|
534
561
|
}
|
|
@@ -538,9 +565,9 @@ var readSession = (address, _chainId) => {
|
|
|
538
565
|
if (isFreshSession(parsed)) return parsed;
|
|
539
566
|
} catch {
|
|
540
567
|
}
|
|
541
|
-
memorySessions.delete(
|
|
568
|
+
memorySessions.delete(key);
|
|
542
569
|
try {
|
|
543
|
-
store?.removeItem(
|
|
570
|
+
store?.removeItem(key);
|
|
544
571
|
} catch {
|
|
545
572
|
}
|
|
546
573
|
return null;
|
|
@@ -550,27 +577,27 @@ var readSession = (address, _chainId) => {
|
|
|
550
577
|
writeSession(address, _chainId, legacy);
|
|
551
578
|
return legacy;
|
|
552
579
|
}
|
|
553
|
-
const cached = memorySessions.get(
|
|
580
|
+
const cached = memorySessions.get(key);
|
|
554
581
|
if (isFreshSession(cached)) return cached;
|
|
555
|
-
if (cached) memorySessions.delete(
|
|
582
|
+
if (cached) memorySessions.delete(key);
|
|
556
583
|
return null;
|
|
557
584
|
};
|
|
558
585
|
var writeSession = (address, _chainId, session) => {
|
|
559
586
|
if (typeof window === "undefined") return;
|
|
560
|
-
const
|
|
561
|
-
memorySessions.set(
|
|
587
|
+
const key = buildKey(address);
|
|
588
|
+
memorySessions.set(key, session);
|
|
562
589
|
const store = storage();
|
|
563
590
|
try {
|
|
564
|
-
store?.setItem(
|
|
591
|
+
store?.setItem(key, JSON.stringify(session));
|
|
565
592
|
} catch {
|
|
566
593
|
}
|
|
567
594
|
};
|
|
568
595
|
var clearSession = (address, _chainId) => {
|
|
569
|
-
const
|
|
570
|
-
memorySessions.delete(
|
|
596
|
+
const key = buildKey(address);
|
|
597
|
+
memorySessions.delete(key);
|
|
571
598
|
const store = storage();
|
|
572
599
|
try {
|
|
573
|
-
store?.removeItem(
|
|
600
|
+
store?.removeItem(key);
|
|
574
601
|
} catch {
|
|
575
602
|
}
|
|
576
603
|
readLegacySession(store, address);
|
|
@@ -601,149 +628,6 @@ function buildProtocolMergeUpdate(available, current, asset) {
|
|
|
601
628
|
};
|
|
602
629
|
}
|
|
603
630
|
|
|
604
|
-
// src/agents/zyfai/splitting.ts
|
|
605
|
-
var MAX_MIN_SPLITS = 4;
|
|
606
|
-
var MIN_FORCE_SPLITS = 2;
|
|
607
|
-
var AUTOMATIC_MIN_SPLITS = 1;
|
|
608
|
-
function toZyfaiSplitting(policy) {
|
|
609
|
-
switch (policy.splittingMode) {
|
|
610
|
-
case "none":
|
|
611
|
-
return { splitting: false };
|
|
612
|
-
case "automatic":
|
|
613
|
-
return { splitting: true, minSplits: AUTOMATIC_MIN_SPLITS };
|
|
614
|
-
case "force":
|
|
615
|
-
return {
|
|
616
|
-
splitting: true,
|
|
617
|
-
minSplits: clampForceSplits(policy.minSplits)
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
function clampForceSplits(minSplits) {
|
|
622
|
-
if (typeof minSplits !== "number" || !Number.isFinite(minSplits)) {
|
|
623
|
-
return MIN_FORCE_SPLITS;
|
|
624
|
-
}
|
|
625
|
-
const rounded = Math.round(minSplits);
|
|
626
|
-
if (rounded < MIN_FORCE_SPLITS) return MIN_FORCE_SPLITS;
|
|
627
|
-
if (rounded > MAX_MIN_SPLITS) return MAX_MIN_SPLITS;
|
|
628
|
-
return rounded;
|
|
629
|
-
}
|
|
630
|
-
function splittingNeedsUpdate(current, desired) {
|
|
631
|
-
if (!current) return true;
|
|
632
|
-
if (current.splitting !== desired.splitting) return true;
|
|
633
|
-
if (desired.minSplits === void 0) return false;
|
|
634
|
-
return current.minSplits !== desired.minSplits;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// src/agents/zyfai/pool-policy.ts
|
|
638
|
-
function toZyfaiCustomizations(policy, availableByProtocol, chainsByProtocol) {
|
|
639
|
-
const selectionByKey = /* @__PURE__ */ new Map();
|
|
640
|
-
for (const selection of policy.selections) {
|
|
641
|
-
selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
|
|
642
|
-
}
|
|
643
|
-
const customizations = [];
|
|
644
|
-
for (const [protocolId, chainIds] of Object.entries(chainsByProtocol)) {
|
|
645
|
-
const available = availableByProtocol.get(protocolId);
|
|
646
|
-
if (available === void 0) {
|
|
647
|
-
continue;
|
|
648
|
-
}
|
|
649
|
-
const availableSet = new Set(available);
|
|
650
|
-
for (const chainId of chainIds) {
|
|
651
|
-
const stored = selectionByKey.get(key(protocolId, chainId));
|
|
652
|
-
if (stored === void 0) {
|
|
653
|
-
customizations.push({
|
|
654
|
-
protocolId,
|
|
655
|
-
chainId,
|
|
656
|
-
pools: [],
|
|
657
|
-
autoselect: true
|
|
658
|
-
});
|
|
659
|
-
continue;
|
|
660
|
-
}
|
|
661
|
-
const pools = stored.pools.filter((pool) => availableSet.has(pool));
|
|
662
|
-
customizations.push({
|
|
663
|
-
protocolId,
|
|
664
|
-
chainId,
|
|
665
|
-
pools,
|
|
666
|
-
autoselect: false
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
return customizations;
|
|
671
|
-
}
|
|
672
|
-
function deriveProtocols(policy, universe, chainsByProtocol) {
|
|
673
|
-
if (policy.selections.length === 0) {
|
|
674
|
-
return null;
|
|
675
|
-
}
|
|
676
|
-
const selectionByKey = /* @__PURE__ */ new Map();
|
|
677
|
-
for (const selection of policy.selections) {
|
|
678
|
-
selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
|
|
679
|
-
}
|
|
680
|
-
return universe.filter((protocolId) => {
|
|
681
|
-
const chainIds = chainsByProtocol[protocolId];
|
|
682
|
-
if (chainIds === void 0 || chainIds.length === 0) {
|
|
683
|
-
return true;
|
|
684
|
-
}
|
|
685
|
-
const fullyDisabled = chainIds.every((chainId) => {
|
|
686
|
-
const stored = selectionByKey.get(key(protocolId, chainId));
|
|
687
|
-
return stored !== void 0 && stored.pools.length === 0;
|
|
688
|
-
});
|
|
689
|
-
return !fullyDisabled;
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
function key(protocolId, chainId) {
|
|
693
|
-
return `${protocolId}:${chainId}`;
|
|
694
|
-
}
|
|
695
|
-
function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
696
|
-
const allowed = new Set(dashboardChainIds);
|
|
697
|
-
const result = {};
|
|
698
|
-
for (const protocol of protocols) {
|
|
699
|
-
result[protocol.id] = (protocol.chains ?? []).filter(
|
|
700
|
-
(chainId) => allowed.has(chainId)
|
|
701
|
-
);
|
|
702
|
-
}
|
|
703
|
-
return result;
|
|
704
|
-
}
|
|
705
|
-
function flattenAvailablePools(raw) {
|
|
706
|
-
const byChain = raw ?? {};
|
|
707
|
-
const names = [];
|
|
708
|
-
for (const byToken of Object.values(byChain ?? {})) {
|
|
709
|
-
for (const entry of Object.values(byToken ?? {})) {
|
|
710
|
-
for (const pool of entry?.pools ?? []) {
|
|
711
|
-
if (pool?.name) names.push(pool.name);
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
return names;
|
|
716
|
-
}
|
|
717
|
-
function protocolListsEqual(a, b) {
|
|
718
|
-
const left = [...a ?? []].sort();
|
|
719
|
-
const right = [...b ?? []].sort();
|
|
720
|
-
if (left.length !== right.length) return false;
|
|
721
|
-
return left.every((value, index) => value === right[index]);
|
|
722
|
-
}
|
|
723
|
-
function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect) {
|
|
724
|
-
if (!current) return true;
|
|
725
|
-
if (current.autoSelectProtocols !== desiredAutoSelect) return true;
|
|
726
|
-
if (desiredProtocols === null) return false;
|
|
727
|
-
return !protocolListsEqual(current.protocols, desiredProtocols);
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// src/lib/debug.ts
|
|
731
|
-
var configuredDebug = false;
|
|
732
|
-
function setOwneyDebug(enabled) {
|
|
733
|
-
configuredDebug = enabled;
|
|
734
|
-
}
|
|
735
|
-
function isOwneyDebug() {
|
|
736
|
-
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
737
|
-
}
|
|
738
|
-
function debugLog(scope, message, data) {
|
|
739
|
-
if (!isOwneyDebug()) return;
|
|
740
|
-
if (data === void 0) {
|
|
741
|
-
console.log(`[${scope}] ${message}`);
|
|
742
|
-
} else {
|
|
743
|
-
console.log(`[${scope}] ${message}`, data);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
631
|
// src/agents/zyfai/zyfai.agent.ts
|
|
748
632
|
var DEFAULT_ZYFAI_RPC_URLS = {
|
|
749
633
|
8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
@@ -756,12 +640,6 @@ var WETH_ADDRESS_BY_CHAIN = {
|
|
|
756
640
|
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
757
641
|
};
|
|
758
642
|
var VIEM_CHAIN = { 8453: base, 42161: arbitrum, 1: mainnet };
|
|
759
|
-
var SPLITTING_ASSETS = ["USDC", "WETH"];
|
|
760
|
-
var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
|
|
761
|
-
function isAlreadyActiveSessionKeyError(error) {
|
|
762
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
763
|
-
return /already has an active session key/i.test(message);
|
|
764
|
-
}
|
|
765
643
|
var ZyfaiAgent = class {
|
|
766
644
|
id = "zyfai";
|
|
767
645
|
supportedChainIds = [8453, 42161, 1];
|
|
@@ -988,71 +866,21 @@ var ZyfaiAgent = class {
|
|
|
988
866
|
return await this.sdk.connectAccount(provider, chainId);
|
|
989
867
|
}
|
|
990
868
|
}
|
|
991
|
-
/** Full connect: SIWE auth + Safe deploy check + session key. */
|
|
992
|
-
async connectFull(state, chainId) {
|
|
993
|
-
await this.connectAuth(state, chainId);
|
|
994
|
-
const validChainId = isValidChainId(chainId);
|
|
995
|
-
const address = this.getAddress();
|
|
996
|
-
const wallet = await this.sdk.getSmartWalletAddress(address, validChainId);
|
|
997
|
-
this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
|
|
998
|
-
if (!wallet.isDeployed) {
|
|
999
|
-
await this.sdk.deploySafe(address, validChainId, "aggressive", false);
|
|
1000
|
-
try {
|
|
1001
|
-
if (validChainId === 42161) {
|
|
1002
|
-
await this.sdk.updateUserProfile({
|
|
1003
|
-
asset: "WETH",
|
|
1004
|
-
chains: [validChainId],
|
|
1005
|
-
protocols: ["c24a33fa-4a87-4376-b756-93bd3957d3ea"],
|
|
1006
|
-
// AAVE protocol ID
|
|
1007
|
-
autoSelectProtocols: false
|
|
1008
|
-
});
|
|
1009
|
-
}
|
|
1010
|
-
} catch (err) {
|
|
1011
|
-
console.warn(
|
|
1012
|
-
"Failed to configure AAVE protocol after Safe deployment:",
|
|
1013
|
-
err instanceof Error ? err.message : String(err)
|
|
1014
|
-
);
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
await this.ensureSessionKey(state, address, validChainId);
|
|
1018
|
-
}
|
|
1019
869
|
/**
|
|
1020
|
-
*
|
|
1021
|
-
*
|
|
1022
|
-
*
|
|
1023
|
-
*
|
|
1024
|
-
*
|
|
1025
|
-
*
|
|
1026
|
-
* to (re)create a key per chain — that 400s and blocks the deposit. We only
|
|
1027
|
-
* create when the user has none.
|
|
1028
|
-
*
|
|
1029
|
-
* The one wrapper-side hardening that IS needed: a chain/account switch can
|
|
1030
|
-
* fire the provider's `accountsChanged` → `disconnectAccount`, tearing down
|
|
1031
|
-
* the zyfai connection (walletClient + authenticatedUserId both null) while
|
|
1032
|
-
* our own `connectAuth` guard still believes this chain is connected and
|
|
1033
|
-
* early-returns without reconnecting. `createSessionKey` then fails with
|
|
1034
|
-
* "No account connected". Detect that and reconnect first.
|
|
1035
|
-
*
|
|
1036
|
-
* NOTE: whether a globally-active session key actually authorizes activity on
|
|
1037
|
-
* a freshly-deployed Safe on a new chain is a backend/zyf question — the
|
|
1038
|
-
* wrapper cannot force per-chain activation given the global 400 guard.
|
|
1039
|
-
* See [[zyfai-per-chain-session-key]].
|
|
870
|
+
* Reconnect if the Zyfai connection was torn down behind our back: a
|
|
871
|
+
* chain/account switch fires the provider's `accountsChanged` →
|
|
872
|
+
* `disconnectAccount` (nulls walletClient + authenticatedUserId) while our
|
|
873
|
+
* own `connectAuth` guard still believes this chain is connected and
|
|
874
|
+
* early-returns. Without this, authenticated calls (depositFunds,
|
|
875
|
+
* withdrawFunds) fail with "No account connected".
|
|
1040
876
|
*/
|
|
1041
|
-
async
|
|
877
|
+
async ensureConnected(state, chainId) {
|
|
878
|
+
await this.connectAuth(state, chainId);
|
|
1042
879
|
const internals = this.sdk;
|
|
1043
880
|
if (!internals.walletClient || internals.authenticatedUserId === null) {
|
|
1044
881
|
this.connectedChainId = null;
|
|
1045
882
|
await this.connectAuth(state, chainId);
|
|
1046
883
|
}
|
|
1047
|
-
const user = await this.sdk.getUserDetails();
|
|
1048
|
-
if (!user.hasActiveSessionKey) {
|
|
1049
|
-
try {
|
|
1050
|
-
await this.sdk.createSessionKey(address, chainId);
|
|
1051
|
-
} catch (error) {
|
|
1052
|
-
if (!isAlreadyActiveSessionKeyError(error)) throw error;
|
|
1053
|
-
internals.hasActiveSessionKey = true;
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
884
|
}
|
|
1057
885
|
/**
|
|
1058
886
|
* Sync newly-available protocols onto an existing user's profile at wallet
|
|
@@ -1122,211 +950,10 @@ var ZyfaiAgent = class {
|
|
|
1122
950
|
throw error;
|
|
1123
951
|
}
|
|
1124
952
|
}
|
|
1125
|
-
/**
|
|
1126
|
-
* Apply the org's capital splitting policy to this user's Zyfai profile.
|
|
1127
|
-
*
|
|
1128
|
-
* Per ASSET, not per chain: Zyfai stores splitting under
|
|
1129
|
-
* `assetTypeSettings[asset]`, so one call would only configure USDC and leave a
|
|
1130
|
-
* WETH holder's real setting untouched.
|
|
1131
|
-
*
|
|
1132
|
-
* Non-fatal throughout. This is a side effect of connecting, so a Zyfai hiccup
|
|
1133
|
-
* must leave the user connected with their profile unchanged, never fail
|
|
1134
|
-
* activation.
|
|
1135
|
-
*/
|
|
1136
|
-
async applyOrgPolicy(_state, _chainId, policy) {
|
|
1137
|
-
debugLog("zyfai:splitting", "applying org policy", {
|
|
1138
|
-
policy,
|
|
1139
|
-
// Per asset because Zyfai stores splitting under assetTypeSettings[asset].
|
|
1140
|
-
assets: SPLITTING_ASSETS
|
|
1141
|
-
});
|
|
1142
|
-
await Promise.all(
|
|
1143
|
-
SPLITTING_ASSETS.map((asset) => this.applySplitting(asset, policy))
|
|
1144
|
-
);
|
|
1145
|
-
if (policy.poolPolicy) {
|
|
1146
|
-
await this.applyPoolPolicy(policy.poolPolicy);
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
async applySplitting(asset, policy) {
|
|
1150
|
-
try {
|
|
1151
|
-
const desired = toZyfaiSplitting(policy);
|
|
1152
|
-
const current = await this.sdk.getUserDetails(asset);
|
|
1153
|
-
const currentSplitting = {
|
|
1154
|
-
splitting: current?.splitting,
|
|
1155
|
-
minSplits: current?.minSplits
|
|
1156
|
-
};
|
|
1157
|
-
const needsUpdate = splittingNeedsUpdate(current, desired);
|
|
1158
|
-
debugLog("zyfai:splitting", `${asset}: comparing`, {
|
|
1159
|
-
mode: policy.splittingMode,
|
|
1160
|
-
current: currentSplitting,
|
|
1161
|
-
desired,
|
|
1162
|
-
// Spelled out because the "why" is the non-obvious part: minSplits is
|
|
1163
|
-
// what separates automatic (1) from force (2-4), not `splitting`.
|
|
1164
|
-
decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
|
|
1165
|
-
});
|
|
1166
|
-
if (!needsUpdate) return;
|
|
1167
|
-
const result = await this.sdk.updateUserProfile({ asset, ...desired });
|
|
1168
|
-
debugLog("zyfai:splitting", `${asset}: wrote updateUserProfile`, {
|
|
1169
|
-
sent: { asset, ...desired },
|
|
1170
|
-
// updateUserProfile re-reads the profile after PATCHing, so this is
|
|
1171
|
-
// Zyfai's own view of the result rather than an echo of what we sent.
|
|
1172
|
-
zyfaiNowReports: {
|
|
1173
|
-
splitting: result?.splitting,
|
|
1174
|
-
minSplits: result?.minSplits
|
|
1175
|
-
}
|
|
1176
|
-
});
|
|
1177
|
-
} catch (error) {
|
|
1178
|
-
console.warn(
|
|
1179
|
-
`[zyfai] applyOrgPolicy(${asset}) failed (non-fatal):`,
|
|
1180
|
-
error instanceof Error ? error.message : String(error)
|
|
1181
|
-
);
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
/**
|
|
1185
|
-
* Apply the org's protocol/pool policy to this user's Zyfai profile.
|
|
1186
|
-
*
|
|
1187
|
-
* Two Zyfai writes, in a strict order:
|
|
1188
|
-
* 1. `updateUserProfile({ protocols, autoSelectProtocols })` per asset —
|
|
1189
|
-
* the explicit allow-list. Chain-agnostic, mirrors `applySplitting`.
|
|
1190
|
-
* 2. `customizeBatch(...)` once — per (protocol, chain), asset-agnostic
|
|
1191
|
-
* pool narrowing.
|
|
1192
|
-
*
|
|
1193
|
-
* Order matters: constraining a protocol's pools (step 2) while that
|
|
1194
|
-
* protocol is not yet in the allow-list (step 1 not yet applied) lands the
|
|
1195
|
-
* constraint on something the rebalance engine isn't considering.
|
|
1196
|
-
*
|
|
1197
|
-
* Wrapped so a failure anywhere is non-fatal — this runs as a side effect of
|
|
1198
|
-
* connecting, so a Zyfai hiccup must leave the user connected with their
|
|
1199
|
-
* profile unchanged, never fail activation.
|
|
1200
|
-
*/
|
|
1201
|
-
async applyPoolPolicy(policy) {
|
|
1202
|
-
try {
|
|
1203
|
-
const { protocols: universe } = await this.sdk.getAvailableProtocols(
|
|
1204
|
-
this.supportedChainIds[0]
|
|
1205
|
-
);
|
|
1206
|
-
const universeIds = universe.map((p) => p.id);
|
|
1207
|
-
const chainsByProtocol = buildChainsByProtocol(
|
|
1208
|
-
universe,
|
|
1209
|
-
POOL_POLICY_CHAIN_IDS
|
|
1210
|
-
);
|
|
1211
|
-
debugLog("zyfai:pools", "protocol universe", {
|
|
1212
|
-
universeIds,
|
|
1213
|
-
chainsByProtocol
|
|
1214
|
-
});
|
|
1215
|
-
let strategy;
|
|
1216
|
-
const desiredProtocols = deriveProtocols(
|
|
1217
|
-
policy,
|
|
1218
|
-
universeIds,
|
|
1219
|
-
chainsByProtocol
|
|
1220
|
-
);
|
|
1221
|
-
const desiredAutoSelect = policy.autoApproveProtocols;
|
|
1222
|
-
for (const asset of SPLITTING_ASSETS) {
|
|
1223
|
-
const current = await this.sdk.getUserDetails(asset);
|
|
1224
|
-
if (strategy === void 0 && (current.strategy === "conservative" || current.strategy === "aggressive")) {
|
|
1225
|
-
strategy = current.strategy;
|
|
1226
|
-
}
|
|
1227
|
-
const needsUpdate = protocolsPolicyNeedsUpdate(
|
|
1228
|
-
current,
|
|
1229
|
-
desiredProtocols,
|
|
1230
|
-
desiredAutoSelect
|
|
1231
|
-
);
|
|
1232
|
-
debugLog("zyfai:pools", `${asset}: comparing protocols`, {
|
|
1233
|
-
current: {
|
|
1234
|
-
protocols: current.protocols,
|
|
1235
|
-
autoSelectProtocols: current.autoSelectProtocols
|
|
1236
|
-
},
|
|
1237
|
-
desired: {
|
|
1238
|
-
protocols: desiredProtocols,
|
|
1239
|
-
autoSelectProtocols: desiredAutoSelect
|
|
1240
|
-
},
|
|
1241
|
-
decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
|
|
1242
|
-
});
|
|
1243
|
-
if (!needsUpdate) continue;
|
|
1244
|
-
const update = {
|
|
1245
|
-
asset,
|
|
1246
|
-
autoSelectProtocols: desiredAutoSelect
|
|
1247
|
-
};
|
|
1248
|
-
if (desiredProtocols !== null) update.protocols = desiredProtocols;
|
|
1249
|
-
await this.sdk.updateUserProfile(update);
|
|
1250
|
-
debugLog("zyfai:pools", `${asset}: wrote protocols update`, update);
|
|
1251
|
-
}
|
|
1252
|
-
const poolResults = await Promise.all(
|
|
1253
|
-
universe.map(async (protocol) => {
|
|
1254
|
-
try {
|
|
1255
|
-
const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1256
|
-
return [protocol.id, flattenAvailablePools(raw)];
|
|
1257
|
-
} catch (error) {
|
|
1258
|
-
console.warn(
|
|
1259
|
-
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
1260
|
-
error instanceof Error ? error.message : String(error)
|
|
1261
|
-
);
|
|
1262
|
-
return null;
|
|
1263
|
-
}
|
|
1264
|
-
})
|
|
1265
|
-
);
|
|
1266
|
-
const availableByProtocol = /* @__PURE__ */ new Map();
|
|
1267
|
-
for (const result of poolResults) {
|
|
1268
|
-
if (result) availableByProtocol.set(result[0], result[1]);
|
|
1269
|
-
}
|
|
1270
|
-
const customizations = toZyfaiCustomizations(
|
|
1271
|
-
policy,
|
|
1272
|
-
availableByProtocol,
|
|
1273
|
-
chainsByProtocol
|
|
1274
|
-
);
|
|
1275
|
-
debugLog("zyfai:pools", "derived customizeBatch payload", {
|
|
1276
|
-
strategy,
|
|
1277
|
-
availableProtocolCount: availableByProtocol.size,
|
|
1278
|
-
customizations
|
|
1279
|
-
});
|
|
1280
|
-
if (customizations.length === 0) {
|
|
1281
|
-
debugLog("zyfai:pools", "skipping customizeBatch \u2014 nothing to send");
|
|
1282
|
-
return;
|
|
1283
|
-
}
|
|
1284
|
-
await this.sdk.customizeBatch(customizations);
|
|
1285
|
-
debugLog("zyfai:pools", "wrote customizeBatch", {
|
|
1286
|
-
count: customizations.length
|
|
1287
|
-
});
|
|
1288
|
-
} catch (error) {
|
|
1289
|
-
console.warn(
|
|
1290
|
-
"[zyfai] applyPoolPolicy failed (non-fatal):",
|
|
1291
|
-
error instanceof Error ? error.message : String(error)
|
|
1292
|
-
);
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
/**
|
|
1296
|
-
* Records the deposit with Zyfai, retrying transient failures.
|
|
1297
|
-
*
|
|
1298
|
-
* This runs AFTER the transfer has already landed on-chain, so it must never
|
|
1299
|
-
* fail the deposit — the funds moved. But it is also the ONLY source of the
|
|
1300
|
-
* "Top up wallet" entry the history is built from: Zyfai auto-deploys the
|
|
1301
|
-
* balance it detects either way, so when this call is lost the user's deposit
|
|
1302
|
-
* never appears in Activity (an earlier withdrawal stays the newest row) and
|
|
1303
|
-
* nothing ever backfills it. Retry, then log loudly enough to be recoverable.
|
|
1304
|
-
*/
|
|
1305
|
-
async logDepositWithRetry(chainId, txHash, amount, tokenAddress) {
|
|
1306
|
-
const ATTEMPTS = 3;
|
|
1307
|
-
const RETRY_DELAY_MS = 1e3;
|
|
1308
|
-
for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
|
|
1309
|
-
try {
|
|
1310
|
-
await (tokenAddress ? this.sdk.logDeposit(chainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(chainId, txHash, amount));
|
|
1311
|
-
return;
|
|
1312
|
-
} catch (logError) {
|
|
1313
|
-
if (attempt === ATTEMPTS) {
|
|
1314
|
-
console.error(
|
|
1315
|
-
"[owney-sdk] Deposit landed on-chain but logDeposit failed \u2014 it will be missing from Zyfai history:",
|
|
1316
|
-
{ txHash, chainId, amount, tokenAddress, error: logError }
|
|
1317
|
-
);
|
|
1318
|
-
return;
|
|
1319
|
-
}
|
|
1320
|
-
await new Promise(
|
|
1321
|
-
(resolve) => setTimeout(resolve, RETRY_DELAY_MS * attempt)
|
|
1322
|
-
);
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
953
|
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
1327
954
|
try {
|
|
1328
|
-
await this.connectFull(state, chainId);
|
|
1329
955
|
const validChainId = isValidChainId(chainId);
|
|
956
|
+
await this.ensureConnected(state, validChainId);
|
|
1330
957
|
if (depositCallback) {
|
|
1331
958
|
const wallet = await this.sdk.getSmartWalletAddress(
|
|
1332
959
|
this.getAddress(),
|
|
@@ -1338,19 +965,22 @@ var ZyfaiAgent = class {
|
|
|
1338
965
|
amount
|
|
1339
966
|
);
|
|
1340
967
|
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
1341
|
-
|
|
1342
|
-
validChainId,
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
968
|
+
try {
|
|
969
|
+
await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
|
|
970
|
+
} catch (logError) {
|
|
971
|
+
console.warn(
|
|
972
|
+
"[owney-sdk] Deposit landed on-chain but logDeposit failed (non-fatal):",
|
|
973
|
+
logError
|
|
974
|
+
);
|
|
975
|
+
}
|
|
1347
976
|
return { txHash, smartWallet: wallet.address, amount };
|
|
1348
977
|
}
|
|
1349
978
|
const raw = await this.sdk.depositFunds(
|
|
1350
979
|
this.getAddress(),
|
|
1351
980
|
validChainId,
|
|
1352
981
|
amount,
|
|
1353
|
-
asset
|
|
982
|
+
asset,
|
|
983
|
+
"aggressive"
|
|
1354
984
|
);
|
|
1355
985
|
return mapDeposit(raw);
|
|
1356
986
|
} catch (error) {
|
|
@@ -1359,8 +989,8 @@ var ZyfaiAgent = class {
|
|
|
1359
989
|
}
|
|
1360
990
|
// --- IAgent: Fund operations ---
|
|
1361
991
|
async withdraw(state, chainId, token, amount) {
|
|
1362
|
-
await this.connectFull(state, chainId);
|
|
1363
992
|
const validChainId = isValidChainId(chainId);
|
|
993
|
+
await this.ensureConnected(state, validChainId);
|
|
1364
994
|
const raw = await this.sdk.withdrawFunds(
|
|
1365
995
|
this.getAddress(),
|
|
1366
996
|
validChainId,
|
|
@@ -1439,19 +1069,7 @@ var ZyfaiAgent = class {
|
|
|
1439
1069
|
}
|
|
1440
1070
|
async getUserProfile(state, chainId) {
|
|
1441
1071
|
await this.connectAuth(state, chainId);
|
|
1442
|
-
const validChainId = isValidChainId(chainId);
|
|
1443
1072
|
const raw = await this.sdk.getUserDetails();
|
|
1444
|
-
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
1445
|
-
asset: "USDC (default \u2014 no asset passed)",
|
|
1446
|
-
splitting: raw.splitting,
|
|
1447
|
-
minSplits: raw.minSplits,
|
|
1448
|
-
strategy: raw.strategy,
|
|
1449
|
-
chains: raw.chains,
|
|
1450
|
-
protocolCount: raw.protocols?.length,
|
|
1451
|
-
hasActiveSessionKey: raw.hasActiveSessionKey,
|
|
1452
|
-
smartWallet: raw.smartWallet
|
|
1453
|
-
});
|
|
1454
|
-
await this.ensureSessionKey(state, this.getAddress(), validChainId);
|
|
1455
1073
|
return mapUserProfile(raw, this.connectedAddress);
|
|
1456
1074
|
}
|
|
1457
1075
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
@@ -1484,40 +1102,6 @@ var ZyfaiAgent = class {
|
|
|
1484
1102
|
|
|
1485
1103
|
// src/lib/routing-api.ts
|
|
1486
1104
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1487
|
-
async function fetchOrgAgentConfig(apiKey) {
|
|
1488
|
-
const url = `${ROUTING_API_BASE_URL}/api/v1/agent/org-config`;
|
|
1489
|
-
try {
|
|
1490
|
-
const res = await fetch(url, {
|
|
1491
|
-
method: "GET",
|
|
1492
|
-
headers: {
|
|
1493
|
-
"Content-Type": "application/json",
|
|
1494
|
-
"x-owney-api-key": `${apiKey}`
|
|
1495
|
-
}
|
|
1496
|
-
});
|
|
1497
|
-
if (!res.ok) {
|
|
1498
|
-
if (res.status !== 404) {
|
|
1499
|
-
console.warn(
|
|
1500
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
1501
|
-
);
|
|
1502
|
-
}
|
|
1503
|
-
return null;
|
|
1504
|
-
}
|
|
1505
|
-
const json = await res.json();
|
|
1506
|
-
const policy = json.success ? json.data ?? null : null;
|
|
1507
|
-
debugLog(
|
|
1508
|
-
"owney-sdk",
|
|
1509
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
1510
|
-
policy ?? void 0
|
|
1511
|
-
);
|
|
1512
|
-
return policy;
|
|
1513
|
-
} catch (error) {
|
|
1514
|
-
console.warn(
|
|
1515
|
-
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
1516
|
-
error instanceof Error ? error.message : String(error)
|
|
1517
|
-
);
|
|
1518
|
-
return null;
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
1105
|
async function fetchAgentKeys(apiKey) {
|
|
1522
1106
|
const url = `${ROUTING_API_BASE_URL}/api/v1/agent/keys`;
|
|
1523
1107
|
const res = await fetch(url, {
|
|
@@ -1582,10 +1166,11 @@ async function withFailureReporting(apiKey, agentType, fn) {
|
|
|
1582
1166
|
// src/lib/helpers/withdraw-helper.ts
|
|
1583
1167
|
import { parseUnits } from "viem";
|
|
1584
1168
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
1169
|
+
const target = asset.toUpperCase();
|
|
1585
1170
|
return agents.map((agent) => {
|
|
1586
1171
|
const agentBalance = aggregated[agent.id];
|
|
1587
1172
|
const tokenBalance = agentBalance?.tokens.find(
|
|
1588
|
-
(t) => t.chainId === chainId &&
|
|
1173
|
+
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
1589
1174
|
);
|
|
1590
1175
|
if (!tokenBalance) return { agent, balance: 0n };
|
|
1591
1176
|
return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
|
|
@@ -1855,6 +1440,84 @@ async function getSponsorRelayerAddress(input) {
|
|
|
1855
1440
|
return parsed.data.relayer;
|
|
1856
1441
|
}
|
|
1857
1442
|
|
|
1443
|
+
// src/lib/permit2.ts
|
|
1444
|
+
import { bytesToHex as bytesToHex2 } from "viem";
|
|
1445
|
+
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
1446
|
+
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
1447
|
+
var ERC20_ALLOWANCE_ABI = [
|
|
1448
|
+
{
|
|
1449
|
+
type: "function",
|
|
1450
|
+
name: "allowance",
|
|
1451
|
+
stateMutability: "view",
|
|
1452
|
+
inputs: [
|
|
1453
|
+
{ name: "owner", type: "address" },
|
|
1454
|
+
{ name: "spender", type: "address" }
|
|
1455
|
+
],
|
|
1456
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1457
|
+
},
|
|
1458
|
+
{
|
|
1459
|
+
type: "function",
|
|
1460
|
+
name: "approve",
|
|
1461
|
+
stateMutability: "nonpayable",
|
|
1462
|
+
inputs: [
|
|
1463
|
+
{ name: "spender", type: "address" },
|
|
1464
|
+
{ name: "amount", type: "uint256" }
|
|
1465
|
+
],
|
|
1466
|
+
outputs: [{ name: "", type: "bool" }]
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
type: "function",
|
|
1470
|
+
name: "balanceOf",
|
|
1471
|
+
stateMutability: "view",
|
|
1472
|
+
inputs: [{ name: "account", type: "address" }],
|
|
1473
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1474
|
+
}
|
|
1475
|
+
];
|
|
1476
|
+
function buildPermitTransferFromTypedData(input) {
|
|
1477
|
+
return {
|
|
1478
|
+
domain: {
|
|
1479
|
+
name: "Permit2",
|
|
1480
|
+
chainId: input.chainId,
|
|
1481
|
+
verifyingContract: PERMIT2_ADDRESS
|
|
1482
|
+
},
|
|
1483
|
+
types: {
|
|
1484
|
+
PermitTransferFrom: [
|
|
1485
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
1486
|
+
{ name: "spender", type: "address" },
|
|
1487
|
+
{ name: "nonce", type: "uint256" },
|
|
1488
|
+
{ name: "deadline", type: "uint256" }
|
|
1489
|
+
],
|
|
1490
|
+
TokenPermissions: [
|
|
1491
|
+
{ name: "token", type: "address" },
|
|
1492
|
+
{ name: "amount", type: "uint256" }
|
|
1493
|
+
]
|
|
1494
|
+
},
|
|
1495
|
+
primaryType: "PermitTransferFrom",
|
|
1496
|
+
message: input.message
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
function randomPermit2Nonce() {
|
|
1500
|
+
const bytes = new Uint8Array(32);
|
|
1501
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
1502
|
+
return BigInt(bytesToHex2(bytes));
|
|
1503
|
+
}
|
|
1504
|
+
async function readPermit2Allowance(publicClient, token, owner) {
|
|
1505
|
+
return publicClient.readContract({
|
|
1506
|
+
address: token,
|
|
1507
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
1508
|
+
functionName: "allowance",
|
|
1509
|
+
args: [owner, PERMIT2_ADDRESS]
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
1513
|
+
return publicClient.readContract({
|
|
1514
|
+
address: token,
|
|
1515
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
1516
|
+
functionName: "balanceOf",
|
|
1517
|
+
args: [owner]
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1858
1521
|
// src/lib/sponsored-deposit.ts
|
|
1859
1522
|
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
1860
1523
|
function makeSponsoredDepositCallback(deps) {
|
|
@@ -2037,7 +1700,6 @@ var VIEM_CHAIN2 = {
|
|
|
2037
1700
|
42161: arbitrum2,
|
|
2038
1701
|
1: mainnet2
|
|
2039
1702
|
};
|
|
2040
|
-
var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
|
|
2041
1703
|
var SPONSORED_WETH_BY_CHAIN = {
|
|
2042
1704
|
8453: "0x4200000000000000000000000000000000000006",
|
|
2043
1705
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -2057,26 +1719,17 @@ var OwneySDK = class {
|
|
|
2057
1719
|
disabledAgents = /* @__PURE__ */ new Set();
|
|
2058
1720
|
state = null;
|
|
2059
1721
|
apiKey;
|
|
2060
|
-
// The org's agent policy, fetched once per client. Applied to each agent on
|
|
2061
|
-
// activation (ROUT-229). `undefined` means "not fetched yet", `null` means
|
|
2062
|
-
// "fetched, and the partner has no policy" — which is a real answer meaning
|
|
2063
|
-
// leave every user's agent profile alone.
|
|
2064
|
-
orgAgentConfig;
|
|
2065
|
-
orgAgentConfigPromise = null;
|
|
2066
1722
|
zyfaiRpcUrls;
|
|
2067
1723
|
routingApiBaseUrl;
|
|
2068
1724
|
referralSource;
|
|
2069
|
-
surfliquidApiBaseUrl;
|
|
2070
1725
|
cachedSponsoredCallback = null;
|
|
2071
1726
|
cachedWethSponsoredCallback = null;
|
|
2072
1727
|
initializingAgentsPromise = null;
|
|
2073
1728
|
constructor(config) {
|
|
2074
1729
|
this.apiKey = config.apiKey;
|
|
2075
|
-
if (config.debug) setOwneyDebug(true);
|
|
2076
1730
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
2077
1731
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2078
1732
|
this.referralSource = config.referralSource;
|
|
2079
|
-
this.surfliquidApiBaseUrl = config.surfliquidApiBaseUrl;
|
|
2080
1733
|
}
|
|
2081
1734
|
/**
|
|
2082
1735
|
* Establish connection state for all subsequent SDK calls.
|
|
@@ -2235,57 +1888,14 @@ var OwneySDK = class {
|
|
|
2235
1888
|
}
|
|
2236
1889
|
return active;
|
|
2237
1890
|
}
|
|
2238
|
-
/**
|
|
2239
|
-
* Read the org's agent policy once and reuse it. fetchOrgAgentConfig never
|
|
2240
|
-
* throws — it degrades to null — so this cannot break activation.
|
|
2241
|
-
*/
|
|
2242
|
-
async getOrgAgentConfig() {
|
|
2243
|
-
if (this.orgAgentConfig !== void 0) return this.orgAgentConfig;
|
|
2244
|
-
if (!this.orgAgentConfigPromise) {
|
|
2245
|
-
this.orgAgentConfigPromise = fetchOrgAgentConfig(this.apiKey).then(
|
|
2246
|
-
(config) => {
|
|
2247
|
-
this.orgAgentConfig = config;
|
|
2248
|
-
return config;
|
|
2249
|
-
}
|
|
2250
|
-
);
|
|
2251
|
-
}
|
|
2252
|
-
return this.orgAgentConfigPromise;
|
|
2253
|
-
}
|
|
2254
|
-
/**
|
|
2255
|
-
* Apply the org policy to one agent, after it has activated.
|
|
2256
|
-
*
|
|
2257
|
-
* Sequenced after activation rather than alongside it because applying the
|
|
2258
|
-
* policy needs the authenticated session activation establishes. Every failure
|
|
2259
|
-
* mode is swallowed: a partner's splitting preference is not worth failing a
|
|
2260
|
-
* user's connect over.
|
|
2261
|
-
*/
|
|
2262
|
-
async applyOrgPolicyTo(agent, state, chainId) {
|
|
2263
|
-
if (!agent.applyOrgPolicy) {
|
|
2264
|
-
debugLog(
|
|
2265
|
-
"owney-sdk",
|
|
2266
|
-
`${agent.id}: no applyOrgPolicy \u2014 agent has no org-level knobs, skipping`
|
|
2267
|
-
);
|
|
2268
|
-
return;
|
|
2269
|
-
}
|
|
2270
|
-
try {
|
|
2271
|
-
const policy = await this.getOrgAgentConfig();
|
|
2272
|
-
if (!policy) return;
|
|
2273
|
-
await agent.applyOrgPolicy(state, chainId, policy);
|
|
2274
|
-
} catch (error) {
|
|
2275
|
-
console.warn(
|
|
2276
|
-
`[owney-sdk] applyOrgPolicy(${agent.id}) failed (non-fatal):`,
|
|
2277
|
-
error instanceof Error ? error.message : String(error)
|
|
2278
|
-
);
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
1891
|
async ensureAgentsInitialized() {
|
|
2282
1892
|
if (this.agents.size > 0) return;
|
|
2283
1893
|
if (!this.initializingAgentsPromise) {
|
|
2284
1894
|
this.initializingAgentsPromise = (async () => {
|
|
2285
1895
|
const agentKeys = await fetchAgentKeys(this.apiKey);
|
|
2286
1896
|
this.disabledAgents.clear();
|
|
2287
|
-
for (const { key
|
|
2288
|
-
const agent =
|
|
1897
|
+
for (const { key, agent_type, is_enabled } of agentKeys) {
|
|
1898
|
+
const agent = this.createAgent(agent_type, key);
|
|
2289
1899
|
if (!agent) continue;
|
|
2290
1900
|
this.agents.set(agent_type, agent);
|
|
2291
1901
|
if (is_enabled === false) {
|
|
@@ -2306,21 +1916,9 @@ var OwneySDK = class {
|
|
|
2306
1916
|
this.initializingAgentsPromise = null;
|
|
2307
1917
|
}
|
|
2308
1918
|
}
|
|
2309
|
-
|
|
1919
|
+
createAgent(agentId, key) {
|
|
2310
1920
|
if (agentId === "zyfai") {
|
|
2311
|
-
return new ZyfaiAgent(
|
|
2312
|
-
}
|
|
2313
|
-
if (agentId === "surfliquid") {
|
|
2314
|
-
if (!projectName) return null;
|
|
2315
|
-
const { SurfLiquidAgent } = await import("./surfliquid.agent-VNGQUSXN.js");
|
|
2316
|
-
return new SurfLiquidAgent({
|
|
2317
|
-
projectName,
|
|
2318
|
-
appId: key2,
|
|
2319
|
-
// Same-origin proxy override — SurfLiquid auth is an httpOnly cookie,
|
|
2320
|
-
// which WebKit (iOS) discards when it is third-party. See
|
|
2321
|
-
// OwneySDKConfig.surfliquidApiBaseUrl.
|
|
2322
|
-
apiBaseUrl: this.surfliquidApiBaseUrl
|
|
2323
|
-
});
|
|
1921
|
+
return new ZyfaiAgent(key, this.zyfaiRpcUrls, this.referralSource);
|
|
2324
1922
|
}
|
|
2325
1923
|
return null;
|
|
2326
1924
|
}
|
|
@@ -2374,7 +1972,6 @@ var OwneySDK = class {
|
|
|
2374
1972
|
Promise.all(
|
|
2375
1973
|
agents.map(async (agent) => {
|
|
2376
1974
|
await agent.activateAgent(state, chainId);
|
|
2377
|
-
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
2378
1975
|
})
|
|
2379
1976
|
).catch((error) => {
|
|
2380
1977
|
console.error("activateAgent background init failed:", error);
|
|
@@ -2402,7 +1999,6 @@ var OwneySDK = class {
|
|
|
2402
1999
|
Promise.all(
|
|
2403
2000
|
enabledCompatible.map(async (agent) => {
|
|
2404
2001
|
await agent.activateAgent(state, chainId);
|
|
2405
|
-
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
2406
2002
|
})
|
|
2407
2003
|
).catch((error) => {
|
|
2408
2004
|
console.error("activateAgent background init failed:", error);
|
|
@@ -2476,30 +2072,18 @@ var OwneySDK = class {
|
|
|
2476
2072
|
);
|
|
2477
2073
|
}
|
|
2478
2074
|
const agentResults = {};
|
|
2479
|
-
const agentErrors = {};
|
|
2480
2075
|
for (const { agent, amount: agentAmount } of agentAmounts) {
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
depositCallback
|
|
2490
|
-
);
|
|
2491
|
-
} catch (error) {
|
|
2492
|
-
agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
|
|
2493
|
-
}
|
|
2494
|
-
}
|
|
2495
|
-
if (Object.keys(agentResults).length === 0) {
|
|
2496
|
-
throw new OwneyError(
|
|
2497
|
-
"DEPOSIT_ALL_FAILED",
|
|
2498
|
-
`All ${agentAmounts.length} agent deposit(s) failed.`,
|
|
2499
|
-
{ agentErrors }
|
|
2076
|
+
agentResults[agent.id] = await this.depositWithFallback(
|
|
2077
|
+
agent,
|
|
2078
|
+
state,
|
|
2079
|
+
chainId,
|
|
2080
|
+
agentAmount,
|
|
2081
|
+
asset,
|
|
2082
|
+
effectiveCallback,
|
|
2083
|
+
depositCallback
|
|
2500
2084
|
);
|
|
2501
2085
|
}
|
|
2502
|
-
return
|
|
2086
|
+
return { agentResults };
|
|
2503
2087
|
}
|
|
2504
2088
|
/**
|
|
2505
2089
|
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
@@ -2615,8 +2199,9 @@ var OwneySDK = class {
|
|
|
2615
2199
|
async hasExistingBalance(agent, state, chainId, asset) {
|
|
2616
2200
|
try {
|
|
2617
2201
|
const balance = await agent.getBalances(state, chainId);
|
|
2202
|
+
const target = asset.toLowerCase();
|
|
2618
2203
|
const token = balance.tokens.find(
|
|
2619
|
-
(t) => t.chainId === chainId &&
|
|
2204
|
+
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
2620
2205
|
);
|
|
2621
2206
|
return !!token && Number(token.amount) > 0;
|
|
2622
2207
|
} catch {
|
|
@@ -2655,34 +2240,6 @@ var OwneySDK = class {
|
|
|
2655
2240
|
}
|
|
2656
2241
|
return eligible;
|
|
2657
2242
|
}
|
|
2658
|
-
/**
|
|
2659
|
-
* Agent ids the routing API provisioned for this org that support the given
|
|
2660
|
-
* chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
|
|
2661
|
-
* surfliquid first). Returns `[]` when the org has no compatible agent — never
|
|
2662
|
-
* throws on an empty org. Loads agent keys on first call (apiKey only, no
|
|
2663
|
-
* wallet), so the UI can resolve which agent to use before the user connects.
|
|
2664
|
-
*
|
|
2665
|
-
* This is the source of truth for agent availability: an agent appears here
|
|
2666
|
-
* iff the routing API returned its key. No per-app feature flags.
|
|
2667
|
-
*/
|
|
2668
|
-
async getEligibleAgentIds(chainId, asset) {
|
|
2669
|
-
try {
|
|
2670
|
-
await this.ensureAgentsInitialized();
|
|
2671
|
-
} catch (error) {
|
|
2672
|
-
if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
|
|
2673
|
-
return [];
|
|
2674
|
-
}
|
|
2675
|
-
throw error;
|
|
2676
|
-
}
|
|
2677
|
-
return [...this.agents.values()].filter((agent) => {
|
|
2678
|
-
const chainAssets = agent.supportedAssets.find(
|
|
2679
|
-
(sa) => sa.chainId === chainId
|
|
2680
|
-
);
|
|
2681
|
-
return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
|
|
2682
|
-
}).map((agent) => agent.id).sort(
|
|
2683
|
-
(a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
|
|
2684
|
-
);
|
|
2685
|
-
}
|
|
2686
2243
|
// --- Fund operations ---
|
|
2687
2244
|
/**
|
|
2688
2245
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -2718,17 +2275,9 @@ var OwneySDK = class {
|
|
|
2718
2275
|
}
|
|
2719
2276
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
2720
2277
|
if (!amount) {
|
|
2721
|
-
const aggregated2 = await this.getBalances();
|
|
2722
|
-
const funded = projectAgentBalancesForAsset(
|
|
2723
|
-
eligibleAgents,
|
|
2724
|
-
aggregated2.agentBalances,
|
|
2725
|
-
chainId,
|
|
2726
|
-
asset,
|
|
2727
|
-
assetInfo.decimals
|
|
2728
|
-
).filter((b) => b.balance > 0n);
|
|
2729
2278
|
const results2 = {};
|
|
2730
2279
|
const agentErrors2 = {};
|
|
2731
|
-
for (const
|
|
2280
|
+
for (const agent of eligibleAgents) {
|
|
2732
2281
|
try {
|
|
2733
2282
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
2734
2283
|
} catch (err) {
|
|
@@ -2745,10 +2294,9 @@ var OwneySDK = class {
|
|
|
2745
2294
|
{ asset, agentErrors: agentErrors2 }
|
|
2746
2295
|
);
|
|
2747
2296
|
}
|
|
2748
|
-
const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
|
|
2749
2297
|
return {
|
|
2750
2298
|
agentResult: results2,
|
|
2751
|
-
totalWithdrawn:
|
|
2299
|
+
totalWithdrawn: sumWithdrawnAmount(results2).toString()
|
|
2752
2300
|
};
|
|
2753
2301
|
}
|
|
2754
2302
|
const requested = BigInt(amount);
|
|
@@ -3265,22 +2813,22 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
3265
2813
|
const legacyKeys = [];
|
|
3266
2814
|
try {
|
|
3267
2815
|
for (let i = 0; i < store.length; i++) {
|
|
3268
|
-
const
|
|
3269
|
-
if (
|
|
2816
|
+
const key = store.key(i);
|
|
2817
|
+
if (key?.startsWith(prefix)) legacyKeys.push(key);
|
|
3270
2818
|
}
|
|
3271
2819
|
} catch {
|
|
3272
2820
|
return null;
|
|
3273
2821
|
}
|
|
3274
2822
|
let adopted = null;
|
|
3275
|
-
for (const
|
|
2823
|
+
for (const key of legacyKeys) {
|
|
3276
2824
|
if (!adopted) {
|
|
3277
2825
|
try {
|
|
3278
|
-
adopted = JSON.parse(store.getItem(
|
|
2826
|
+
adopted = JSON.parse(store.getItem(key) ?? "");
|
|
3279
2827
|
} catch {
|
|
3280
2828
|
}
|
|
3281
2829
|
}
|
|
3282
2830
|
try {
|
|
3283
|
-
store.removeItem(
|
|
2831
|
+
store.removeItem(key);
|
|
3284
2832
|
} catch {
|
|
3285
2833
|
}
|
|
3286
2834
|
}
|
|
@@ -3288,11 +2836,11 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
3288
2836
|
};
|
|
3289
2837
|
var readSiwxSession = (address, chainId) => {
|
|
3290
2838
|
if (typeof window === "undefined") return null;
|
|
3291
|
-
const
|
|
2839
|
+
const key = buildKey2(address);
|
|
3292
2840
|
const store = storage2();
|
|
3293
2841
|
let raw = null;
|
|
3294
2842
|
try {
|
|
3295
|
-
raw = store?.getItem(
|
|
2843
|
+
raw = store?.getItem(key) ?? null;
|
|
3296
2844
|
} catch {
|
|
3297
2845
|
raw = null;
|
|
3298
2846
|
}
|
|
@@ -3300,9 +2848,9 @@ var readSiwxSession = (address, chainId) => {
|
|
|
3300
2848
|
try {
|
|
3301
2849
|
return JSON.parse(raw);
|
|
3302
2850
|
} catch {
|
|
3303
|
-
memorySiwxSessions.delete(
|
|
2851
|
+
memorySiwxSessions.delete(key);
|
|
3304
2852
|
try {
|
|
3305
|
-
store?.removeItem(
|
|
2853
|
+
store?.removeItem(key);
|
|
3306
2854
|
} catch {
|
|
3307
2855
|
}
|
|
3308
2856
|
return null;
|
|
@@ -3313,24 +2861,24 @@ var readSiwxSession = (address, chainId) => {
|
|
|
3313
2861
|
writeSiwxSession(address, chainId, legacy);
|
|
3314
2862
|
return legacy;
|
|
3315
2863
|
}
|
|
3316
|
-
return memorySiwxSessions.get(
|
|
2864
|
+
return memorySiwxSessions.get(key) ?? null;
|
|
3317
2865
|
};
|
|
3318
2866
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
3319
2867
|
if (typeof window === "undefined") return;
|
|
3320
|
-
const
|
|
3321
|
-
memorySiwxSessions.set(
|
|
2868
|
+
const key = buildKey2(address);
|
|
2869
|
+
memorySiwxSessions.set(key, session);
|
|
3322
2870
|
const store = storage2();
|
|
3323
2871
|
try {
|
|
3324
|
-
store?.setItem(
|
|
2872
|
+
store?.setItem(key, JSON.stringify(session));
|
|
3325
2873
|
} catch {
|
|
3326
2874
|
}
|
|
3327
2875
|
};
|
|
3328
2876
|
var clearSiwxSession = (address, _chainId) => {
|
|
3329
|
-
const
|
|
3330
|
-
memorySiwxSessions.delete(
|
|
2877
|
+
const key = buildKey2(address);
|
|
2878
|
+
memorySiwxSessions.delete(key);
|
|
3331
2879
|
const store = storage2();
|
|
3332
2880
|
try {
|
|
3333
|
-
store?.removeItem(
|
|
2881
|
+
store?.removeItem(key);
|
|
3334
2882
|
} catch {
|
|
3335
2883
|
}
|
|
3336
2884
|
readLegacySiwxSession(store, address);
|
|
@@ -3459,6 +3007,5 @@ export {
|
|
|
3459
3007
|
NotConnectedError,
|
|
3460
3008
|
OwneyError,
|
|
3461
3009
|
OwneySDK,
|
|
3462
|
-
createOwneySIWX
|
|
3463
|
-
setOwneyDebug
|
|
3010
|
+
createOwneySIWX
|
|
3464
3011
|
};
|