@owney/sdk 0.6.5 → 0.6.6-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/dist/chunk-GMNQBWUB.js +55 -0
- package/dist/index.cjs +755 -699
- package/dist/index.d.cts +82 -144
- package/dist/index.d.ts +82 -144
- package/dist/index.js +265 -698
- package/dist/surfliquid.agent-EPFISJPR.js +390 -0
- package/package.json +4 -5
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
6
9
|
var __export = (target, all) => {
|
|
7
10
|
for (var name in all)
|
|
8
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -17,9 +20,486 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
20
|
};
|
|
18
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
22
|
|
|
23
|
+
// src/errors.ts
|
|
24
|
+
var OwneyError, AgentNotFoundError, NotConnectedError, AgentChainIncompatibleError;
|
|
25
|
+
var init_errors = __esm({
|
|
26
|
+
"src/errors.ts"() {
|
|
27
|
+
"use strict";
|
|
28
|
+
OwneyError = class extends Error {
|
|
29
|
+
code;
|
|
30
|
+
details;
|
|
31
|
+
agentId;
|
|
32
|
+
constructor(code, message, details, agentId) {
|
|
33
|
+
const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
|
|
34
|
+
super(`${prefix} ${message}`);
|
|
35
|
+
this.name = "OwneyError";
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.details = details;
|
|
38
|
+
this.agentId = agentId;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
AgentNotFoundError = class extends OwneyError {
|
|
42
|
+
constructor(agentId, available) {
|
|
43
|
+
super(
|
|
44
|
+
"AGENT_NOT_FOUND",
|
|
45
|
+
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
46
|
+
{ agentId, available },
|
|
47
|
+
agentId
|
|
48
|
+
);
|
|
49
|
+
this.name = "AgentNotFoundError";
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
NotConnectedError = class extends OwneyError {
|
|
53
|
+
constructor() {
|
|
54
|
+
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
55
|
+
this.name = "NotConnectedError";
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
AgentChainIncompatibleError = class extends OwneyError {
|
|
59
|
+
incompatibleAgents;
|
|
60
|
+
connectedChainId;
|
|
61
|
+
constructor(incompatibleAgents, connectedChainId) {
|
|
62
|
+
const details = incompatibleAgents.map(
|
|
63
|
+
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
64
|
+
).join("; ");
|
|
65
|
+
super(
|
|
66
|
+
"AGENT_CHAIN_INCOMPATIBLE",
|
|
67
|
+
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
68
|
+
{ incompatibleAgents, connectedChainId }
|
|
69
|
+
);
|
|
70
|
+
this.name = "AgentChainIncompatibleError";
|
|
71
|
+
this.incompatibleAgents = incompatibleAgents;
|
|
72
|
+
this.connectedChainId = connectedChainId;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// src/agents/surfliquid/surfliquid.constants.ts
|
|
79
|
+
var SURFLIQUID_CHAIN_ID, SURFLIQUID_CHAIN_NAME, SURFLIQUID_USDC_ADDRESS, SURFLIQUID_USDC_DECIMALS, SURFLIQUID_MIN_DEPOSIT, SURFLIQUID_SUPPORTED_ASSETS, SURFLIQUID_ACTION_MAP;
|
|
80
|
+
var init_surfliquid_constants = __esm({
|
|
81
|
+
"src/agents/surfliquid/surfliquid.constants.ts"() {
|
|
82
|
+
"use strict";
|
|
83
|
+
SURFLIQUID_CHAIN_ID = 8453;
|
|
84
|
+
SURFLIQUID_CHAIN_NAME = "BASE";
|
|
85
|
+
SURFLIQUID_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
86
|
+
SURFLIQUID_USDC_DECIMALS = 6;
|
|
87
|
+
SURFLIQUID_MIN_DEPOSIT = "0";
|
|
88
|
+
SURFLIQUID_SUPPORTED_ASSETS = [
|
|
89
|
+
{
|
|
90
|
+
chainId: SURFLIQUID_CHAIN_ID,
|
|
91
|
+
chain: SURFLIQUID_CHAIN_NAME,
|
|
92
|
+
assets: [{ symbol: "USDC", minDepositAmount: SURFLIQUID_MIN_DEPOSIT }]
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
SURFLIQUID_ACTION_MAP = {
|
|
96
|
+
// INITIAL_DEPOSIT is the user's real first deposit, so surface it like any
|
|
97
|
+
// other funding ("Top up"). The mini-app intentionally hides the "Deposit"
|
|
98
|
+
// action (it marks Zyfai's smart-account-creation event), which would
|
|
99
|
+
// otherwise drop a SurfLiquid user's only history row.
|
|
100
|
+
INITIAL_DEPOSIT: "Top up",
|
|
101
|
+
DEPOSIT: "Top up",
|
|
102
|
+
USER_DEPOSIT: "Top up",
|
|
103
|
+
WITHDRAWAL: "Withdraw",
|
|
104
|
+
USER_WITHDRAWAL: "Withdraw",
|
|
105
|
+
REBALANCE: "Rebalance",
|
|
106
|
+
REBALANCE_COMPLETED: "Rebalance",
|
|
107
|
+
CROSS_CHAIN_REBALANCE: "Rebalance",
|
|
108
|
+
MERKL_CLAIM: "Earned"
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// src/agents/surfliquid/surfliquid.mapper.ts
|
|
114
|
+
function mapMessage(message) {
|
|
115
|
+
const action = SURFLIQUID_ACTION_MAP[message.transactionType];
|
|
116
|
+
if (!action)
|
|
117
|
+
return null;
|
|
118
|
+
return {
|
|
119
|
+
agent: "surfliquid",
|
|
120
|
+
action,
|
|
121
|
+
date: message.timestamp,
|
|
122
|
+
oldApy: message.apyBefore != null ? String(message.apyBefore) : null,
|
|
123
|
+
newApy: message.apyAfter != null ? String(message.apyAfter) : null,
|
|
124
|
+
transactions: [
|
|
125
|
+
{
|
|
126
|
+
txHashes: [message.txHash],
|
|
127
|
+
chainId: message.chainId,
|
|
128
|
+
tokenSymbol: message.token ?? void 0,
|
|
129
|
+
amount: message.amount != null ? String(message.amount) : void 0
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
rebalanceLog: []
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function mapHistory(result, _chainId) {
|
|
136
|
+
const data = result.messages.map(mapMessage).filter((entry) => entry !== null);
|
|
137
|
+
const hasMore = result.page < result.pages;
|
|
138
|
+
return {
|
|
139
|
+
data,
|
|
140
|
+
hasMore,
|
|
141
|
+
nextCursor: hasMore ? String(result.page + 1) : void 0
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function mapBalances2(vault, chainId, morphoStats) {
|
|
145
|
+
const assets = (vault.assets ?? []).filter(
|
|
146
|
+
(asset) => asset.assetSymbol === "USDC" && asset.chainId === chainId
|
|
147
|
+
);
|
|
148
|
+
const tokens = assets.map((asset) => ({
|
|
149
|
+
chain: SURFLIQUID_CHAIN_NAME,
|
|
150
|
+
chainId: SURFLIQUID_CHAIN_ID,
|
|
151
|
+
asset: "USDC",
|
|
152
|
+
amount: String(asset.currentValueUSD)
|
|
153
|
+
}));
|
|
154
|
+
const positions = assets.map((asset) => {
|
|
155
|
+
const stats = morphoStats?.get(asset.morphoVaultAddress?.toLowerCase() ?? "");
|
|
156
|
+
return {
|
|
157
|
+
chain: SURFLIQUID_CHAIN_NAME,
|
|
158
|
+
protocol: "Morpho",
|
|
159
|
+
// The specific MetaMorpho vault (e.g. "Gauntlet USDC Frontier"), read from
|
|
160
|
+
// Morpho — renders as "Morpho <vault>" alongside zyfai's vault detail.
|
|
161
|
+
pool: stats?.name ?? void 0,
|
|
162
|
+
asset: "USDC",
|
|
163
|
+
amount: String(asset.currentValueUSD),
|
|
164
|
+
apy: asset.currentAPY,
|
|
165
|
+
// TVL + liquidity come straight from Morpho (SurfLiquid's API omits them).
|
|
166
|
+
tvl: stats?.tvlUsd,
|
|
167
|
+
liquidity: stats?.liquidityUsd
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
const total = assets.reduce(
|
|
171
|
+
(sum, asset) => sum + asset.currentValueUSD,
|
|
172
|
+
0
|
|
173
|
+
);
|
|
174
|
+
return {
|
|
175
|
+
smartWallet: vault.userVaultAddress ?? void 0,
|
|
176
|
+
totalBalance: String(total),
|
|
177
|
+
totalBalanceAsset: "usdc",
|
|
178
|
+
tokens,
|
|
179
|
+
positions
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function mapAccountApy(vault, days) {
|
|
183
|
+
const breakdown = vault.apyBreakdown;
|
|
184
|
+
const windowed = breakdown ? breakdown[APY_WINDOW_KEY[days]] : void 0;
|
|
185
|
+
const apy = windowed != null ? windowed : breakdown?.currentAPY ?? 0;
|
|
186
|
+
return {
|
|
187
|
+
walletAddress: vault.walletAddress ?? "",
|
|
188
|
+
weightedApyAfterFee: apy,
|
|
189
|
+
apyByChainAndAsset: { [SURFLIQUID_CHAIN_ID]: { USDC: apy } },
|
|
190
|
+
// Parity with Sail: SurfLiquid's core-sdk exposes no daily net-APY series.
|
|
191
|
+
history: []
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function mapEarnings2(vault, vaultAddress) {
|
|
195
|
+
return {
|
|
196
|
+
smartWallet: vaultAddress,
|
|
197
|
+
lifetimeEarnings: vault.earned?.totalEarningsUSD ?? 0,
|
|
198
|
+
tokens: []
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function mapUserProfile2(vault, address) {
|
|
202
|
+
return {
|
|
203
|
+
address,
|
|
204
|
+
smartWallet: vault.userVaultAddress ?? "",
|
|
205
|
+
chains: [SURFLIQUID_CHAIN_ID],
|
|
206
|
+
// SurfLiquid uses cookie-based session auth — no session-key concept.
|
|
207
|
+
hasActiveSessionKey: false,
|
|
208
|
+
protocols: ["SurfLiquid"]
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
var APY_WINDOW_KEY;
|
|
212
|
+
var init_surfliquid_mapper = __esm({
|
|
213
|
+
"src/agents/surfliquid/surfliquid.mapper.ts"() {
|
|
214
|
+
"use strict";
|
|
215
|
+
init_surfliquid_constants();
|
|
216
|
+
APY_WINDOW_KEY = {
|
|
217
|
+
"7D": "apy7d",
|
|
218
|
+
"14D": "apy14d",
|
|
219
|
+
"30D": "apy30d"
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// src/agents/surfliquid/surfliquid.wallet-adapter.ts
|
|
225
|
+
var import_core_sdk, OwneyWalletAdapter;
|
|
226
|
+
var init_surfliquid_wallet_adapter = __esm({
|
|
227
|
+
"src/agents/surfliquid/surfliquid.wallet-adapter.ts"() {
|
|
228
|
+
"use strict";
|
|
229
|
+
import_core_sdk = require("@surf_liquid/core-sdk");
|
|
230
|
+
OwneyWalletAdapter = class extends import_core_sdk.BaseWalletAdapter {
|
|
231
|
+
constructor(eip1193Provider) {
|
|
232
|
+
super();
|
|
233
|
+
this.eip1193Provider = eip1193Provider;
|
|
234
|
+
}
|
|
235
|
+
name = "owney";
|
|
236
|
+
get installed() {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
resolveProvider() {
|
|
240
|
+
return this.eip1193Provider;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// src/agents/surfliquid/surfliquid.morpho.ts
|
|
247
|
+
async function fetchMorphoVaultStats(vaultAddress, chainId) {
|
|
248
|
+
try {
|
|
249
|
+
const res = await fetch(MORPHO_API_URL, {
|
|
250
|
+
method: "POST",
|
|
251
|
+
headers: { "Content-Type": "application/json" },
|
|
252
|
+
body: JSON.stringify({
|
|
253
|
+
query: VAULT_STATS_QUERY,
|
|
254
|
+
variables: { address: vaultAddress, chainId }
|
|
255
|
+
})
|
|
256
|
+
});
|
|
257
|
+
if (!res.ok)
|
|
258
|
+
return null;
|
|
259
|
+
const json = await res.json();
|
|
260
|
+
if (json.errors)
|
|
261
|
+
return null;
|
|
262
|
+
const vault = json.data?.vaultByAddress;
|
|
263
|
+
const tvlUsd = vault?.state?.totalAssetsUsd;
|
|
264
|
+
const liquidityUsd = vault?.liquidity?.usd;
|
|
265
|
+
if (typeof tvlUsd !== "number" || typeof liquidityUsd !== "number") {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
const name = typeof vault?.name === "string" ? vault.name : null;
|
|
269
|
+
return { name, tvlUsd, liquidityUsd };
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.warn("surfliquid: Morpho vault stats fetch failed", {
|
|
272
|
+
vaultAddress,
|
|
273
|
+
chainId,
|
|
274
|
+
error
|
|
275
|
+
});
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async function fetchMorphoStatsByVault(vaultAddresses, chainId) {
|
|
280
|
+
const unique = [...new Set(vaultAddresses.map((a) => a.toLowerCase()))];
|
|
281
|
+
const entries = await Promise.all(
|
|
282
|
+
unique.map(
|
|
283
|
+
async (address) => [address, await fetchMorphoVaultStats(address, chainId)]
|
|
284
|
+
)
|
|
285
|
+
);
|
|
286
|
+
const byVault = /* @__PURE__ */ new Map();
|
|
287
|
+
for (const [address, stats] of entries) {
|
|
288
|
+
if (stats)
|
|
289
|
+
byVault.set(address, stats);
|
|
290
|
+
}
|
|
291
|
+
return byVault;
|
|
292
|
+
}
|
|
293
|
+
var MORPHO_API_URL, VAULT_STATS_QUERY;
|
|
294
|
+
var init_surfliquid_morpho = __esm({
|
|
295
|
+
"src/agents/surfliquid/surfliquid.morpho.ts"() {
|
|
296
|
+
"use strict";
|
|
297
|
+
MORPHO_API_URL = "https://api.morpho.org/graphql";
|
|
298
|
+
VAULT_STATS_QUERY = `
|
|
299
|
+
query VaultStats($address: String!, $chainId: Int!) {
|
|
300
|
+
vaultByAddress(address: $address, chainId: $chainId) {
|
|
301
|
+
name
|
|
302
|
+
state { totalAssetsUsd }
|
|
303
|
+
liquidity { usd }
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
`;
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// src/agents/surfliquid/surfliquid.agent.ts
|
|
311
|
+
var surfliquid_agent_exports = {};
|
|
312
|
+
__export(surfliquid_agent_exports, {
|
|
313
|
+
SurfLiquidAgent: () => SurfLiquidAgent
|
|
314
|
+
});
|
|
315
|
+
var import_core_sdk2, import_viem4, APY_WINDOW_KEY2, SurfLiquidAgent;
|
|
316
|
+
var init_surfliquid_agent = __esm({
|
|
317
|
+
"src/agents/surfliquid/surfliquid.agent.ts"() {
|
|
318
|
+
"use strict";
|
|
319
|
+
import_core_sdk2 = require("@surf_liquid/core-sdk");
|
|
320
|
+
import_viem4 = require("viem");
|
|
321
|
+
init_errors();
|
|
322
|
+
init_surfliquid_constants();
|
|
323
|
+
init_surfliquid_mapper();
|
|
324
|
+
init_surfliquid_wallet_adapter();
|
|
325
|
+
init_surfliquid_morpho();
|
|
326
|
+
APY_WINDOW_KEY2 = {
|
|
327
|
+
"7D": "apy7d",
|
|
328
|
+
"14D": "apy14d",
|
|
329
|
+
"30D": "apy30d"
|
|
330
|
+
};
|
|
331
|
+
SurfLiquidAgent = class {
|
|
332
|
+
constructor(config) {
|
|
333
|
+
this.config = config;
|
|
334
|
+
}
|
|
335
|
+
id = "surfliquid";
|
|
336
|
+
supportedChainIds = [SURFLIQUID_CHAIN_ID];
|
|
337
|
+
supportedAssets = SURFLIQUID_SUPPORTED_ASSETS;
|
|
338
|
+
surf = null;
|
|
339
|
+
connectedAddress = null;
|
|
340
|
+
// In-flight connect+auth, shared by concurrent callers to dedupe SIWE.
|
|
341
|
+
connectPromise = null;
|
|
342
|
+
ensureSurf() {
|
|
343
|
+
if (this.surf)
|
|
344
|
+
return this.surf;
|
|
345
|
+
this.surf = import_core_sdk2.SurfClient.create({
|
|
346
|
+
projectName: this.config.projectName,
|
|
347
|
+
appId: this.config.appId,
|
|
348
|
+
environment: "mainnet",
|
|
349
|
+
chainId: SURFLIQUID_CHAIN_ID,
|
|
350
|
+
apiBaseUrl: this.config.apiBaseUrl,
|
|
351
|
+
rpcUrl: this.config.rpcUrl,
|
|
352
|
+
// Let the SDK auto-approve USDC to the vault before depositing.
|
|
353
|
+
autoApprove: true
|
|
354
|
+
});
|
|
355
|
+
return this.surf;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Connects + authenticates the SurfClient against owney's provider. Skips the
|
|
359
|
+
* SIWE prompt when already authenticated for this wallet (cookie session).
|
|
360
|
+
*/
|
|
361
|
+
async connect(state) {
|
|
362
|
+
const surf = this.ensureSurf();
|
|
363
|
+
const sameWallet = this.connectedAddress?.toLowerCase() === state.walletAddress.toLowerCase();
|
|
364
|
+
if (sameWallet && surf.getAuthState().authenticated) {
|
|
365
|
+
return surf;
|
|
366
|
+
}
|
|
367
|
+
if (this.connectPromise)
|
|
368
|
+
return this.connectPromise;
|
|
369
|
+
this.connectPromise = (async () => {
|
|
370
|
+
surf.registerWalletAdapter(
|
|
371
|
+
"owney",
|
|
372
|
+
new OwneyWalletAdapter(state.provider)
|
|
373
|
+
);
|
|
374
|
+
await surf.connectWallet("owney");
|
|
375
|
+
await surf.authenticate();
|
|
376
|
+
this.connectedAddress = state.walletAddress;
|
|
377
|
+
return surf;
|
|
378
|
+
})();
|
|
379
|
+
try {
|
|
380
|
+
return await this.connectPromise;
|
|
381
|
+
} finally {
|
|
382
|
+
this.connectPromise = null;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Returns the user's vault address, deploying one on first use. SurfLiquid's
|
|
387
|
+
* deposit requires the vault to exist, so this must run before depositing.
|
|
388
|
+
* Existing vault → returned as-is; otherwise `deployVault()` is sent
|
|
389
|
+
* (user-pays) and the freshly deployed address is returned.
|
|
390
|
+
*/
|
|
391
|
+
async resolveVaultAddress(surf, owner) {
|
|
392
|
+
const vault = await surf.getVault(owner).catch(() => null);
|
|
393
|
+
if (vault?.exists && vault.userVaultAddress) {
|
|
394
|
+
return vault.userVaultAddress;
|
|
395
|
+
}
|
|
396
|
+
const { vaultAddress } = await surf.deployVault();
|
|
397
|
+
return vaultAddress;
|
|
398
|
+
}
|
|
399
|
+
// --- IAgent: lifecycle ---
|
|
400
|
+
async disconnect() {
|
|
401
|
+
if (this.surf) {
|
|
402
|
+
await this.surf.disconnectWallet();
|
|
403
|
+
}
|
|
404
|
+
this.connectedAddress = null;
|
|
405
|
+
}
|
|
406
|
+
async activateAgent(state) {
|
|
407
|
+
await this.connect(state);
|
|
408
|
+
}
|
|
409
|
+
// --- IAgent: funds (user-pays; depositCallback intentionally ignored) ---
|
|
410
|
+
async deposit(state, _chainId, amount, _depositCallback) {
|
|
411
|
+
const surf = await this.connect(state);
|
|
412
|
+
const human = (0, import_viem4.formatUnits)(BigInt(amount), SURFLIQUID_USDC_DECIMALS);
|
|
413
|
+
const smartWallet = await this.resolveVaultAddress(
|
|
414
|
+
surf,
|
|
415
|
+
state.walletAddress
|
|
416
|
+
);
|
|
417
|
+
const tx = await surf.deposit({
|
|
418
|
+
asset: SURFLIQUID_USDC_ADDRESS,
|
|
419
|
+
amount: human
|
|
420
|
+
});
|
|
421
|
+
await tx.wait();
|
|
422
|
+
return { txHash: tx.hash, smartWallet, amount };
|
|
423
|
+
}
|
|
424
|
+
async withdraw(state, _chainId, _token, amount) {
|
|
425
|
+
const surf = await this.connect(state);
|
|
426
|
+
const human = amount != null ? (0, import_viem4.formatUnits)(BigInt(amount), SURFLIQUID_USDC_DECIMALS) : void 0;
|
|
427
|
+
try {
|
|
428
|
+
const tx = await surf.withdraw({
|
|
429
|
+
asset: SURFLIQUID_USDC_ADDRESS,
|
|
430
|
+
amount: human
|
|
431
|
+
});
|
|
432
|
+
await tx.wait();
|
|
433
|
+
return {
|
|
434
|
+
txHash: tx.hash,
|
|
435
|
+
type: amount != null ? "partial" : "full",
|
|
436
|
+
amount: amount ?? "0"
|
|
437
|
+
};
|
|
438
|
+
} catch (error) {
|
|
439
|
+
throw new OwneyError(
|
|
440
|
+
"WITHDRAW_FAILED",
|
|
441
|
+
error instanceof Error ? error.message : "SurfLiquid withdraw failed.",
|
|
442
|
+
{ chainId: SURFLIQUID_CHAIN_ID, amount },
|
|
443
|
+
this.id
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
// --- IAgent: portfolio reads ---
|
|
448
|
+
async getBalances(state, chainId) {
|
|
449
|
+
const surf = await this.connect(state);
|
|
450
|
+
const vault = await surf.getVault(state.walletAddress);
|
|
451
|
+
const morphoVaultAddresses = (vault.assets ?? []).filter((asset) => asset.assetSymbol === "USDC" && asset.chainId === chainId).map((asset) => asset.morphoVaultAddress).filter((address) => Boolean(address));
|
|
452
|
+
const morphoStats = await fetchMorphoStatsByVault(
|
|
453
|
+
morphoVaultAddresses,
|
|
454
|
+
chainId
|
|
455
|
+
);
|
|
456
|
+
return mapBalances2(vault, chainId, morphoStats);
|
|
457
|
+
}
|
|
458
|
+
async getEarnings(state) {
|
|
459
|
+
const surf = await this.connect(state);
|
|
460
|
+
const vault = await surf.getVault(state.walletAddress);
|
|
461
|
+
return mapEarnings2(vault, vault.userVaultAddress ?? state.walletAddress);
|
|
462
|
+
}
|
|
463
|
+
async getAccountApy(state, _chainId, days) {
|
|
464
|
+
const surf = await this.connect(state);
|
|
465
|
+
const vault = await surf.getVault(state.walletAddress);
|
|
466
|
+
return mapAccountApy(vault, days);
|
|
467
|
+
}
|
|
468
|
+
async getHistory(state, chainId, options) {
|
|
469
|
+
const surf = await this.connect(state);
|
|
470
|
+
const page = options?.cursor ? Number(options.cursor) : 1;
|
|
471
|
+
const limit = options?.limit ?? 10;
|
|
472
|
+
const result = await surf.getAgentMessages(
|
|
473
|
+
state.walletAddress,
|
|
474
|
+
page,
|
|
475
|
+
limit,
|
|
476
|
+
options?.fromDate,
|
|
477
|
+
options?.toDate
|
|
478
|
+
);
|
|
479
|
+
return mapHistory(result, chainId);
|
|
480
|
+
}
|
|
481
|
+
async getUserProfile(state) {
|
|
482
|
+
const surf = await this.connect(state);
|
|
483
|
+
const vault = await surf.getVault(state.walletAddress);
|
|
484
|
+
return mapUserProfile2(vault, state.walletAddress);
|
|
485
|
+
}
|
|
486
|
+
// --- IAgent: discovery (no wallet) ---
|
|
487
|
+
async getAgentApy(days, _options) {
|
|
488
|
+
const surf = this.ensureSurf();
|
|
489
|
+
const assets = await surf.getSupportedAssets(SURFLIQUID_CHAIN_ID);
|
|
490
|
+
const usdc = assets.find((asset) => asset.assetSymbol === "USDC");
|
|
491
|
+
if (!usdc)
|
|
492
|
+
return { averageApy: 0 };
|
|
493
|
+
const windowed = usdc[APY_WINDOW_KEY2[days]];
|
|
494
|
+
return { averageApy: windowed != null ? windowed : usdc.currentAPY };
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
|
|
20
500
|
// src/index.ts
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
501
|
+
var src_exports = {};
|
|
502
|
+
__export(src_exports, {
|
|
23
503
|
AgentChainIncompatibleError: () => AgentChainIncompatibleError,
|
|
24
504
|
AgentNotFoundError: () => AgentNotFoundError,
|
|
25
505
|
InvalidHistoryCursorError: () => InvalidHistoryCursorError,
|
|
@@ -28,7 +508,7 @@ __export(index_exports, {
|
|
|
28
508
|
OwneySDK: () => OwneySDK,
|
|
29
509
|
createOwneySIWX: () => createOwneySIWX
|
|
30
510
|
});
|
|
31
|
-
module.exports = __toCommonJS(
|
|
511
|
+
module.exports = __toCommonJS(src_exports);
|
|
32
512
|
|
|
33
513
|
// src/agents/zyfai/zyfai.agent.ts
|
|
34
514
|
var import_sdk = require("@zyfai/sdk");
|
|
@@ -37,10 +517,10 @@ var import_chains = require("viem/chains");
|
|
|
37
517
|
|
|
38
518
|
// src/types/config.ts
|
|
39
519
|
var P_SUPPORTED_CHAINS = {
|
|
40
|
-
chainIds: [8453, 42161
|
|
41
|
-
chains: ["BASE", "ARBITRUM"
|
|
520
|
+
chainIds: [8453, 42161],
|
|
521
|
+
chains: ["BASE", "ARBITRUM"]
|
|
42
522
|
};
|
|
43
|
-
var SUPPORTED_TOKENS = ["USDC", "USDT"
|
|
523
|
+
var SUPPORTED_TOKENS = ["USDC", "USDT"];
|
|
44
524
|
var SUPPORTED_CHAIN_IDS = P_SUPPORTED_CHAINS.chainIds;
|
|
45
525
|
var SUPPORTED_CHAINS = P_SUPPORTED_CHAINS.chains;
|
|
46
526
|
var SupportedAssets = [
|
|
@@ -61,83 +541,11 @@ var SupportedAssets = [
|
|
|
61
541
|
symbol: "USDT",
|
|
62
542
|
chainId: 42161,
|
|
63
543
|
decimals: 6
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
address: `0x4200000000000000000000000000000000000006`,
|
|
67
|
-
symbol: "WETH",
|
|
68
|
-
chainId: 8453,
|
|
69
|
-
decimals: 18
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
address: `0x82aF49447D8a07e3bd95BD0d56f35241523fBab1`,
|
|
73
|
-
symbol: "WETH",
|
|
74
|
-
chainId: 42161,
|
|
75
|
-
decimals: 18
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
address: `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48`,
|
|
79
|
-
symbol: "USDC",
|
|
80
|
-
chainId: 1,
|
|
81
|
-
decimals: 6
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
address: `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2`,
|
|
85
|
-
symbol: "WETH",
|
|
86
|
-
chainId: 1,
|
|
87
|
-
decimals: 18
|
|
88
544
|
}
|
|
89
545
|
];
|
|
90
546
|
|
|
91
|
-
// src/errors.ts
|
|
92
|
-
var OwneyError = class extends Error {
|
|
93
|
-
code;
|
|
94
|
-
details;
|
|
95
|
-
agentId;
|
|
96
|
-
constructor(code, message, details, agentId) {
|
|
97
|
-
const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
|
|
98
|
-
super(`${prefix} ${message}`);
|
|
99
|
-
this.name = "OwneyError";
|
|
100
|
-
this.code = code;
|
|
101
|
-
this.details = details;
|
|
102
|
-
this.agentId = agentId;
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
var AgentNotFoundError = class extends OwneyError {
|
|
106
|
-
constructor(agentId, available) {
|
|
107
|
-
super(
|
|
108
|
-
"AGENT_NOT_FOUND",
|
|
109
|
-
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
110
|
-
{ agentId, available },
|
|
111
|
-
agentId
|
|
112
|
-
);
|
|
113
|
-
this.name = "AgentNotFoundError";
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
var NotConnectedError = class extends OwneyError {
|
|
117
|
-
constructor() {
|
|
118
|
-
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
119
|
-
this.name = "NotConnectedError";
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
var AgentChainIncompatibleError = class extends OwneyError {
|
|
123
|
-
incompatibleAgents;
|
|
124
|
-
connectedChainId;
|
|
125
|
-
constructor(incompatibleAgents, connectedChainId) {
|
|
126
|
-
const details = incompatibleAgents.map(
|
|
127
|
-
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
128
|
-
).join("; ");
|
|
129
|
-
super(
|
|
130
|
-
"AGENT_CHAIN_INCOMPATIBLE",
|
|
131
|
-
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
132
|
-
{ incompatibleAgents, connectedChainId }
|
|
133
|
-
);
|
|
134
|
-
this.name = "AgentChainIncompatibleError";
|
|
135
|
-
this.incompatibleAgents = incompatibleAgents;
|
|
136
|
-
this.connectedChainId = connectedChainId;
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
|
|
140
547
|
// src/lib/utils.ts
|
|
548
|
+
init_errors();
|
|
141
549
|
var isValidChainId = (chainId) => {
|
|
142
550
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
|
|
143
551
|
throw new OwneyError(
|
|
@@ -151,7 +559,8 @@ var isValidChainId = (chainId) => {
|
|
|
151
559
|
function hexToDecimal(hex, decimals = 6) {
|
|
152
560
|
const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
|
|
153
561
|
const parsed = BigInt(normalized);
|
|
154
|
-
if (decimals <= 0)
|
|
562
|
+
if (decimals <= 0)
|
|
563
|
+
return Number(parsed);
|
|
155
564
|
const divisor = BigInt(10 ** decimals);
|
|
156
565
|
const whole = parsed / divisor;
|
|
157
566
|
const remainder = parsed % divisor;
|
|
@@ -164,20 +573,6 @@ var DayFilterMapping = {
|
|
|
164
573
|
};
|
|
165
574
|
|
|
166
575
|
// src/agents/zyfai/zyfai.mapper.ts
|
|
167
|
-
var DECIMALS_BY_SYMBOL = {
|
|
168
|
-
usdc: 6,
|
|
169
|
-
usdt: 6,
|
|
170
|
-
weth: 18,
|
|
171
|
-
eth: 18,
|
|
172
|
-
cbeth: 18,
|
|
173
|
-
wbtc: 8,
|
|
174
|
-
cbbtc: 8
|
|
175
|
-
};
|
|
176
|
-
function tokenDecimals(symbol, explicit) {
|
|
177
|
-
if (typeof explicit === "number" && Number.isFinite(explicit))
|
|
178
|
-
return explicit;
|
|
179
|
-
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
180
|
-
}
|
|
181
576
|
function mapDeposit(raw) {
|
|
182
577
|
return {
|
|
183
578
|
txHash: raw.txHash,
|
|
@@ -194,8 +589,7 @@ function mapWithdraw(raw) {
|
|
|
194
589
|
}
|
|
195
590
|
var CHAIN_ID_TO_NAME = {
|
|
196
591
|
8453: "BASE",
|
|
197
|
-
42161: "ARBITRUM"
|
|
198
|
-
1: "ETHEREUM"
|
|
592
|
+
42161: "ARBITRUM"
|
|
199
593
|
};
|
|
200
594
|
var NAME_TO_CHAIN_ID = Object.entries(
|
|
201
595
|
CHAIN_ID_TO_NAME
|
|
@@ -205,52 +599,54 @@ var NAME_TO_CHAIN_ID = Object.entries(
|
|
|
205
599
|
}, {});
|
|
206
600
|
function resolveChainId(chain) {
|
|
207
601
|
const asNum = Number(chain);
|
|
208
|
-
if (Number.isFinite(asNum) && asNum > 0)
|
|
602
|
+
if (Number.isFinite(asNum) && asNum > 0)
|
|
603
|
+
return asNum;
|
|
209
604
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
210
605
|
}
|
|
211
606
|
function mapBalances(raw, _chainId, smartWallet) {
|
|
212
607
|
const portfolio = raw.portfolio;
|
|
213
608
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
609
|
+
const rawPositions = portfolio.positions ?? [];
|
|
610
|
+
const detailByKey = /* @__PURE__ */ new Map();
|
|
611
|
+
for (const p of rawPositions) {
|
|
612
|
+
const cid = resolveChainId(p.chain ?? "");
|
|
613
|
+
const symbol = (p.token_symbol ?? "").toUpperCase();
|
|
614
|
+
if (Number.isFinite(cid) && symbol)
|
|
615
|
+
detailByKey.set(`${cid}:${symbol}`, p);
|
|
616
|
+
}
|
|
214
617
|
let totalBalance = 0;
|
|
215
618
|
const tokens = [];
|
|
619
|
+
const positions = [];
|
|
216
620
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
|
|
217
621
|
const positionChainId = Number(chainIdKey);
|
|
622
|
+
const chainName = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
|
|
218
623
|
for (const [symbol, { balance, decimals }] of Object.entries(
|
|
219
624
|
tokensBySymbol ?? {}
|
|
220
625
|
)) {
|
|
221
626
|
const amount = hexToDecimal(balance || "0", decimals);
|
|
222
627
|
totalBalance += amount;
|
|
223
628
|
tokens.push({
|
|
224
|
-
chain:
|
|
629
|
+
chain: chainName,
|
|
225
630
|
chainId: positionChainId,
|
|
226
631
|
asset: symbol,
|
|
227
632
|
amount: String(amount)
|
|
228
633
|
});
|
|
634
|
+
const detail = detailByKey.get(`${positionChainId}:${symbol.toUpperCase()}`);
|
|
635
|
+
positions.push({
|
|
636
|
+
chain: chainName,
|
|
637
|
+
protocol: detail?.protocol_name ?? detail?.protocol_id ?? "",
|
|
638
|
+
protocolId: detail?.protocol_id,
|
|
639
|
+
pool: detail?.pool,
|
|
640
|
+
asset: symbol,
|
|
641
|
+
amount: String(amount),
|
|
642
|
+
apy: detail?.pool_apy,
|
|
643
|
+
tvl: detail?.pool_tvl,
|
|
644
|
+
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
645
|
+
// structural read keeps it undefined today and auto-populates later.
|
|
646
|
+
liquidity: detail?.liquidity
|
|
647
|
+
});
|
|
229
648
|
}
|
|
230
649
|
}
|
|
231
|
-
const positions = (portfolio.positions ?? []).map((p) => ({
|
|
232
|
-
chain: p.chain ?? "",
|
|
233
|
-
protocol: p.protocol_name ?? p.protocol_id ?? "",
|
|
234
|
-
protocolId: p.protocol_id,
|
|
235
|
-
pool: p.pool,
|
|
236
|
-
asset: p.token_symbol ?? "",
|
|
237
|
-
// Position amount is hex-encoded base units (like the token balances
|
|
238
|
-
// above). `PositionSlot` doesn't type a `decimals` field, but runtime
|
|
239
|
-
// getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
|
|
240
|
-
// when present, otherwise fall back to the per-symbol lookup so
|
|
241
|
-
// non-6-decimal assets like WETH don't render 1e12 too large.
|
|
242
|
-
amount: String(
|
|
243
|
-
hexToDecimal(
|
|
244
|
-
p.amount || "0",
|
|
245
|
-
tokenDecimals(p.token_symbol, p.decimals)
|
|
246
|
-
)
|
|
247
|
-
),
|
|
248
|
-
apy: p.pool_apy,
|
|
249
|
-
tvl: p.pool_tvl,
|
|
250
|
-
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
251
|
-
// structural read keeps it undefined today and auto-populates later.
|
|
252
|
-
liquidity: p.liquidity
|
|
253
|
-
}));
|
|
254
650
|
return {
|
|
255
651
|
smartWallet,
|
|
256
652
|
totalBalance: String(totalBalance),
|
|
@@ -289,39 +685,41 @@ function mapEarnings(raw, smartWallet) {
|
|
|
289
685
|
};
|
|
290
686
|
}
|
|
291
687
|
function mapWeightedApyByChain(raw) {
|
|
292
|
-
if (!raw)
|
|
688
|
+
if (!raw)
|
|
689
|
+
return void 0;
|
|
293
690
|
const out = {};
|
|
294
691
|
for (const [chainKey, tokenApy] of Object.entries(raw)) {
|
|
295
692
|
const chainId = Number(chainKey);
|
|
296
|
-
if (!SUPPORTED_CHAIN_IDS.includes(chainId))
|
|
693
|
+
if (!SUPPORTED_CHAIN_IDS.includes(chainId))
|
|
694
|
+
continue;
|
|
297
695
|
const perAsset = {};
|
|
298
696
|
for (const [symbol, apy] of Object.entries(tokenApy)) {
|
|
299
697
|
const token = symbol;
|
|
300
|
-
if (!SUPPORTED_TOKENS.includes(token))
|
|
698
|
+
if (!SUPPORTED_TOKENS.includes(token))
|
|
699
|
+
continue;
|
|
301
700
|
perAsset[token] = apy;
|
|
302
701
|
}
|
|
303
|
-
if (Object.keys(perAsset).length > 0)
|
|
702
|
+
if (Object.keys(perAsset).length > 0)
|
|
703
|
+
out[chainId] = perAsset;
|
|
304
704
|
}
|
|
305
705
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
306
706
|
}
|
|
307
|
-
function rawPoolApyForChain(entry, chainId
|
|
707
|
+
function rawPoolApyForChain(entry, chainId) {
|
|
308
708
|
let weightedSum = 0;
|
|
309
709
|
let totalBalance = 0;
|
|
310
710
|
for (const p of entry.positions ?? []) {
|
|
311
|
-
if (p.chainId !== chainId)
|
|
312
|
-
|
|
711
|
+
if (p.chainId !== chainId)
|
|
712
|
+
continue;
|
|
313
713
|
const balance = p.balance ?? 0;
|
|
314
|
-
if (balance <= 0 || typeof p.apy !== "number")
|
|
714
|
+
if (balance <= 0 || typeof p.apy !== "number")
|
|
715
|
+
continue;
|
|
315
716
|
weightedSum += p.apy * balance;
|
|
316
717
|
totalBalance += balance;
|
|
317
718
|
}
|
|
318
719
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
319
720
|
}
|
|
320
|
-
function mapApyHistory(raw, chainId
|
|
321
|
-
const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
|
|
322
|
-
date,
|
|
323
|
-
apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
|
|
324
|
-
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
721
|
+
function mapApyHistory(raw, chainId) {
|
|
722
|
+
const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({ date, apy: rawPoolApyForChain(entry, chainId) })).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
325
723
|
return {
|
|
326
724
|
walletAddress: raw.walletAddress,
|
|
327
725
|
weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
|
|
@@ -344,7 +742,6 @@ function mapEntries(rawEntries, chainId) {
|
|
|
344
742
|
"Withdraw from protocol": "Withdraw",
|
|
345
743
|
Rebalance: "Rebalance"
|
|
346
744
|
};
|
|
347
|
-
void chainId;
|
|
348
745
|
return rawEntries.map((entry) => {
|
|
349
746
|
const rawLog = entry.rebalanceLog;
|
|
350
747
|
const positions = entry.positions ?? [];
|
|
@@ -366,15 +763,7 @@ function mapEntries(rawEntries, chainId) {
|
|
|
366
763
|
fromPool: from.pool,
|
|
367
764
|
toPool: to.pool,
|
|
368
765
|
tokenSymbol: matched?.token_symbol ?? "",
|
|
369
|
-
amount: matched ? String(
|
|
370
|
-
hexToDecimal(
|
|
371
|
-
matched.amount || "0x",
|
|
372
|
-
tokenDecimals(
|
|
373
|
-
matched.token_symbol,
|
|
374
|
-
matched.decimals
|
|
375
|
-
)
|
|
376
|
-
)
|
|
377
|
-
) : "",
|
|
766
|
+
amount: matched ? String(hexToDecimal(matched.amount || "0x")) : "",
|
|
378
767
|
status: "success"
|
|
379
768
|
}
|
|
380
769
|
];
|
|
@@ -393,12 +782,7 @@ function mapEntries(rawEntries, chainId) {
|
|
|
393
782
|
txHashes: entry.transactionHash ? [entry.transactionHash] : [],
|
|
394
783
|
chainId: entry.chainId,
|
|
395
784
|
tokenSymbol: entry?.positions?.[0]?.token_symbol,
|
|
396
|
-
amount: hasAmount ? String(
|
|
397
|
-
hexToDecimal(
|
|
398
|
-
rawAmount,
|
|
399
|
-
tokenDecimals(entry?.positions?.[0]?.token_symbol)
|
|
400
|
-
)
|
|
401
|
-
) : void 0
|
|
785
|
+
amount: hasAmount ? String(hexToDecimal(rawAmount)) : void 0
|
|
402
786
|
}
|
|
403
787
|
],
|
|
404
788
|
rebalanceLog
|
|
@@ -423,7 +807,8 @@ function mapApyByStrategy(raw) {
|
|
|
423
807
|
const supported = SupportedAssets.find(
|
|
424
808
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
425
809
|
);
|
|
426
|
-
if (!supported)
|
|
810
|
+
if (!supported)
|
|
811
|
+
continue;
|
|
427
812
|
const chainKey = supported.chainId;
|
|
428
813
|
const symbolKey = supported.symbol;
|
|
429
814
|
const bucket = apyPerAsset[chainKey] ?? {};
|
|
@@ -453,8 +838,10 @@ function computeAllocationApy(positions) {
|
|
|
453
838
|
totalValue += value;
|
|
454
839
|
const chainId = resolveChainId(p.chain);
|
|
455
840
|
const token = p.asset;
|
|
456
|
-
if (!SUPPORTED_CHAIN_IDS.includes(chainId))
|
|
457
|
-
|
|
841
|
+
if (!SUPPORTED_CHAIN_IDS.includes(chainId))
|
|
842
|
+
continue;
|
|
843
|
+
if (!SUPPORTED_TOKENS.includes(token))
|
|
844
|
+
continue;
|
|
458
845
|
const perAsset = buckets[chainId] ?? {};
|
|
459
846
|
const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
|
|
460
847
|
cell.weightedSum += apy * value;
|
|
@@ -471,7 +858,8 @@ function computeAllocationApy(positions) {
|
|
|
471
858
|
out[token] = cell.weightedSum / cell.value;
|
|
472
859
|
}
|
|
473
860
|
}
|
|
474
|
-
if (Object.keys(out).length > 0)
|
|
861
|
+
if (Object.keys(out).length > 0)
|
|
862
|
+
apyByChainAndAsset[chainId] = out;
|
|
475
863
|
}
|
|
476
864
|
const totalApy = totalValue > 0 ? String(weightedSum / totalValue) : "0";
|
|
477
865
|
return { totalApy, apyByChainAndAsset };
|
|
@@ -488,14 +876,19 @@ function extractWithdrawnAmount(logs, recipient, token, decimals) {
|
|
|
488
876
|
let total = 0n;
|
|
489
877
|
let matched = false;
|
|
490
878
|
for (const log of logs) {
|
|
491
|
-
if (log.address.toLowerCase() !== wantToken)
|
|
492
|
-
|
|
493
|
-
if (
|
|
494
|
-
|
|
879
|
+
if (log.address.toLowerCase() !== wantToken)
|
|
880
|
+
continue;
|
|
881
|
+
if ((log.topics[0] ?? "").toLowerCase() !== TRANSFER_TOPIC)
|
|
882
|
+
continue;
|
|
883
|
+
if (topicToAddress(log.topics[2]) !== wantRecipient)
|
|
884
|
+
continue;
|
|
885
|
+
if (!log.data || log.data === "0x")
|
|
886
|
+
continue;
|
|
495
887
|
total += BigInt(log.data);
|
|
496
888
|
matched = true;
|
|
497
889
|
}
|
|
498
|
-
if (!matched)
|
|
890
|
+
if (!matched)
|
|
891
|
+
return void 0;
|
|
499
892
|
return String(hexToDecimal(`0x${total.toString(16)}`, decimals));
|
|
500
893
|
}
|
|
501
894
|
|
|
@@ -511,7 +904,8 @@ function encodeHistoryCursor(payload) {
|
|
|
511
904
|
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
|
|
512
905
|
}
|
|
513
906
|
function decodeHistoryCursor(token) {
|
|
514
|
-
if (!token)
|
|
907
|
+
if (!token)
|
|
908
|
+
throw new InvalidHistoryCursorError("Cursor is empty.");
|
|
515
909
|
let parsed;
|
|
516
910
|
try {
|
|
517
911
|
const json = Buffer.from(token, "base64").toString("utf8");
|
|
@@ -525,10 +919,14 @@ function decodeHistoryCursor(token) {
|
|
|
525
919
|
return parsed;
|
|
526
920
|
}
|
|
527
921
|
|
|
922
|
+
// src/agents/zyfai/zyfai.agent.ts
|
|
923
|
+
init_errors();
|
|
924
|
+
|
|
528
925
|
// src/agents/zyfai/zyfai.auth-cache.ts
|
|
529
926
|
var KEY_PREFIX = "owney.zyfai.session";
|
|
530
927
|
var storage = () => {
|
|
531
|
-
if (typeof window === "undefined")
|
|
928
|
+
if (typeof window === "undefined")
|
|
929
|
+
return null;
|
|
532
930
|
try {
|
|
533
931
|
return window.localStorage;
|
|
534
932
|
} catch {
|
|
@@ -538,13 +936,15 @@ var storage = () => {
|
|
|
538
936
|
var buildKey = (address, chainId) => `${KEY_PREFIX}:${address.toLowerCase()}:${chainId}`;
|
|
539
937
|
var isJwtExpired = (token) => {
|
|
540
938
|
const parts = token.split(".");
|
|
541
|
-
if (parts.length !== 3)
|
|
939
|
+
if (parts.length !== 3)
|
|
940
|
+
return false;
|
|
542
941
|
try {
|
|
543
942
|
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
544
943
|
const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
|
|
545
944
|
const decoded = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8");
|
|
546
945
|
const claims = JSON.parse(decoded);
|
|
547
|
-
if (typeof claims.exp !== "number")
|
|
946
|
+
if (typeof claims.exp !== "number")
|
|
947
|
+
return false;
|
|
548
948
|
return Date.now() / 1e3 >= claims.exp - 30;
|
|
549
949
|
} catch {
|
|
550
950
|
return false;
|
|
@@ -552,9 +952,11 @@ var isJwtExpired = (token) => {
|
|
|
552
952
|
};
|
|
553
953
|
var readSession = (address, chainId) => {
|
|
554
954
|
const store = storage();
|
|
555
|
-
if (!store)
|
|
955
|
+
if (!store)
|
|
956
|
+
return null;
|
|
556
957
|
const raw = store.getItem(buildKey(address, chainId));
|
|
557
|
-
if (!raw)
|
|
958
|
+
if (!raw)
|
|
959
|
+
return null;
|
|
558
960
|
try {
|
|
559
961
|
const parsed = JSON.parse(raw);
|
|
560
962
|
if (!parsed.token || isJwtExpired(parsed.token)) {
|
|
@@ -569,7 +971,8 @@ var readSession = (address, chainId) => {
|
|
|
569
971
|
};
|
|
570
972
|
var writeSession = (address, chainId, session) => {
|
|
571
973
|
const store = storage();
|
|
572
|
-
if (!store)
|
|
974
|
+
if (!store)
|
|
975
|
+
return;
|
|
573
976
|
try {
|
|
574
977
|
store.setItem(buildKey(address, chainId), JSON.stringify(session));
|
|
575
978
|
} catch {
|
|
@@ -577,67 +980,33 @@ var writeSession = (address, chainId, session) => {
|
|
|
577
980
|
};
|
|
578
981
|
var clearSession = (address, chainId) => {
|
|
579
982
|
const store = storage();
|
|
580
|
-
if (!store)
|
|
983
|
+
if (!store)
|
|
984
|
+
return;
|
|
581
985
|
try {
|
|
582
986
|
store.removeItem(buildKey(address, chainId));
|
|
583
987
|
} catch {
|
|
584
988
|
}
|
|
585
989
|
};
|
|
586
990
|
|
|
587
|
-
// src/agents/zyfai/autoSelect.ts
|
|
588
|
-
function buildAutoSelectUpdate(current, asset) {
|
|
589
|
-
if (current.autoSelectProtocols === true) return null;
|
|
590
|
-
return {
|
|
591
|
-
asset,
|
|
592
|
-
autoSelectProtocols: true,
|
|
593
|
-
...current.chains ? { chains: current.chains } : {}
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
|
|
597
991
|
// src/agents/zyfai/zyfai.agent.ts
|
|
598
992
|
var DEFAULT_ZYFAI_RPC_URLS = {
|
|
599
993
|
8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
600
|
-
42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
|
|
601
|
-
1: "https://eth-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
|
|
994
|
+
42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
|
|
602
995
|
};
|
|
603
|
-
var
|
|
604
|
-
8453: "0x4200000000000000000000000000000000000006",
|
|
605
|
-
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
606
|
-
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
607
|
-
};
|
|
608
|
-
var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum, 1: import_chains.mainnet };
|
|
996
|
+
var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum };
|
|
609
997
|
var ZyfaiAgent = class {
|
|
610
998
|
id = "zyfai";
|
|
611
|
-
supportedChainIds = [8453, 42161
|
|
999
|
+
supportedChainIds = [8453, 42161];
|
|
612
1000
|
supportedAssets = [
|
|
613
1001
|
{
|
|
614
1002
|
chainId: 8453,
|
|
615
1003
|
chain: "BASE",
|
|
616
|
-
assets: [
|
|
617
|
-
{ symbol: "USDC", minDepositAmount: "1500000" },
|
|
618
|
-
{ symbol: "WETH", minDepositAmount: "500000000000000" }
|
|
619
|
-
]
|
|
1004
|
+
assets: [{ symbol: "USDC", minDepositAmount: "1500000" }]
|
|
620
1005
|
},
|
|
621
1006
|
{
|
|
622
1007
|
chainId: 42161,
|
|
623
1008
|
chain: "ARBITRUM",
|
|
624
|
-
assets: [
|
|
625
|
-
{ symbol: "USDC", minDepositAmount: "1500000" },
|
|
626
|
-
{ symbol: "WETH", minDepositAmount: "500000000000000" }
|
|
627
|
-
]
|
|
628
|
-
},
|
|
629
|
-
{
|
|
630
|
-
chainId: 1,
|
|
631
|
-
chain: "ETHEREUM",
|
|
632
|
-
// Zyfai enforces per-asset minimum portfolios on Ethereum mainnet, keyed
|
|
633
|
-
// by MIN_PORTFOLIO_BALANCE[1] in @zyfai/sdk (mainnet gas only pays off on
|
|
634
|
-
// large positions). USDC: 10,000 (10_000 * 1e6). WETH: 5 (5 * 1e18).
|
|
635
|
-
// NOTE: the @zyfai/sdk README claims WETH has no minimum — that's stale;
|
|
636
|
-
// the shipped code enforces 5 WETH, so match it or deposits reject on-chain.
|
|
637
|
-
assets: [
|
|
638
|
-
{ symbol: "USDC", minDepositAmount: "10000000000" },
|
|
639
|
-
{ symbol: "WETH", minDepositAmount: "5000000000000000000" }
|
|
640
|
-
]
|
|
1009
|
+
assets: [{ symbol: "USDC", minDepositAmount: "1500000" }]
|
|
641
1010
|
}
|
|
642
1011
|
];
|
|
643
1012
|
sdk;
|
|
@@ -661,7 +1030,8 @@ var ZyfaiAgent = class {
|
|
|
661
1030
|
}
|
|
662
1031
|
getPublicClient(chainId) {
|
|
663
1032
|
const cached = this.publicClients.get(chainId);
|
|
664
|
-
if (cached)
|
|
1033
|
+
if (cached)
|
|
1034
|
+
return cached;
|
|
665
1035
|
const client = (0, import_viem.createPublicClient)({
|
|
666
1036
|
chain: VIEM_CHAIN[chainId],
|
|
667
1037
|
transport: (0, import_viem.http)(this.rpcUrls[chainId])
|
|
@@ -671,43 +1041,33 @@ var ZyfaiAgent = class {
|
|
|
671
1041
|
}
|
|
672
1042
|
/**
|
|
673
1043
|
* Fills in the true withdrawn amount for `Withdraw` entries by decoding the
|
|
674
|
-
*
|
|
1044
|
+
* USDC `Transfer` that lands in the user's wallet on-chain. The Zyfai history
|
|
675
1045
|
* payload only reports the *remaining* position for a partial withdraw (see
|
|
676
1046
|
* the note in `mapEntries`), so without this the UI shows the leftover
|
|
677
1047
|
* balance instead of the amount withdrawn.
|
|
678
1048
|
*
|
|
679
|
-
* The withdrawn asset varies per entry (USDC or WETH), so it's resolved
|
|
680
|
-
* from `tx.tokenSymbol` (set by `mapEntries` from the entry's own
|
|
681
|
-
* position) rather than assumed to always be USDC. Zyfai sometimes
|
|
682
|
-
* denominates WETH as lowercase "eth" — that's normalized to WETH.
|
|
683
|
-
* Entries with no symbol (older payload shapes) fall back to USDC to
|
|
684
|
-
* preserve prior behavior.
|
|
685
|
-
*
|
|
686
1049
|
* Mutates `entries` in place. Runs all lookups concurrently and is tolerant
|
|
687
1050
|
* of RPC failures: an entry whose amount can't be resolved keeps
|
|
688
1051
|
* `amount: undefined`, which consumers render as a hidden/retryable row
|
|
689
1052
|
* rather than a wrong number.
|
|
690
1053
|
*/
|
|
691
1054
|
async resolveWithdrawnAmounts(entries, chainId, recipient) {
|
|
1055
|
+
const asset = SupportedAssets.find(
|
|
1056
|
+
(a) => a.chainId === chainId && a.symbol === "USDC"
|
|
1057
|
+
);
|
|
1058
|
+
if (!asset)
|
|
1059
|
+
return;
|
|
692
1060
|
const withdrawals = entries.filter(
|
|
693
1061
|
(e) => e.action === "Withdraw" && e.transactions[0]?.txHashes[0]
|
|
694
1062
|
);
|
|
695
|
-
if (withdrawals.length === 0)
|
|
1063
|
+
if (withdrawals.length === 0)
|
|
1064
|
+
return;
|
|
696
1065
|
const client = this.getPublicClient(chainId);
|
|
697
1066
|
await Promise.all(
|
|
698
1067
|
withdrawals.map(async (entry) => {
|
|
699
1068
|
const tx = entry.transactions[0];
|
|
700
1069
|
const txHash = tx.txHashes[0];
|
|
701
|
-
const
|
|
702
|
-
const normalizedSymbol = rawSymbol ? rawSymbol.toLowerCase() === "eth" ? "WETH" : rawSymbol.toUpperCase() : "USDC";
|
|
703
|
-
const asset = SupportedAssets.find(
|
|
704
|
-
(a) => a.chainId === chainId && a.symbol === normalizedSymbol
|
|
705
|
-
) ?? SupportedAssets.find(
|
|
706
|
-
(a) => a.chainId === chainId && a.symbol === "USDC"
|
|
707
|
-
);
|
|
708
|
-
if (!asset) return;
|
|
709
|
-
const cacheKey = `${txHash}:${asset.address}`;
|
|
710
|
-
const cached = this.withdrawAmountCache.get(cacheKey);
|
|
1070
|
+
const cached = this.withdrawAmountCache.get(txHash);
|
|
711
1071
|
if (cached !== void 0) {
|
|
712
1072
|
tx.amount = cached;
|
|
713
1073
|
return;
|
|
@@ -723,7 +1083,7 @@ var ZyfaiAgent = class {
|
|
|
723
1083
|
asset.decimals
|
|
724
1084
|
);
|
|
725
1085
|
if (amount !== void 0) {
|
|
726
|
-
this.withdrawAmountCache.set(
|
|
1086
|
+
this.withdrawAmountCache.set(txHash, amount);
|
|
727
1087
|
tx.amount = amount;
|
|
728
1088
|
}
|
|
729
1089
|
} catch {
|
|
@@ -742,11 +1102,13 @@ var ZyfaiAgent = class {
|
|
|
742
1102
|
* for some apps and the patch only matters in the browser anyway.
|
|
743
1103
|
*/
|
|
744
1104
|
installAuthCache() {
|
|
745
|
-
if (typeof window === "undefined")
|
|
1105
|
+
if (typeof window === "undefined")
|
|
1106
|
+
return;
|
|
746
1107
|
const internals = this.sdk;
|
|
747
1108
|
const original = internals.authenticateUser.bind(internals);
|
|
748
1109
|
internals.authenticateUser = async () => {
|
|
749
|
-
if (internals.authenticatedUserId !== null)
|
|
1110
|
+
if (internals.authenticatedUserId !== null)
|
|
1111
|
+
return;
|
|
750
1112
|
const address = internals.walletClient?.account?.address;
|
|
751
1113
|
const chainId = internals.currentChainId;
|
|
752
1114
|
if (address && chainId) {
|
|
@@ -821,10 +1183,12 @@ var ZyfaiAgent = class {
|
|
|
821
1183
|
return await this.sdk.connectAccount(provider, chainId);
|
|
822
1184
|
} catch (error) {
|
|
823
1185
|
const message = error instanceof Error ? error.message : String(error);
|
|
824
|
-
if (!message.toLowerCase().includes("unauthorized"))
|
|
1186
|
+
if (!message.toLowerCase().includes("unauthorized"))
|
|
1187
|
+
throw error;
|
|
825
1188
|
const internals = this.sdk;
|
|
826
1189
|
const address = internals.walletClient?.account?.address;
|
|
827
|
-
if (address)
|
|
1190
|
+
if (address)
|
|
1191
|
+
clearSession(address, chainId);
|
|
828
1192
|
return await this.sdk.connectAccount(provider, chainId);
|
|
829
1193
|
}
|
|
830
1194
|
}
|
|
@@ -837,22 +1201,6 @@ var ZyfaiAgent = class {
|
|
|
837
1201
|
this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
|
|
838
1202
|
if (!wallet.isDeployed) {
|
|
839
1203
|
await this.sdk.deploySafe(address, validChainId, "aggressive", true);
|
|
840
|
-
try {
|
|
841
|
-
if (validChainId === 42161) {
|
|
842
|
-
await this.sdk.updateUserProfile({
|
|
843
|
-
asset: "WETH",
|
|
844
|
-
chains: [validChainId],
|
|
845
|
-
protocols: ["c24a33fa-4a87-4376-b756-93bd3957d3ea"],
|
|
846
|
-
// AAVE protocol ID
|
|
847
|
-
autoSelectProtocols: false
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
} catch (err) {
|
|
851
|
-
console.warn(
|
|
852
|
-
"Failed to configure AAVE protocol after Safe deployment:",
|
|
853
|
-
err instanceof Error ? err.message : String(err)
|
|
854
|
-
);
|
|
855
|
-
}
|
|
856
1204
|
}
|
|
857
1205
|
const user = await this.sdk.getUserDetails();
|
|
858
1206
|
if (!user.hasActiveSessionKey) {
|
|
@@ -894,7 +1242,7 @@ var ZyfaiAgent = class {
|
|
|
894
1242
|
throw error;
|
|
895
1243
|
}
|
|
896
1244
|
}
|
|
897
|
-
async deposit(state, chainId, amount,
|
|
1245
|
+
async deposit(state, chainId, amount, depositCallback) {
|
|
898
1246
|
try {
|
|
899
1247
|
await this.connectFull(state, chainId);
|
|
900
1248
|
const validChainId = isValidChainId(chainId);
|
|
@@ -908,15 +1256,13 @@ var ZyfaiAgent = class {
|
|
|
908
1256
|
validChainId,
|
|
909
1257
|
amount
|
|
910
1258
|
);
|
|
911
|
-
|
|
912
|
-
await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
|
|
1259
|
+
await this.sdk.logDeposit(validChainId, txHash, amount);
|
|
913
1260
|
return { txHash, smartWallet: wallet.address, amount };
|
|
914
1261
|
}
|
|
915
1262
|
const raw = await this.sdk.depositFunds(
|
|
916
1263
|
this.getAddress(),
|
|
917
1264
|
validChainId,
|
|
918
|
-
amount
|
|
919
|
-
asset
|
|
1265
|
+
amount
|
|
920
1266
|
);
|
|
921
1267
|
return mapDeposit(raw);
|
|
922
1268
|
} catch (error) {
|
|
@@ -954,10 +1300,10 @@ var ZyfaiAgent = class {
|
|
|
954
1300
|
const raw = await this.sdk.getOnchainEarnings(smartWallet);
|
|
955
1301
|
return mapEarnings(raw, smartWallet);
|
|
956
1302
|
}
|
|
957
|
-
async getAccountApy(state, chainId, days
|
|
1303
|
+
async getAccountApy(state, chainId, days) {
|
|
958
1304
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
959
1305
|
const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
960
|
-
return mapApyHistory(raw, chainId
|
|
1306
|
+
return mapApyHistory(raw, chainId);
|
|
961
1307
|
}
|
|
962
1308
|
async getHistory(state, chainId, options) {
|
|
963
1309
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -1012,20 +1358,6 @@ var ZyfaiAgent = class {
|
|
|
1012
1358
|
}
|
|
1013
1359
|
return mapUserProfile(raw, this.connectedAddress);
|
|
1014
1360
|
}
|
|
1015
|
-
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
1016
|
-
await this.connectAuth(state, chainId);
|
|
1017
|
-
const current = await this.sdk.getUserDetails(asset);
|
|
1018
|
-
const update = buildAutoSelectUpdate(
|
|
1019
|
-
{
|
|
1020
|
-
autoSelectProtocols: current.autoSelectProtocols,
|
|
1021
|
-
chains: current.chains
|
|
1022
|
-
},
|
|
1023
|
-
asset
|
|
1024
|
-
);
|
|
1025
|
-
if (!update) return false;
|
|
1026
|
-
await this.sdk.updateUserProfile(update);
|
|
1027
|
-
return true;
|
|
1028
|
-
}
|
|
1029
1361
|
// --- IAgent: Discovery (no wallet required) ---
|
|
1030
1362
|
async getAgentApy(days, options) {
|
|
1031
1363
|
const raw = await this.sdk.getAPYPerStrategy(
|
|
@@ -1039,7 +1371,11 @@ var ZyfaiAgent = class {
|
|
|
1039
1371
|
}
|
|
1040
1372
|
};
|
|
1041
1373
|
|
|
1374
|
+
// src/client.ts
|
|
1375
|
+
init_errors();
|
|
1376
|
+
|
|
1042
1377
|
// src/lib/routing-api.ts
|
|
1378
|
+
init_errors();
|
|
1043
1379
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1044
1380
|
async function fetchAgentKeys(apiKey) {
|
|
1045
1381
|
const url = `${ROUTING_API_BASE_URL}/api/v1/agent/keys`;
|
|
@@ -1070,6 +1406,7 @@ async function fetchAgentKeys(apiKey) {
|
|
|
1070
1406
|
}
|
|
1071
1407
|
|
|
1072
1408
|
// src/lib/health-report.ts
|
|
1409
|
+
init_errors();
|
|
1073
1410
|
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1074
1411
|
async function reportAgentFailure(apiKey, agentType, errorCode) {
|
|
1075
1412
|
try {
|
|
@@ -1111,7 +1448,8 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
1111
1448
|
const tokenBalance = agentBalance?.tokens.find(
|
|
1112
1449
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
1113
1450
|
);
|
|
1114
|
-
if (!tokenBalance)
|
|
1451
|
+
if (!tokenBalance)
|
|
1452
|
+
return { agent, balance: 0n };
|
|
1115
1453
|
return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
|
|
1116
1454
|
});
|
|
1117
1455
|
}
|
|
@@ -1128,9 +1466,11 @@ function planProportionalShares(balances, requested, totalAvailable) {
|
|
|
1128
1466
|
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
1129
1467
|
});
|
|
1130
1468
|
for (const p of byHeadroom) {
|
|
1131
|
-
if (remainder === 0n)
|
|
1469
|
+
if (remainder === 0n)
|
|
1470
|
+
break;
|
|
1132
1471
|
const headroom = p.balance - p.planned;
|
|
1133
|
-
if (headroom <= 0n)
|
|
1472
|
+
if (headroom <= 0n)
|
|
1473
|
+
continue;
|
|
1134
1474
|
const take = headroom < remainder ? headroom : remainder;
|
|
1135
1475
|
p.planned += take;
|
|
1136
1476
|
remainder -= take;
|
|
@@ -1159,7 +1499,8 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
|
1159
1499
|
(s, c) => s + (c.balance - c.planned),
|
|
1160
1500
|
0n
|
|
1161
1501
|
);
|
|
1162
|
-
if (totalHeadroom === 0n)
|
|
1502
|
+
if (totalHeadroom === 0n)
|
|
1503
|
+
return;
|
|
1163
1504
|
let distributed = 0n;
|
|
1164
1505
|
for (const c of candidates) {
|
|
1165
1506
|
const headroom = c.balance - c.planned;
|
|
@@ -1170,9 +1511,11 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
|
1170
1511
|
}
|
|
1171
1512
|
let leftover = amount - distributed;
|
|
1172
1513
|
for (const c of candidates) {
|
|
1173
|
-
if (leftover === 0n)
|
|
1514
|
+
if (leftover === 0n)
|
|
1515
|
+
break;
|
|
1174
1516
|
const headroom = c.balance - c.planned;
|
|
1175
|
-
if (headroom <= 0n)
|
|
1517
|
+
if (headroom <= 0n)
|
|
1518
|
+
continue;
|
|
1176
1519
|
const take = headroom < leftover ? headroom : leftover;
|
|
1177
1520
|
c.planned += take;
|
|
1178
1521
|
leftover -= take;
|
|
@@ -1189,13 +1532,16 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
1189
1532
|
for (const id of Object.keys(agentApys)) {
|
|
1190
1533
|
const cells = agentApys[id].apyByChainAndAsset;
|
|
1191
1534
|
const balance = agentBalances[id] ?? 0;
|
|
1192
|
-
if (!cells || balance <= 0)
|
|
1535
|
+
if (!cells || balance <= 0)
|
|
1536
|
+
continue;
|
|
1193
1537
|
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
1194
|
-
if (!perAsset)
|
|
1538
|
+
if (!perAsset)
|
|
1539
|
+
continue;
|
|
1195
1540
|
const chainId = Number(chainKey);
|
|
1196
1541
|
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
1197
1542
|
const apy = Number(apyValue ?? 0);
|
|
1198
|
-
if (apy === 0)
|
|
1543
|
+
if (apy === 0)
|
|
1544
|
+
continue;
|
|
1199
1545
|
sums[chainId] ??= {};
|
|
1200
1546
|
weights[chainId] ??= {};
|
|
1201
1547
|
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
@@ -1209,7 +1555,8 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
1209
1555
|
const perAssetOut = {};
|
|
1210
1556
|
for (const asset of Object.keys(sums[chainId])) {
|
|
1211
1557
|
const w = weights[chainId][asset];
|
|
1212
|
-
if (w > 0)
|
|
1558
|
+
if (w > 0)
|
|
1559
|
+
perAssetOut[asset] = sums[chainId][asset] / w;
|
|
1213
1560
|
}
|
|
1214
1561
|
if (Object.keys(perAssetOut).length > 0) {
|
|
1215
1562
|
out[chainId] = perAssetOut;
|
|
@@ -1222,6 +1569,9 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
1222
1569
|
var import_viem5 = require("viem");
|
|
1223
1570
|
var import_chains2 = require("viem/chains");
|
|
1224
1571
|
|
|
1572
|
+
// src/lib/sponsored-deposit.ts
|
|
1573
|
+
init_errors();
|
|
1574
|
+
|
|
1225
1575
|
// src/lib/transfer-auth.ts
|
|
1226
1576
|
var import_viem3 = require("viem");
|
|
1227
1577
|
var ERC20_META_ABI = [
|
|
@@ -1259,6 +1609,7 @@ function randomAuthNonce() {
|
|
|
1259
1609
|
}
|
|
1260
1610
|
|
|
1261
1611
|
// src/lib/sponsor-client.ts
|
|
1612
|
+
init_errors();
|
|
1262
1613
|
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1263
1614
|
async function postSponsorTransferAuth(input) {
|
|
1264
1615
|
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
@@ -1266,10 +1617,7 @@ async function postSponsorTransferAuth(input) {
|
|
|
1266
1617
|
try {
|
|
1267
1618
|
res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
1268
1619
|
method: "POST",
|
|
1269
|
-
headers: {
|
|
1270
|
-
"content-type": "application/json",
|
|
1271
|
-
"x-owney-api-key": input.apiKey
|
|
1272
|
-
},
|
|
1620
|
+
headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
|
|
1273
1621
|
body: JSON.stringify(input.body)
|
|
1274
1622
|
});
|
|
1275
1623
|
} catch (networkError) {
|
|
@@ -1289,91 +1637,11 @@ async function postSponsorTransferAuth(input) {
|
|
|
1289
1637
|
throw new OwneyError(
|
|
1290
1638
|
"SPONSOR_REQUEST_FAILED",
|
|
1291
1639
|
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1292
|
-
{
|
|
1293
|
-
statusCode: res.status,
|
|
1294
|
-
responseBody: text.slice(0, 500),
|
|
1295
|
-
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
1296
|
-
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
1297
|
-
safeToFallback: res.status === 503
|
|
1298
|
-
}
|
|
1640
|
+
{ statusCode: res.status, responseBody: text.slice(0, 500) }
|
|
1299
1641
|
);
|
|
1300
1642
|
}
|
|
1301
1643
|
return parsed.data;
|
|
1302
1644
|
}
|
|
1303
|
-
async function postSponsorPermit2Transfer(input) {
|
|
1304
|
-
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
1305
|
-
let res;
|
|
1306
|
-
try {
|
|
1307
|
-
res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
|
|
1308
|
-
method: "POST",
|
|
1309
|
-
headers: {
|
|
1310
|
-
"content-type": "application/json",
|
|
1311
|
-
"x-owney-api-key": input.apiKey
|
|
1312
|
-
},
|
|
1313
|
-
body: JSON.stringify(input.body)
|
|
1314
|
-
});
|
|
1315
|
-
} catch (networkError) {
|
|
1316
|
-
throw new OwneyError(
|
|
1317
|
-
"SPONSOR_REQUEST_FAILED",
|
|
1318
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1319
|
-
{ cause: String(networkError), safeToFallback: false }
|
|
1320
|
-
);
|
|
1321
|
-
}
|
|
1322
|
-
const text = await res.text();
|
|
1323
|
-
let parsed = null;
|
|
1324
|
-
try {
|
|
1325
|
-
parsed = JSON.parse(text);
|
|
1326
|
-
} catch {
|
|
1327
|
-
}
|
|
1328
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
1329
|
-
throw new OwneyError(
|
|
1330
|
-
"SPONSOR_REQUEST_FAILED",
|
|
1331
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1332
|
-
{
|
|
1333
|
-
statusCode: res.status,
|
|
1334
|
-
responseBody: text.slice(0, 500),
|
|
1335
|
-
safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
|
|
1336
|
-
}
|
|
1337
|
-
);
|
|
1338
|
-
}
|
|
1339
|
-
return parsed.data;
|
|
1340
|
-
}
|
|
1341
|
-
async function getSponsorRelayerAddress(input) {
|
|
1342
|
-
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
1343
|
-
let res;
|
|
1344
|
-
try {
|
|
1345
|
-
res = await fetch(
|
|
1346
|
-
`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
1347
|
-
{
|
|
1348
|
-
headers: { "x-owney-api-key": input.apiKey }
|
|
1349
|
-
}
|
|
1350
|
-
);
|
|
1351
|
-
} catch (networkError) {
|
|
1352
|
-
throw new OwneyError(
|
|
1353
|
-
"SPONSOR_REQUEST_FAILED",
|
|
1354
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1355
|
-
{ cause: String(networkError), safeToFallback: true }
|
|
1356
|
-
);
|
|
1357
|
-
}
|
|
1358
|
-
const text = await res.text();
|
|
1359
|
-
let parsed = null;
|
|
1360
|
-
try {
|
|
1361
|
-
parsed = JSON.parse(text);
|
|
1362
|
-
} catch {
|
|
1363
|
-
}
|
|
1364
|
-
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
1365
|
-
throw new OwneyError(
|
|
1366
|
-
"SPONSOR_REQUEST_FAILED",
|
|
1367
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1368
|
-
{
|
|
1369
|
-
statusCode: res.status,
|
|
1370
|
-
responseBody: text.slice(0, 500),
|
|
1371
|
-
safeToFallback: true
|
|
1372
|
-
}
|
|
1373
|
-
);
|
|
1374
|
-
}
|
|
1375
|
-
return parsed.data.relayer;
|
|
1376
|
-
}
|
|
1377
1645
|
|
|
1378
1646
|
// src/lib/sponsored-deposit.ts
|
|
1379
1647
|
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
@@ -1425,114 +1693,6 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
1425
1693
|
};
|
|
1426
1694
|
}
|
|
1427
1695
|
|
|
1428
|
-
// src/lib/permit2.ts
|
|
1429
|
-
var import_viem4 = require("viem");
|
|
1430
|
-
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
1431
|
-
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
1432
|
-
var ERC20_ALLOWANCE_ABI = [
|
|
1433
|
-
{
|
|
1434
|
-
type: "function",
|
|
1435
|
-
name: "allowance",
|
|
1436
|
-
stateMutability: "view",
|
|
1437
|
-
inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }],
|
|
1438
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
1439
|
-
},
|
|
1440
|
-
{
|
|
1441
|
-
type: "function",
|
|
1442
|
-
name: "approve",
|
|
1443
|
-
stateMutability: "nonpayable",
|
|
1444
|
-
inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }],
|
|
1445
|
-
outputs: [{ name: "", type: "bool" }]
|
|
1446
|
-
}
|
|
1447
|
-
];
|
|
1448
|
-
function buildPermitTransferFromTypedData(input) {
|
|
1449
|
-
return {
|
|
1450
|
-
domain: { name: "Permit2", chainId: input.chainId, verifyingContract: PERMIT2_ADDRESS },
|
|
1451
|
-
types: {
|
|
1452
|
-
PermitTransferFrom: [
|
|
1453
|
-
{ name: "permitted", type: "TokenPermissions" },
|
|
1454
|
-
{ name: "spender", type: "address" },
|
|
1455
|
-
{ name: "nonce", type: "uint256" },
|
|
1456
|
-
{ name: "deadline", type: "uint256" }
|
|
1457
|
-
],
|
|
1458
|
-
TokenPermissions: [
|
|
1459
|
-
{ name: "token", type: "address" },
|
|
1460
|
-
{ name: "amount", type: "uint256" }
|
|
1461
|
-
]
|
|
1462
|
-
},
|
|
1463
|
-
primaryType: "PermitTransferFrom",
|
|
1464
|
-
message: input.message
|
|
1465
|
-
};
|
|
1466
|
-
}
|
|
1467
|
-
function randomPermit2Nonce() {
|
|
1468
|
-
const bytes = new Uint8Array(32);
|
|
1469
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
1470
|
-
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
1471
|
-
}
|
|
1472
|
-
async function readPermit2Allowance(publicClient, token, owner) {
|
|
1473
|
-
return publicClient.readContract({
|
|
1474
|
-
address: token,
|
|
1475
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
1476
|
-
functionName: "allowance",
|
|
1477
|
-
args: [owner, PERMIT2_ADDRESS]
|
|
1478
|
-
});
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
// src/lib/sponsored-weth-deposit.ts
|
|
1482
|
-
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
1483
|
-
function makeSponsoredWethCallback(deps) {
|
|
1484
|
-
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
1485
|
-
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
1486
|
-
return async (smartWallet, chainId, amount) => {
|
|
1487
|
-
const cid = chainId;
|
|
1488
|
-
const token = deps.tokenAddressByChain[cid];
|
|
1489
|
-
if (!token) {
|
|
1490
|
-
throw new OwneyError("CHAIN_UNSUPPORTED", `No sponsored WETH configured for chain ${chainId}`);
|
|
1491
|
-
}
|
|
1492
|
-
const amountWei = BigInt(amount);
|
|
1493
|
-
const pub = deps.getPublicClient(cid);
|
|
1494
|
-
const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
|
|
1495
|
-
if (allowance < amountWei) {
|
|
1496
|
-
throw new OwneyError(
|
|
1497
|
-
"PERMIT2_APPROVAL_REQUIRED",
|
|
1498
|
-
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
1499
|
-
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
1500
|
-
);
|
|
1501
|
-
}
|
|
1502
|
-
const relayer = await get({ baseUrl: deps.baseUrl, apiKey: deps.apiKey, chainId: cid });
|
|
1503
|
-
const nonce = randomPermit2Nonce();
|
|
1504
|
-
const deadline = BigInt(Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS);
|
|
1505
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
1506
|
-
chainId: cid,
|
|
1507
|
-
message: {
|
|
1508
|
-
permitted: { token, amount: amountWei },
|
|
1509
|
-
spender: relayer,
|
|
1510
|
-
nonce,
|
|
1511
|
-
deadline
|
|
1512
|
-
}
|
|
1513
|
-
});
|
|
1514
|
-
const wallet = deps.getWalletClient(cid);
|
|
1515
|
-
const signature = await wallet.signTypedData(
|
|
1516
|
-
{ account: deps.ownerAddress, ...typedData }
|
|
1517
|
-
);
|
|
1518
|
-
const result = await post({
|
|
1519
|
-
baseUrl: deps.baseUrl,
|
|
1520
|
-
apiKey: deps.apiKey,
|
|
1521
|
-
body: {
|
|
1522
|
-
chainId: cid,
|
|
1523
|
-
token,
|
|
1524
|
-
from: deps.ownerAddress,
|
|
1525
|
-
to: smartWallet,
|
|
1526
|
-
amount,
|
|
1527
|
-
nonce: nonce.toString(),
|
|
1528
|
-
deadline: deadline.toString(),
|
|
1529
|
-
signature
|
|
1530
|
-
}
|
|
1531
|
-
});
|
|
1532
|
-
return result.txHash;
|
|
1533
|
-
};
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
1696
|
// src/client.ts
|
|
1537
1697
|
function encodeMultiAgentCursor(map) {
|
|
1538
1698
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
@@ -1556,22 +1716,13 @@ function decodeMultiAgentCursor(token) {
|
|
|
1556
1716
|
}
|
|
1557
1717
|
var SPONSORED_USDC_BY_CHAIN = {
|
|
1558
1718
|
8453: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
|
|
1559
|
-
42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
|
|
1560
|
-
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
1719
|
+
42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
|
|
1561
1720
|
};
|
|
1562
1721
|
var VIEM_CHAIN2 = {
|
|
1563
1722
|
8453: import_chains2.base,
|
|
1564
|
-
42161: import_chains2.arbitrum
|
|
1565
|
-
1: import_chains2.mainnet
|
|
1566
|
-
};
|
|
1567
|
-
var SPONSORED_WETH_BY_CHAIN = {
|
|
1568
|
-
8453: "0x4200000000000000000000000000000000000006",
|
|
1569
|
-
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
1570
|
-
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
1723
|
+
42161: import_chains2.arbitrum
|
|
1571
1724
|
};
|
|
1572
|
-
|
|
1573
|
-
return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
|
|
1574
|
-
}
|
|
1725
|
+
var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
|
|
1575
1726
|
var OwneySDK = class {
|
|
1576
1727
|
agents = /* @__PURE__ */ new Map();
|
|
1577
1728
|
activeAgents = /* @__PURE__ */ new Set();
|
|
@@ -1584,14 +1735,11 @@ var OwneySDK = class {
|
|
|
1584
1735
|
state = null;
|
|
1585
1736
|
apiKey;
|
|
1586
1737
|
zyfaiRpcUrls;
|
|
1587
|
-
routingApiBaseUrl;
|
|
1588
1738
|
cachedSponsoredCallback = null;
|
|
1589
|
-
cachedWethSponsoredCallback = null;
|
|
1590
1739
|
initializingAgentsPromise = null;
|
|
1591
1740
|
constructor(config) {
|
|
1592
1741
|
this.apiKey = config.apiKey;
|
|
1593
1742
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
1594
|
-
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
1595
1743
|
}
|
|
1596
1744
|
/**
|
|
1597
1745
|
* Establish connection state for all subsequent SDK calls.
|
|
@@ -1612,7 +1760,6 @@ var OwneySDK = class {
|
|
|
1612
1760
|
}
|
|
1613
1761
|
this.state = { provider, walletAddress, chainId: null };
|
|
1614
1762
|
this.cachedSponsoredCallback = null;
|
|
1615
|
-
this.cachedWethSponsoredCallback = null;
|
|
1616
1763
|
}
|
|
1617
1764
|
/**
|
|
1618
1765
|
* Disconnect from all agents and clear connection state.
|
|
@@ -1626,7 +1773,6 @@ var OwneySDK = class {
|
|
|
1626
1773
|
this.disabledAgents.clear();
|
|
1627
1774
|
this.state = null;
|
|
1628
1775
|
this.cachedSponsoredCallback = null;
|
|
1629
|
-
this.cachedWethSponsoredCallback = null;
|
|
1630
1776
|
}
|
|
1631
1777
|
isAgentDisabled(agentId) {
|
|
1632
1778
|
return this.disabledAgents.has(agentId);
|
|
@@ -1646,7 +1792,8 @@ var OwneySDK = class {
|
|
|
1646
1792
|
return this.state?.chainId ?? null;
|
|
1647
1793
|
}
|
|
1648
1794
|
requireState() {
|
|
1649
|
-
if (!this.state)
|
|
1795
|
+
if (!this.state)
|
|
1796
|
+
throw new NotConnectedError();
|
|
1650
1797
|
return this.state;
|
|
1651
1798
|
}
|
|
1652
1799
|
requireChainId() {
|
|
@@ -1676,12 +1823,12 @@ var OwneySDK = class {
|
|
|
1676
1823
|
* `TransferWithAuthorization`, then POSTs to the sponsor API.
|
|
1677
1824
|
*/
|
|
1678
1825
|
getDefaultSponsoredCallback() {
|
|
1679
|
-
if (this.cachedSponsoredCallback)
|
|
1826
|
+
if (this.cachedSponsoredCallback)
|
|
1827
|
+
return this.cachedSponsoredCallback;
|
|
1680
1828
|
const provider = this.requireConnectedProvider();
|
|
1681
1829
|
const owner = this.state.walletAddress;
|
|
1682
1830
|
this.cachedSponsoredCallback = makeSponsoredDepositCallback({
|
|
1683
1831
|
apiKey: this.apiKey,
|
|
1684
|
-
baseUrl: this.routingApiBaseUrl,
|
|
1685
1832
|
ownerAddress: owner,
|
|
1686
1833
|
tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
|
|
1687
1834
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
@@ -1699,37 +1846,6 @@ var OwneySDK = class {
|
|
|
1699
1846
|
});
|
|
1700
1847
|
return this.cachedSponsoredCallback;
|
|
1701
1848
|
}
|
|
1702
|
-
/**
|
|
1703
|
-
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
1704
|
-
* callback used when the caller omits `depositCallback` for a WETH
|
|
1705
|
-
* deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
1706
|
-
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
1707
|
-
*/
|
|
1708
|
-
getDefaultWethSponsoredCallback() {
|
|
1709
|
-
if (this.cachedWethSponsoredCallback)
|
|
1710
|
-
return this.cachedWethSponsoredCallback;
|
|
1711
|
-
const provider = this.requireConnectedProvider();
|
|
1712
|
-
const owner = this.state.walletAddress;
|
|
1713
|
-
this.cachedWethSponsoredCallback = makeSponsoredWethCallback({
|
|
1714
|
-
apiKey: this.apiKey,
|
|
1715
|
-
baseUrl: this.routingApiBaseUrl,
|
|
1716
|
-
ownerAddress: owner,
|
|
1717
|
-
tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
|
|
1718
|
-
// Casts work around viem's chain-narrowed Client vs the generic
|
|
1719
|
-
// PublicClient/WalletClient param types — structurally identical at
|
|
1720
|
-
// runtime, but the two share a name TS treats as unrelated.
|
|
1721
|
-
getPublicClient: (cid) => (0, import_viem5.createPublicClient)({
|
|
1722
|
-
chain: VIEM_CHAIN2[cid],
|
|
1723
|
-
transport: (0, import_viem5.custom)(provider)
|
|
1724
|
-
}),
|
|
1725
|
-
getWalletClient: (cid) => (0, import_viem5.createWalletClient)({
|
|
1726
|
-
account: owner,
|
|
1727
|
-
chain: VIEM_CHAIN2[cid],
|
|
1728
|
-
transport: (0, import_viem5.custom)(provider)
|
|
1729
|
-
})
|
|
1730
|
-
});
|
|
1731
|
-
return this.cachedWethSponsoredCallback;
|
|
1732
|
-
}
|
|
1733
1849
|
getAgent(agentId) {
|
|
1734
1850
|
const agent = this.agents.get(agentId);
|
|
1735
1851
|
if (!agent) {
|
|
@@ -1741,19 +1857,22 @@ var OwneySDK = class {
|
|
|
1741
1857
|
const active = /* @__PURE__ */ new Map();
|
|
1742
1858
|
for (const id of this.activeAgents) {
|
|
1743
1859
|
const agent = this.agents.get(id);
|
|
1744
|
-
if (agent)
|
|
1860
|
+
if (agent)
|
|
1861
|
+
active.set(id, agent);
|
|
1745
1862
|
}
|
|
1746
1863
|
return active;
|
|
1747
1864
|
}
|
|
1748
1865
|
async ensureAgentsInitialized() {
|
|
1749
|
-
if (this.agents.size > 0)
|
|
1866
|
+
if (this.agents.size > 0)
|
|
1867
|
+
return;
|
|
1750
1868
|
if (!this.initializingAgentsPromise) {
|
|
1751
1869
|
this.initializingAgentsPromise = (async () => {
|
|
1752
1870
|
const agentKeys = await fetchAgentKeys(this.apiKey);
|
|
1753
1871
|
this.disabledAgents.clear();
|
|
1754
|
-
for (const { key, agent_type, is_enabled } of agentKeys) {
|
|
1755
|
-
const agent = this.createAgent(agent_type, key);
|
|
1756
|
-
if (!agent)
|
|
1872
|
+
for (const { key, agent_type, project_name, is_enabled } of agentKeys) {
|
|
1873
|
+
const agent = await this.createAgent(agent_type, key, project_name);
|
|
1874
|
+
if (!agent)
|
|
1875
|
+
continue;
|
|
1757
1876
|
this.agents.set(agent_type, agent);
|
|
1758
1877
|
if (is_enabled === false) {
|
|
1759
1878
|
this.disabledAgents.add(agent_type);
|
|
@@ -1773,10 +1892,16 @@ var OwneySDK = class {
|
|
|
1773
1892
|
this.initializingAgentsPromise = null;
|
|
1774
1893
|
}
|
|
1775
1894
|
}
|
|
1776
|
-
createAgent(agentId, key) {
|
|
1895
|
+
async createAgent(agentId, key, projectName) {
|
|
1777
1896
|
if (agentId === "zyfai") {
|
|
1778
1897
|
return new ZyfaiAgent(key, this.zyfaiRpcUrls);
|
|
1779
1898
|
}
|
|
1899
|
+
if (agentId === "surfliquid") {
|
|
1900
|
+
if (!projectName)
|
|
1901
|
+
return null;
|
|
1902
|
+
const { SurfLiquidAgent: SurfLiquidAgent2 } = await Promise.resolve().then(() => (init_surfliquid_agent(), surfliquid_agent_exports));
|
|
1903
|
+
return new SurfLiquidAgent2({ projectName, appId: key });
|
|
1904
|
+
}
|
|
1780
1905
|
return null;
|
|
1781
1906
|
}
|
|
1782
1907
|
// --- Account lifecycle ---
|
|
@@ -1880,7 +2005,7 @@ var OwneySDK = class {
|
|
|
1880
2005
|
}
|
|
1881
2006
|
const state = this.requireState();
|
|
1882
2007
|
const chainId = this.requireChainId();
|
|
1883
|
-
const effectiveCallback = depositCallback ??
|
|
2008
|
+
const effectiveCallback = depositCallback ?? this.getDefaultSponsoredCallback();
|
|
1884
2009
|
if (agentId) {
|
|
1885
2010
|
if (this.isAgentDisabled(agentId)) {
|
|
1886
2011
|
throw new OwneyError(
|
|
@@ -1892,14 +2017,10 @@ var OwneySDK = class {
|
|
|
1892
2017
|
const agent = this.getAgent(agentId);
|
|
1893
2018
|
this.validateAssetSupport(agent, chainId, asset);
|
|
1894
2019
|
await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
|
|
1895
|
-
return
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
chainId,
|
|
1899
|
-
amount,
|
|
1900
|
-
asset,
|
|
1901
|
-
effectiveCallback,
|
|
1902
|
-
depositCallback
|
|
2020
|
+
return withFailureReporting(
|
|
2021
|
+
this.apiKey,
|
|
2022
|
+
agent.id,
|
|
2023
|
+
() => agent.deposit(state, chainId, amount, effectiveCallback)
|
|
1903
2024
|
);
|
|
1904
2025
|
}
|
|
1905
2026
|
const totalAmount = BigInt(amount);
|
|
@@ -1929,77 +2050,26 @@ var OwneySDK = class {
|
|
|
1929
2050
|
);
|
|
1930
2051
|
}
|
|
1931
2052
|
const agentResults = {};
|
|
2053
|
+
const agentErrors = {};
|
|
1932
2054
|
for (const { agent, amount: agentAmount } of agentAmounts) {
|
|
1933
|
-
agentResults[agent.id] = await this.depositWithFallback(
|
|
1934
|
-
agent,
|
|
1935
|
-
state,
|
|
1936
|
-
chainId,
|
|
1937
|
-
agentAmount,
|
|
1938
|
-
asset,
|
|
1939
|
-
effectiveCallback,
|
|
1940
|
-
depositCallback
|
|
1941
|
-
);
|
|
1942
|
-
}
|
|
1943
|
-
return { agentResults };
|
|
1944
|
-
}
|
|
1945
|
-
/**
|
|
1946
|
-
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
1947
|
-
* two independent auto-recovery mechanisms:
|
|
1948
|
-
*
|
|
1949
|
-
* 1. Missing Permit2 allowance: when the app did not supply its own
|
|
1950
|
-
* callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
|
|
1951
|
-
* WETH deposit, this is the wallet's first gasless WETH deposit. We send
|
|
1952
|
-
* the one-time (user-paid) Permit2 approval via `approvePermit2()` and
|
|
1953
|
-
* retry the SAME sponsored attempt once. Bounded to one approval attempt
|
|
1954
|
-
* per call so a wallet/agent that keeps reporting the allowance as
|
|
1955
|
-
* missing can't loop forever. If `approvePermit2()` itself throws (e.g.
|
|
1956
|
-
* the user rejects the wallet prompt), that error propagates as-is —
|
|
1957
|
-
* the user said no to a transaction, so we must not turn around and ask
|
|
1958
|
-
* them to pay for a different one via the fallback below.
|
|
1959
|
-
* 2. `shouldFallbackToUserPaid`: the SDK's own WETH sponsor path failed at
|
|
1960
|
-
* the infrastructure layer in a provably safe-to-retry way, so we warn
|
|
1961
|
-
* and retry once with `undefined`, falling through to a user-paid
|
|
1962
|
-
* native deposit. This still applies after a successful Permit2
|
|
1963
|
-
* approval retry (the retried sponsored attempt can itself hit a safe
|
|
1964
|
-
* sponsor failure).
|
|
1965
|
-
*
|
|
1966
|
-
* Any other error — an app-supplied callback's own failure in particular —
|
|
1967
|
-
* propagates unchanged. Scoped per agent invocation so one agent's failure
|
|
1968
|
-
* doesn't force a retry of sibling agents in a multi-agent split.
|
|
1969
|
-
*/
|
|
1970
|
-
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback) {
|
|
1971
|
-
const attempt = (callback) => withFailureReporting(
|
|
1972
|
-
this.apiKey,
|
|
1973
|
-
agent.id,
|
|
1974
|
-
() => agent.deposit(
|
|
1975
|
-
state,
|
|
1976
|
-
chainId,
|
|
1977
|
-
amount,
|
|
1978
|
-
asset,
|
|
1979
|
-
callback
|
|
1980
|
-
)
|
|
1981
|
-
);
|
|
1982
|
-
let approvalAttempted = false;
|
|
1983
|
-
for (; ; ) {
|
|
1984
2055
|
try {
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
console.warn(
|
|
1990
|
-
"[owney-sdk] First WETH deposit: sending one-time Permit2 approval..."
|
|
1991
|
-
);
|
|
1992
|
-
await this.approvePermit2();
|
|
1993
|
-
continue;
|
|
1994
|
-
}
|
|
1995
|
-
if (!shouldFallbackToUserPaid(error, asset, appCallback)) throw error;
|
|
1996
|
-
console.warn(
|
|
1997
|
-
`[owney-sdk] ${asset} sponsorship unavailable, falling back to user-paid deposit:`,
|
|
1998
|
-
error
|
|
2056
|
+
agentResults[agent.id] = await withFailureReporting(
|
|
2057
|
+
this.apiKey,
|
|
2058
|
+
agent.id,
|
|
2059
|
+
() => agent.deposit(state, chainId, agentAmount, effectiveCallback)
|
|
1999
2060
|
);
|
|
2000
|
-
|
|
2061
|
+
} catch (error) {
|
|
2062
|
+
agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
|
|
2001
2063
|
}
|
|
2002
2064
|
}
|
|
2065
|
+
if (Object.keys(agentResults).length === 0) {
|
|
2066
|
+
throw new OwneyError(
|
|
2067
|
+
"DEPOSIT_ALL_FAILED",
|
|
2068
|
+
`All ${agentAmounts.length} agent deposit(s) failed.`,
|
|
2069
|
+
{ agentErrors }
|
|
2070
|
+
);
|
|
2071
|
+
}
|
|
2072
|
+
return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
|
|
2003
2073
|
}
|
|
2004
2074
|
getMinDepositAmount(agent, chainId, asset) {
|
|
2005
2075
|
const chainAssets = agent.supportedAssets.find(
|
|
@@ -2009,7 +2079,8 @@ var OwneySDK = class {
|
|
|
2009
2079
|
return BigInt(assetEntry?.minDepositAmount ?? "0");
|
|
2010
2080
|
}
|
|
2011
2081
|
splitDepositAmount(totalAmount, agents, chainId, asset, exempt = /* @__PURE__ */ new Set()) {
|
|
2012
|
-
if (agents.length === 0)
|
|
2082
|
+
if (agents.length === 0)
|
|
2083
|
+
return [];
|
|
2013
2084
|
const perAgent = totalAmount / BigInt(agents.length);
|
|
2014
2085
|
const remainder = totalAmount % BigInt(agents.length);
|
|
2015
2086
|
const splits = agents.map((agent, i) => ({
|
|
@@ -2033,8 +2104,10 @@ var OwneySDK = class {
|
|
|
2033
2104
|
async validateMinDepositAmount(agent, state, chainId, asset, amount) {
|
|
2034
2105
|
const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
|
|
2035
2106
|
const parsedAmount = BigInt(amount);
|
|
2036
|
-
if (parsedAmount >= minDepositAmount)
|
|
2037
|
-
|
|
2107
|
+
if (parsedAmount >= minDepositAmount)
|
|
2108
|
+
return;
|
|
2109
|
+
if (await this.hasExistingBalance(agent, state, chainId, asset))
|
|
2110
|
+
return;
|
|
2038
2111
|
throw new OwneyError(
|
|
2039
2112
|
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
2040
2113
|
`Amount "${amount}" is below the required minimum deposit "${minDepositAmount.toString()}" for agent "${agent.id}" on chain ${chainId} for asset "${asset}".`,
|
|
@@ -2097,6 +2170,34 @@ var OwneySDK = class {
|
|
|
2097
2170
|
}
|
|
2098
2171
|
return eligible;
|
|
2099
2172
|
}
|
|
2173
|
+
/**
|
|
2174
|
+
* Agent ids the routing API provisioned for this org that support the given
|
|
2175
|
+
* chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
|
|
2176
|
+
* surfliquid first). Returns `[]` when the org has no compatible agent — never
|
|
2177
|
+
* throws on an empty org. Loads agent keys on first call (apiKey only, no
|
|
2178
|
+
* wallet), so the UI can resolve which agent to use before the user connects.
|
|
2179
|
+
*
|
|
2180
|
+
* This is the source of truth for agent availability: an agent appears here
|
|
2181
|
+
* iff the routing API returned its key. No per-app feature flags.
|
|
2182
|
+
*/
|
|
2183
|
+
async getEligibleAgentIds(chainId, asset) {
|
|
2184
|
+
try {
|
|
2185
|
+
await this.ensureAgentsInitialized();
|
|
2186
|
+
} catch (error) {
|
|
2187
|
+
if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
|
|
2188
|
+
return [];
|
|
2189
|
+
}
|
|
2190
|
+
throw error;
|
|
2191
|
+
}
|
|
2192
|
+
return [...this.agents.values()].filter((agent) => {
|
|
2193
|
+
const chainAssets = agent.supportedAssets.find(
|
|
2194
|
+
(sa) => sa.chainId === chainId
|
|
2195
|
+
);
|
|
2196
|
+
return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
|
|
2197
|
+
}).map((agent) => agent.id).sort(
|
|
2198
|
+
(a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
|
|
2199
|
+
);
|
|
2200
|
+
}
|
|
2100
2201
|
// --- Fund operations ---
|
|
2101
2202
|
/**
|
|
2102
2203
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -2132,9 +2233,17 @@ var OwneySDK = class {
|
|
|
2132
2233
|
}
|
|
2133
2234
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
2134
2235
|
if (!amount) {
|
|
2236
|
+
const aggregated2 = await this.getBalances();
|
|
2237
|
+
const funded = projectAgentBalancesForAsset(
|
|
2238
|
+
eligibleAgents,
|
|
2239
|
+
aggregated2.agentBalances,
|
|
2240
|
+
chainId,
|
|
2241
|
+
asset,
|
|
2242
|
+
assetInfo.decimals
|
|
2243
|
+
).filter((b) => b.balance > 0n);
|
|
2135
2244
|
const results2 = {};
|
|
2136
2245
|
const agentErrors2 = {};
|
|
2137
|
-
for (const agent of
|
|
2246
|
+
for (const { agent } of funded) {
|
|
2138
2247
|
try {
|
|
2139
2248
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
2140
2249
|
} catch (err) {
|
|
@@ -2151,9 +2260,10 @@ var OwneySDK = class {
|
|
|
2151
2260
|
{ asset, agentErrors: agentErrors2 }
|
|
2152
2261
|
);
|
|
2153
2262
|
}
|
|
2263
|
+
const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
|
|
2154
2264
|
return {
|
|
2155
2265
|
agentResult: results2,
|
|
2156
|
-
totalWithdrawn:
|
|
2266
|
+
totalWithdrawn: totalWithdrawn.toString()
|
|
2157
2267
|
};
|
|
2158
2268
|
}
|
|
2159
2269
|
const requested = BigInt(amount);
|
|
@@ -2198,7 +2308,8 @@ var OwneySDK = class {
|
|
|
2198
2308
|
const agentErrors = {};
|
|
2199
2309
|
for (let i = 0; i < plans.length; i++) {
|
|
2200
2310
|
const p = plans[i];
|
|
2201
|
-
if (p.planned === 0n)
|
|
2311
|
+
if (p.planned === 0n)
|
|
2312
|
+
continue;
|
|
2202
2313
|
try {
|
|
2203
2314
|
results[p.agent.id] = await p.agent.withdraw(
|
|
2204
2315
|
state,
|
|
@@ -2332,30 +2443,19 @@ var OwneySDK = class {
|
|
|
2332
2443
|
*/
|
|
2333
2444
|
async getAccountApy({
|
|
2334
2445
|
agentId,
|
|
2335
|
-
days
|
|
2336
|
-
tokenSymbol
|
|
2446
|
+
days
|
|
2337
2447
|
}) {
|
|
2338
2448
|
const state = this.requireState();
|
|
2339
2449
|
const chainId = this.requireChainId();
|
|
2340
2450
|
if (agentId) {
|
|
2341
|
-
return this.getAgent(agentId).getAccountApy(
|
|
2342
|
-
state,
|
|
2343
|
-
chainId,
|
|
2344
|
-
days,
|
|
2345
|
-
tokenSymbol
|
|
2346
|
-
);
|
|
2451
|
+
return this.getAgent(agentId).getAccountApy(state, chainId, days);
|
|
2347
2452
|
}
|
|
2348
2453
|
const activeAgents = this.getActiveAgents();
|
|
2349
2454
|
const entries = [...activeAgents.entries()];
|
|
2350
2455
|
const [apyResults, balanceResults] = await Promise.all([
|
|
2351
2456
|
Promise.all(
|
|
2352
2457
|
entries.map(async ([id, agent]) => {
|
|
2353
|
-
const apy = await agent.getAccountApy(
|
|
2354
|
-
state,
|
|
2355
|
-
chainId,
|
|
2356
|
-
days,
|
|
2357
|
-
tokenSymbol
|
|
2358
|
-
);
|
|
2458
|
+
const apy = await agent.getAccountApy(state, chainId, days);
|
|
2359
2459
|
return [id, apy];
|
|
2360
2460
|
})
|
|
2361
2461
|
),
|
|
@@ -2378,13 +2478,15 @@ var OwneySDK = class {
|
|
|
2378
2478
|
let relevantBalance = 0;
|
|
2379
2479
|
for (const id of Object.keys(results)) {
|
|
2380
2480
|
const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
|
|
2381
|
-
if (agentApy === 0)
|
|
2481
|
+
if (agentApy === 0)
|
|
2482
|
+
continue;
|
|
2382
2483
|
relevantBalance += balances[id];
|
|
2383
2484
|
}
|
|
2384
2485
|
if (relevantBalance > 0) {
|
|
2385
2486
|
for (const id of Object.keys(results)) {
|
|
2386
2487
|
const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
|
|
2387
|
-
if (agentApy === 0)
|
|
2488
|
+
if (agentApy === 0)
|
|
2489
|
+
continue;
|
|
2388
2490
|
const weight = balances[id] / relevantBalance;
|
|
2389
2491
|
totalApy += agentApy * weight;
|
|
2390
2492
|
}
|
|
@@ -2439,7 +2541,8 @@ var OwneySDK = class {
|
|
|
2439
2541
|
const nextCursorMap = {};
|
|
2440
2542
|
let hasMore = false;
|
|
2441
2543
|
for (const { agentId: id, page } of perAgent) {
|
|
2442
|
-
if (!page)
|
|
2544
|
+
if (!page)
|
|
2545
|
+
continue;
|
|
2443
2546
|
merged.push(...page.data);
|
|
2444
2547
|
if (page.hasMore && page.nextCursor) {
|
|
2445
2548
|
nextCursorMap[id] = page.nextCursor;
|
|
@@ -2448,12 +2551,16 @@ var OwneySDK = class {
|
|
|
2448
2551
|
}
|
|
2449
2552
|
merged.sort((a, b) => {
|
|
2450
2553
|
const dateDiff = new Date(b.date).getTime() - new Date(a.date).getTime();
|
|
2451
|
-
if (dateDiff !== 0)
|
|
2452
|
-
|
|
2554
|
+
if (dateDiff !== 0)
|
|
2555
|
+
return dateDiff;
|
|
2556
|
+
if (a.agent !== b.agent)
|
|
2557
|
+
return a.agent < b.agent ? -1 : 1;
|
|
2453
2558
|
const aTx = a.transactions?.[0]?.txHashes?.[0] ?? "";
|
|
2454
2559
|
const bTx = b.transactions?.[0]?.txHashes?.[0] ?? "";
|
|
2455
|
-
if (aTx !== bTx)
|
|
2456
|
-
|
|
2560
|
+
if (aTx !== bTx)
|
|
2561
|
+
return aTx < bTx ? -1 : 1;
|
|
2562
|
+
if (a.action !== b.action)
|
|
2563
|
+
return a.action < b.action ? -1 : 1;
|
|
2457
2564
|
return 0;
|
|
2458
2565
|
});
|
|
2459
2566
|
return {
|
|
@@ -2488,67 +2595,6 @@ var OwneySDK = class {
|
|
|
2488
2595
|
agentUserProfile: results
|
|
2489
2596
|
};
|
|
2490
2597
|
}
|
|
2491
|
-
/**
|
|
2492
|
-
* Ensure the given asset uses backend protocol auto-selection for the active
|
|
2493
|
-
* chain's account. No-op (returns false) for agents that don't support it.
|
|
2494
|
-
* @param asset - "USDC" or "WETH".
|
|
2495
|
-
* @param agentId - Agent to target. Defaults to "zyfai".
|
|
2496
|
-
* @returns whether a profile write occurred.
|
|
2497
|
-
*/
|
|
2498
|
-
async ensureAutoSelectProtocols(asset, agentId = "zyfai") {
|
|
2499
|
-
const state = this.requireState();
|
|
2500
|
-
const chainId = this.requireChainId();
|
|
2501
|
-
const agent = this.getAgent(agentId);
|
|
2502
|
-
if (typeof agent.ensureAutoSelectProtocols !== "function") return false;
|
|
2503
|
-
return agent.ensureAutoSelectProtocols(state, chainId, asset);
|
|
2504
|
-
}
|
|
2505
|
-
/**
|
|
2506
|
-
* One-time, user-paid approval of Permit2 on the sponsored WETH token for
|
|
2507
|
-
* the active chain. Required once per wallet per chain before gasless WETH
|
|
2508
|
-
* deposits; afterwards deposit() is signature-only. Resolves only after the
|
|
2509
|
-
* approval transaction is mined (1 confirmation), so a subsequent deposit()
|
|
2510
|
-
* will see the new allowance; throws if the transaction reverted.
|
|
2511
|
-
* @returns the approval transaction hash.
|
|
2512
|
-
*/
|
|
2513
|
-
async approvePermit2(asset = "WETH") {
|
|
2514
|
-
void asset;
|
|
2515
|
-
const state = this.requireState();
|
|
2516
|
-
const chainId = this.requireChainId();
|
|
2517
|
-
this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
|
|
2518
|
-
const token = SPONSORED_WETH_BY_CHAIN[chainId];
|
|
2519
|
-
if (!token) {
|
|
2520
|
-
throw new OwneyError(
|
|
2521
|
-
"CHAIN_UNSUPPORTED",
|
|
2522
|
-
`No sponsored WETH on chain ${chainId}`
|
|
2523
|
-
);
|
|
2524
|
-
}
|
|
2525
|
-
const provider = this.requireConnectedProvider();
|
|
2526
|
-
const wallet = (0, import_viem5.createWalletClient)({
|
|
2527
|
-
account: state.walletAddress,
|
|
2528
|
-
chain: VIEM_CHAIN2[chainId],
|
|
2529
|
-
transport: (0, import_viem5.custom)(provider)
|
|
2530
|
-
});
|
|
2531
|
-
const hash = await wallet.writeContract({
|
|
2532
|
-
address: token,
|
|
2533
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2534
|
-
functionName: "approve",
|
|
2535
|
-
args: [PERMIT2_ADDRESS, MAX_UINT256],
|
|
2536
|
-
account: state.walletAddress,
|
|
2537
|
-
chain: VIEM_CHAIN2[chainId]
|
|
2538
|
-
});
|
|
2539
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
2540
|
-
chain: VIEM_CHAIN2[chainId],
|
|
2541
|
-
transport: (0, import_viem5.custom)(provider)
|
|
2542
|
-
});
|
|
2543
|
-
const receipt = await publicClient.waitForTransactionReceipt({
|
|
2544
|
-
hash,
|
|
2545
|
-
confirmations: 1
|
|
2546
|
-
});
|
|
2547
|
-
if (receipt.status !== "success") {
|
|
2548
|
-
throw new Error(`Permit2 approval reverted (tx ${hash})`);
|
|
2549
|
-
}
|
|
2550
|
-
return hash;
|
|
2551
|
-
}
|
|
2552
2598
|
// --- Discovery (no wallet required) ---
|
|
2553
2599
|
/**
|
|
2554
2600
|
* Get the agent's average APY performance over a time period. Does not require a wallet connection.
|
|
@@ -2646,6 +2692,9 @@ var OwneySDK = class {
|
|
|
2646
2692
|
}
|
|
2647
2693
|
};
|
|
2648
2694
|
|
|
2695
|
+
// src/index.ts
|
|
2696
|
+
init_errors();
|
|
2697
|
+
|
|
2649
2698
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
2650
2699
|
var import_viem6 = require("viem");
|
|
2651
2700
|
var import_siwe = require("siwe");
|
|
@@ -2654,7 +2703,8 @@ var import_sdk2 = require("@zyfai/sdk");
|
|
|
2654
2703
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
2655
2704
|
var KEY_PREFIX2 = "owney.siwx.session";
|
|
2656
2705
|
var storage2 = () => {
|
|
2657
|
-
if (typeof window === "undefined")
|
|
2706
|
+
if (typeof window === "undefined")
|
|
2707
|
+
return null;
|
|
2658
2708
|
try {
|
|
2659
2709
|
return window.localStorage;
|
|
2660
2710
|
} catch {
|
|
@@ -2664,9 +2714,11 @@ var storage2 = () => {
|
|
|
2664
2714
|
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2665
2715
|
var readSiwxSession = (address, chainId) => {
|
|
2666
2716
|
const store = storage2();
|
|
2667
|
-
if (!store)
|
|
2717
|
+
if (!store)
|
|
2718
|
+
return null;
|
|
2668
2719
|
const raw = store.getItem(buildKey2(address, chainId));
|
|
2669
|
-
if (!raw)
|
|
2720
|
+
if (!raw)
|
|
2721
|
+
return null;
|
|
2670
2722
|
try {
|
|
2671
2723
|
return JSON.parse(raw);
|
|
2672
2724
|
} catch {
|
|
@@ -2676,7 +2728,8 @@ var readSiwxSession = (address, chainId) => {
|
|
|
2676
2728
|
};
|
|
2677
2729
|
var writeSiwxSession = (address, chainId, session) => {
|
|
2678
2730
|
const store = storage2();
|
|
2679
|
-
if (!store)
|
|
2731
|
+
if (!store)
|
|
2732
|
+
return;
|
|
2680
2733
|
try {
|
|
2681
2734
|
store.setItem(buildKey2(address, chainId), JSON.stringify(session));
|
|
2682
2735
|
} catch {
|
|
@@ -2684,7 +2737,8 @@ var writeSiwxSession = (address, chainId, session) => {
|
|
|
2684
2737
|
};
|
|
2685
2738
|
var clearSiwxSession = (address, chainId) => {
|
|
2686
2739
|
const store = storage2();
|
|
2687
|
-
if (!store)
|
|
2740
|
+
if (!store)
|
|
2741
|
+
return;
|
|
2688
2742
|
try {
|
|
2689
2743
|
store.removeItem(buildKey2(address, chainId));
|
|
2690
2744
|
} catch {
|
|
@@ -2747,10 +2801,12 @@ function buildSIWXConfig(deps) {
|
|
|
2747
2801
|
const jwt = readSession(address, id);
|
|
2748
2802
|
const stored = readSiwxSession(address, id);
|
|
2749
2803
|
if (!jwt) {
|
|
2750
|
-
if (stored)
|
|
2804
|
+
if (stored)
|
|
2805
|
+
clearSiwxSession(address, id);
|
|
2751
2806
|
return [];
|
|
2752
2807
|
}
|
|
2753
|
-
if (!stored)
|
|
2808
|
+
if (!stored)
|
|
2809
|
+
return [];
|
|
2754
2810
|
const session = {
|
|
2755
2811
|
// `stored.data.chainId` is a plain string in the cache (kept AppKit-free);
|
|
2756
2812
|
// it always holds a valid CaipNetworkId here, so narrow at the boundary.
|