@owney/sdk 0.6.6-beta.1 → 0.6.6-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +699 -755
- package/dist/index.d.cts +144 -82
- package/dist/index.d.ts +144 -82
- package/dist/index.js +698 -265
- package/package.json +5 -4
- package/dist/chunk-GMNQBWUB.js +0 -55
- package/dist/surfliquid.agent-EPFISJPR.js +0 -390
package/dist/index.cjs
CHANGED
|
@@ -3,9 +3,6 @@ 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
|
-
};
|
|
9
6
|
var __export = (target, all) => {
|
|
10
7
|
for (var name in all)
|
|
11
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -20,486 +17,9 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
20
17
|
};
|
|
21
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
19
|
|
|
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
|
-
|
|
500
20
|
// src/index.ts
|
|
501
|
-
var
|
|
502
|
-
__export(
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
503
23
|
AgentChainIncompatibleError: () => AgentChainIncompatibleError,
|
|
504
24
|
AgentNotFoundError: () => AgentNotFoundError,
|
|
505
25
|
InvalidHistoryCursorError: () => InvalidHistoryCursorError,
|
|
@@ -508,7 +28,7 @@ __export(src_exports, {
|
|
|
508
28
|
OwneySDK: () => OwneySDK,
|
|
509
29
|
createOwneySIWX: () => createOwneySIWX
|
|
510
30
|
});
|
|
511
|
-
module.exports = __toCommonJS(
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
512
32
|
|
|
513
33
|
// src/agents/zyfai/zyfai.agent.ts
|
|
514
34
|
var import_sdk = require("@zyfai/sdk");
|
|
@@ -517,10 +37,10 @@ var import_chains = require("viem/chains");
|
|
|
517
37
|
|
|
518
38
|
// src/types/config.ts
|
|
519
39
|
var P_SUPPORTED_CHAINS = {
|
|
520
|
-
chainIds: [8453, 42161],
|
|
521
|
-
chains: ["BASE", "ARBITRUM"]
|
|
40
|
+
chainIds: [8453, 42161, 1],
|
|
41
|
+
chains: ["BASE", "ARBITRUM", "ETHEREUM"]
|
|
522
42
|
};
|
|
523
|
-
var SUPPORTED_TOKENS = ["USDC", "USDT"];
|
|
43
|
+
var SUPPORTED_TOKENS = ["USDC", "USDT", "WETH"];
|
|
524
44
|
var SUPPORTED_CHAIN_IDS = P_SUPPORTED_CHAINS.chainIds;
|
|
525
45
|
var SUPPORTED_CHAINS = P_SUPPORTED_CHAINS.chains;
|
|
526
46
|
var SupportedAssets = [
|
|
@@ -541,11 +61,83 @@ var SupportedAssets = [
|
|
|
541
61
|
symbol: "USDT",
|
|
542
62
|
chainId: 42161,
|
|
543
63
|
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
|
|
544
88
|
}
|
|
545
89
|
];
|
|
546
90
|
|
|
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
|
+
|
|
547
140
|
// src/lib/utils.ts
|
|
548
|
-
init_errors();
|
|
549
141
|
var isValidChainId = (chainId) => {
|
|
550
142
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
|
|
551
143
|
throw new OwneyError(
|
|
@@ -559,8 +151,7 @@ var isValidChainId = (chainId) => {
|
|
|
559
151
|
function hexToDecimal(hex, decimals = 6) {
|
|
560
152
|
const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
|
|
561
153
|
const parsed = BigInt(normalized);
|
|
562
|
-
if (decimals <= 0)
|
|
563
|
-
return Number(parsed);
|
|
154
|
+
if (decimals <= 0) return Number(parsed);
|
|
564
155
|
const divisor = BigInt(10 ** decimals);
|
|
565
156
|
const whole = parsed / divisor;
|
|
566
157
|
const remainder = parsed % divisor;
|
|
@@ -573,6 +164,20 @@ var DayFilterMapping = {
|
|
|
573
164
|
};
|
|
574
165
|
|
|
575
166
|
// 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
|
+
}
|
|
576
181
|
function mapDeposit(raw) {
|
|
577
182
|
return {
|
|
578
183
|
txHash: raw.txHash,
|
|
@@ -589,7 +194,8 @@ function mapWithdraw(raw) {
|
|
|
589
194
|
}
|
|
590
195
|
var CHAIN_ID_TO_NAME = {
|
|
591
196
|
8453: "BASE",
|
|
592
|
-
42161: "ARBITRUM"
|
|
197
|
+
42161: "ARBITRUM",
|
|
198
|
+
1: "ETHEREUM"
|
|
593
199
|
};
|
|
594
200
|
var NAME_TO_CHAIN_ID = Object.entries(
|
|
595
201
|
CHAIN_ID_TO_NAME
|
|
@@ -599,54 +205,52 @@ var NAME_TO_CHAIN_ID = Object.entries(
|
|
|
599
205
|
}, {});
|
|
600
206
|
function resolveChainId(chain) {
|
|
601
207
|
const asNum = Number(chain);
|
|
602
|
-
if (Number.isFinite(asNum) && asNum > 0)
|
|
603
|
-
return asNum;
|
|
208
|
+
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
604
209
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
605
210
|
}
|
|
606
211
|
function mapBalances(raw, _chainId, smartWallet) {
|
|
607
212
|
const portfolio = raw.portfolio;
|
|
608
213
|
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
|
-
}
|
|
617
214
|
let totalBalance = 0;
|
|
618
215
|
const tokens = [];
|
|
619
|
-
const positions = [];
|
|
620
216
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
|
|
621
217
|
const positionChainId = Number(chainIdKey);
|
|
622
|
-
const chainName = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
|
|
623
218
|
for (const [symbol, { balance, decimals }] of Object.entries(
|
|
624
219
|
tokensBySymbol ?? {}
|
|
625
220
|
)) {
|
|
626
221
|
const amount = hexToDecimal(balance || "0", decimals);
|
|
627
222
|
totalBalance += amount;
|
|
628
223
|
tokens.push({
|
|
629
|
-
chain:
|
|
224
|
+
chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
|
|
630
225
|
chainId: positionChainId,
|
|
631
226
|
asset: symbol,
|
|
632
227
|
amount: String(amount)
|
|
633
228
|
});
|
|
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
|
-
});
|
|
648
229
|
}
|
|
649
230
|
}
|
|
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
|
+
}));
|
|
650
254
|
return {
|
|
651
255
|
smartWallet,
|
|
652
256
|
totalBalance: String(totalBalance),
|
|
@@ -685,41 +289,39 @@ function mapEarnings(raw, smartWallet) {
|
|
|
685
289
|
};
|
|
686
290
|
}
|
|
687
291
|
function mapWeightedApyByChain(raw) {
|
|
688
|
-
if (!raw)
|
|
689
|
-
return void 0;
|
|
292
|
+
if (!raw) return void 0;
|
|
690
293
|
const out = {};
|
|
691
294
|
for (const [chainKey, tokenApy] of Object.entries(raw)) {
|
|
692
295
|
const chainId = Number(chainKey);
|
|
693
|
-
if (!SUPPORTED_CHAIN_IDS.includes(chainId))
|
|
694
|
-
continue;
|
|
296
|
+
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
695
297
|
const perAsset = {};
|
|
696
298
|
for (const [symbol, apy] of Object.entries(tokenApy)) {
|
|
697
299
|
const token = symbol;
|
|
698
|
-
if (!SUPPORTED_TOKENS.includes(token))
|
|
699
|
-
continue;
|
|
300
|
+
if (!SUPPORTED_TOKENS.includes(token)) continue;
|
|
700
301
|
perAsset[token] = apy;
|
|
701
302
|
}
|
|
702
|
-
if (Object.keys(perAsset).length > 0)
|
|
703
|
-
out[chainId] = perAsset;
|
|
303
|
+
if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
|
|
704
304
|
}
|
|
705
305
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
706
306
|
}
|
|
707
|
-
function rawPoolApyForChain(entry, chainId) {
|
|
307
|
+
function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
708
308
|
let weightedSum = 0;
|
|
709
309
|
let totalBalance = 0;
|
|
710
310
|
for (const p of entry.positions ?? []) {
|
|
711
|
-
if (p.chainId !== chainId)
|
|
712
|
-
|
|
311
|
+
if (p.chainId !== chainId) continue;
|
|
312
|
+
if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
|
|
713
313
|
const balance = p.balance ?? 0;
|
|
714
|
-
if (balance <= 0 || typeof p.apy !== "number")
|
|
715
|
-
continue;
|
|
314
|
+
if (balance <= 0 || typeof p.apy !== "number") continue;
|
|
716
315
|
weightedSum += p.apy * balance;
|
|
717
316
|
totalBalance += balance;
|
|
718
317
|
}
|
|
719
318
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
720
319
|
}
|
|
721
|
-
function mapApyHistory(raw, chainId) {
|
|
722
|
-
const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
|
|
320
|
+
function mapApyHistory(raw, chainId, tokenSymbol) {
|
|
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));
|
|
723
325
|
return {
|
|
724
326
|
walletAddress: raw.walletAddress,
|
|
725
327
|
weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
|
|
@@ -742,6 +344,7 @@ function mapEntries(rawEntries, chainId) {
|
|
|
742
344
|
"Withdraw from protocol": "Withdraw",
|
|
743
345
|
Rebalance: "Rebalance"
|
|
744
346
|
};
|
|
347
|
+
void chainId;
|
|
745
348
|
return rawEntries.map((entry) => {
|
|
746
349
|
const rawLog = entry.rebalanceLog;
|
|
747
350
|
const positions = entry.positions ?? [];
|
|
@@ -763,7 +366,15 @@ function mapEntries(rawEntries, chainId) {
|
|
|
763
366
|
fromPool: from.pool,
|
|
764
367
|
toPool: to.pool,
|
|
765
368
|
tokenSymbol: matched?.token_symbol ?? "",
|
|
766
|
-
amount: matched ? String(
|
|
369
|
+
amount: matched ? String(
|
|
370
|
+
hexToDecimal(
|
|
371
|
+
matched.amount || "0x",
|
|
372
|
+
tokenDecimals(
|
|
373
|
+
matched.token_symbol,
|
|
374
|
+
matched.decimals
|
|
375
|
+
)
|
|
376
|
+
)
|
|
377
|
+
) : "",
|
|
767
378
|
status: "success"
|
|
768
379
|
}
|
|
769
380
|
];
|
|
@@ -782,7 +393,12 @@ function mapEntries(rawEntries, chainId) {
|
|
|
782
393
|
txHashes: entry.transactionHash ? [entry.transactionHash] : [],
|
|
783
394
|
chainId: entry.chainId,
|
|
784
395
|
tokenSymbol: entry?.positions?.[0]?.token_symbol,
|
|
785
|
-
amount: hasAmount ? String(
|
|
396
|
+
amount: hasAmount ? String(
|
|
397
|
+
hexToDecimal(
|
|
398
|
+
rawAmount,
|
|
399
|
+
tokenDecimals(entry?.positions?.[0]?.token_symbol)
|
|
400
|
+
)
|
|
401
|
+
) : void 0
|
|
786
402
|
}
|
|
787
403
|
],
|
|
788
404
|
rebalanceLog
|
|
@@ -807,8 +423,7 @@ function mapApyByStrategy(raw) {
|
|
|
807
423
|
const supported = SupportedAssets.find(
|
|
808
424
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
809
425
|
);
|
|
810
|
-
if (!supported)
|
|
811
|
-
continue;
|
|
426
|
+
if (!supported) continue;
|
|
812
427
|
const chainKey = supported.chainId;
|
|
813
428
|
const symbolKey = supported.symbol;
|
|
814
429
|
const bucket = apyPerAsset[chainKey] ?? {};
|
|
@@ -838,10 +453,8 @@ function computeAllocationApy(positions) {
|
|
|
838
453
|
totalValue += value;
|
|
839
454
|
const chainId = resolveChainId(p.chain);
|
|
840
455
|
const token = p.asset;
|
|
841
|
-
if (!SUPPORTED_CHAIN_IDS.includes(chainId))
|
|
842
|
-
|
|
843
|
-
if (!SUPPORTED_TOKENS.includes(token))
|
|
844
|
-
continue;
|
|
456
|
+
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
457
|
+
if (!SUPPORTED_TOKENS.includes(token)) continue;
|
|
845
458
|
const perAsset = buckets[chainId] ?? {};
|
|
846
459
|
const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
|
|
847
460
|
cell.weightedSum += apy * value;
|
|
@@ -858,8 +471,7 @@ function computeAllocationApy(positions) {
|
|
|
858
471
|
out[token] = cell.weightedSum / cell.value;
|
|
859
472
|
}
|
|
860
473
|
}
|
|
861
|
-
if (Object.keys(out).length > 0)
|
|
862
|
-
apyByChainAndAsset[chainId] = out;
|
|
474
|
+
if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
|
|
863
475
|
}
|
|
864
476
|
const totalApy = totalValue > 0 ? String(weightedSum / totalValue) : "0";
|
|
865
477
|
return { totalApy, apyByChainAndAsset };
|
|
@@ -876,19 +488,14 @@ function extractWithdrawnAmount(logs, recipient, token, decimals) {
|
|
|
876
488
|
let total = 0n;
|
|
877
489
|
let matched = false;
|
|
878
490
|
for (const log of logs) {
|
|
879
|
-
if (log.address.toLowerCase() !== wantToken)
|
|
880
|
-
|
|
881
|
-
if ((log.topics[
|
|
882
|
-
|
|
883
|
-
if (topicToAddress(log.topics[2]) !== wantRecipient)
|
|
884
|
-
continue;
|
|
885
|
-
if (!log.data || log.data === "0x")
|
|
886
|
-
continue;
|
|
491
|
+
if (log.address.toLowerCase() !== wantToken) continue;
|
|
492
|
+
if ((log.topics[0] ?? "").toLowerCase() !== TRANSFER_TOPIC) continue;
|
|
493
|
+
if (topicToAddress(log.topics[2]) !== wantRecipient) continue;
|
|
494
|
+
if (!log.data || log.data === "0x") continue;
|
|
887
495
|
total += BigInt(log.data);
|
|
888
496
|
matched = true;
|
|
889
497
|
}
|
|
890
|
-
if (!matched)
|
|
891
|
-
return void 0;
|
|
498
|
+
if (!matched) return void 0;
|
|
892
499
|
return String(hexToDecimal(`0x${total.toString(16)}`, decimals));
|
|
893
500
|
}
|
|
894
501
|
|
|
@@ -904,8 +511,7 @@ function encodeHistoryCursor(payload) {
|
|
|
904
511
|
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
|
|
905
512
|
}
|
|
906
513
|
function decodeHistoryCursor(token) {
|
|
907
|
-
if (!token)
|
|
908
|
-
throw new InvalidHistoryCursorError("Cursor is empty.");
|
|
514
|
+
if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
|
|
909
515
|
let parsed;
|
|
910
516
|
try {
|
|
911
517
|
const json = Buffer.from(token, "base64").toString("utf8");
|
|
@@ -919,14 +525,10 @@ function decodeHistoryCursor(token) {
|
|
|
919
525
|
return parsed;
|
|
920
526
|
}
|
|
921
527
|
|
|
922
|
-
// src/agents/zyfai/zyfai.agent.ts
|
|
923
|
-
init_errors();
|
|
924
|
-
|
|
925
528
|
// src/agents/zyfai/zyfai.auth-cache.ts
|
|
926
529
|
var KEY_PREFIX = "owney.zyfai.session";
|
|
927
530
|
var storage = () => {
|
|
928
|
-
if (typeof window === "undefined")
|
|
929
|
-
return null;
|
|
531
|
+
if (typeof window === "undefined") return null;
|
|
930
532
|
try {
|
|
931
533
|
return window.localStorage;
|
|
932
534
|
} catch {
|
|
@@ -936,15 +538,13 @@ var storage = () => {
|
|
|
936
538
|
var buildKey = (address, chainId) => `${KEY_PREFIX}:${address.toLowerCase()}:${chainId}`;
|
|
937
539
|
var isJwtExpired = (token) => {
|
|
938
540
|
const parts = token.split(".");
|
|
939
|
-
if (parts.length !== 3)
|
|
940
|
-
return false;
|
|
541
|
+
if (parts.length !== 3) return false;
|
|
941
542
|
try {
|
|
942
543
|
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
943
544
|
const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
|
|
944
545
|
const decoded = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8");
|
|
945
546
|
const claims = JSON.parse(decoded);
|
|
946
|
-
if (typeof claims.exp !== "number")
|
|
947
|
-
return false;
|
|
547
|
+
if (typeof claims.exp !== "number") return false;
|
|
948
548
|
return Date.now() / 1e3 >= claims.exp - 30;
|
|
949
549
|
} catch {
|
|
950
550
|
return false;
|
|
@@ -952,11 +552,9 @@ var isJwtExpired = (token) => {
|
|
|
952
552
|
};
|
|
953
553
|
var readSession = (address, chainId) => {
|
|
954
554
|
const store = storage();
|
|
955
|
-
if (!store)
|
|
956
|
-
return null;
|
|
555
|
+
if (!store) return null;
|
|
957
556
|
const raw = store.getItem(buildKey(address, chainId));
|
|
958
|
-
if (!raw)
|
|
959
|
-
return null;
|
|
557
|
+
if (!raw) return null;
|
|
960
558
|
try {
|
|
961
559
|
const parsed = JSON.parse(raw);
|
|
962
560
|
if (!parsed.token || isJwtExpired(parsed.token)) {
|
|
@@ -971,8 +569,7 @@ var readSession = (address, chainId) => {
|
|
|
971
569
|
};
|
|
972
570
|
var writeSession = (address, chainId, session) => {
|
|
973
571
|
const store = storage();
|
|
974
|
-
if (!store)
|
|
975
|
-
return;
|
|
572
|
+
if (!store) return;
|
|
976
573
|
try {
|
|
977
574
|
store.setItem(buildKey(address, chainId), JSON.stringify(session));
|
|
978
575
|
} catch {
|
|
@@ -980,33 +577,67 @@ var writeSession = (address, chainId, session) => {
|
|
|
980
577
|
};
|
|
981
578
|
var clearSession = (address, chainId) => {
|
|
982
579
|
const store = storage();
|
|
983
|
-
if (!store)
|
|
984
|
-
return;
|
|
580
|
+
if (!store) return;
|
|
985
581
|
try {
|
|
986
582
|
store.removeItem(buildKey(address, chainId));
|
|
987
583
|
} catch {
|
|
988
584
|
}
|
|
989
585
|
};
|
|
990
586
|
|
|
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
|
+
|
|
991
597
|
// src/agents/zyfai/zyfai.agent.ts
|
|
992
598
|
var DEFAULT_ZYFAI_RPC_URLS = {
|
|
993
599
|
8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
994
|
-
42161: "https://arb-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"
|
|
995
602
|
};
|
|
996
|
-
var
|
|
603
|
+
var WETH_ADDRESS_BY_CHAIN = {
|
|
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 };
|
|
997
609
|
var ZyfaiAgent = class {
|
|
998
610
|
id = "zyfai";
|
|
999
|
-
supportedChainIds = [8453, 42161];
|
|
611
|
+
supportedChainIds = [8453, 42161, 1];
|
|
1000
612
|
supportedAssets = [
|
|
1001
613
|
{
|
|
1002
614
|
chainId: 8453,
|
|
1003
615
|
chain: "BASE",
|
|
1004
|
-
assets: [
|
|
616
|
+
assets: [
|
|
617
|
+
{ symbol: "USDC", minDepositAmount: "1500000" },
|
|
618
|
+
{ symbol: "WETH", minDepositAmount: "500000000000000" }
|
|
619
|
+
]
|
|
1005
620
|
},
|
|
1006
621
|
{
|
|
1007
622
|
chainId: 42161,
|
|
1008
623
|
chain: "ARBITRUM",
|
|
1009
|
-
assets: [
|
|
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. Lowered in @zyfai/sdk@0.2.40.
|
|
634
|
+
// USDC: 100 (100 * 1e6). WETH: 0.005 (5 * 1e15).
|
|
635
|
+
// NOTE: the @zyfai/sdk README claims WETH has no minimum — that's stale;
|
|
636
|
+
// the shipped code enforces 0.005 WETH, so match it or deposits reject on-chain.
|
|
637
|
+
assets: [
|
|
638
|
+
{ symbol: "USDC", minDepositAmount: "100000000" },
|
|
639
|
+
{ symbol: "WETH", minDepositAmount: "5000000000000000" }
|
|
640
|
+
]
|
|
1010
641
|
}
|
|
1011
642
|
];
|
|
1012
643
|
sdk;
|
|
@@ -1030,8 +661,7 @@ var ZyfaiAgent = class {
|
|
|
1030
661
|
}
|
|
1031
662
|
getPublicClient(chainId) {
|
|
1032
663
|
const cached = this.publicClients.get(chainId);
|
|
1033
|
-
if (cached)
|
|
1034
|
-
return cached;
|
|
664
|
+
if (cached) return cached;
|
|
1035
665
|
const client = (0, import_viem.createPublicClient)({
|
|
1036
666
|
chain: VIEM_CHAIN[chainId],
|
|
1037
667
|
transport: (0, import_viem.http)(this.rpcUrls[chainId])
|
|
@@ -1041,33 +671,43 @@ var ZyfaiAgent = class {
|
|
|
1041
671
|
}
|
|
1042
672
|
/**
|
|
1043
673
|
* Fills in the true withdrawn amount for `Withdraw` entries by decoding the
|
|
1044
|
-
*
|
|
674
|
+
* on-chain `Transfer` that lands in the user's wallet. The Zyfai history
|
|
1045
675
|
* payload only reports the *remaining* position for a partial withdraw (see
|
|
1046
676
|
* the note in `mapEntries`), so without this the UI shows the leftover
|
|
1047
677
|
* balance instead of the amount withdrawn.
|
|
1048
678
|
*
|
|
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
|
+
*
|
|
1049
686
|
* Mutates `entries` in place. Runs all lookups concurrently and is tolerant
|
|
1050
687
|
* of RPC failures: an entry whose amount can't be resolved keeps
|
|
1051
688
|
* `amount: undefined`, which consumers render as a hidden/retryable row
|
|
1052
689
|
* rather than a wrong number.
|
|
1053
690
|
*/
|
|
1054
691
|
async resolveWithdrawnAmounts(entries, chainId, recipient) {
|
|
1055
|
-
const asset = SupportedAssets.find(
|
|
1056
|
-
(a) => a.chainId === chainId && a.symbol === "USDC"
|
|
1057
|
-
);
|
|
1058
|
-
if (!asset)
|
|
1059
|
-
return;
|
|
1060
692
|
const withdrawals = entries.filter(
|
|
1061
693
|
(e) => e.action === "Withdraw" && e.transactions[0]?.txHashes[0]
|
|
1062
694
|
);
|
|
1063
|
-
if (withdrawals.length === 0)
|
|
1064
|
-
return;
|
|
695
|
+
if (withdrawals.length === 0) return;
|
|
1065
696
|
const client = this.getPublicClient(chainId);
|
|
1066
697
|
await Promise.all(
|
|
1067
698
|
withdrawals.map(async (entry) => {
|
|
1068
699
|
const tx = entry.transactions[0];
|
|
1069
700
|
const txHash = tx.txHashes[0];
|
|
1070
|
-
const
|
|
701
|
+
const rawSymbol = tx.tokenSymbol;
|
|
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);
|
|
1071
711
|
if (cached !== void 0) {
|
|
1072
712
|
tx.amount = cached;
|
|
1073
713
|
return;
|
|
@@ -1083,7 +723,7 @@ var ZyfaiAgent = class {
|
|
|
1083
723
|
asset.decimals
|
|
1084
724
|
);
|
|
1085
725
|
if (amount !== void 0) {
|
|
1086
|
-
this.withdrawAmountCache.set(
|
|
726
|
+
this.withdrawAmountCache.set(cacheKey, amount);
|
|
1087
727
|
tx.amount = amount;
|
|
1088
728
|
}
|
|
1089
729
|
} catch {
|
|
@@ -1102,13 +742,11 @@ var ZyfaiAgent = class {
|
|
|
1102
742
|
* for some apps and the patch only matters in the browser anyway.
|
|
1103
743
|
*/
|
|
1104
744
|
installAuthCache() {
|
|
1105
|
-
if (typeof window === "undefined")
|
|
1106
|
-
return;
|
|
745
|
+
if (typeof window === "undefined") return;
|
|
1107
746
|
const internals = this.sdk;
|
|
1108
747
|
const original = internals.authenticateUser.bind(internals);
|
|
1109
748
|
internals.authenticateUser = async () => {
|
|
1110
|
-
if (internals.authenticatedUserId !== null)
|
|
1111
|
-
return;
|
|
749
|
+
if (internals.authenticatedUserId !== null) return;
|
|
1112
750
|
const address = internals.walletClient?.account?.address;
|
|
1113
751
|
const chainId = internals.currentChainId;
|
|
1114
752
|
if (address && chainId) {
|
|
@@ -1183,12 +821,10 @@ var ZyfaiAgent = class {
|
|
|
1183
821
|
return await this.sdk.connectAccount(provider, chainId);
|
|
1184
822
|
} catch (error) {
|
|
1185
823
|
const message = error instanceof Error ? error.message : String(error);
|
|
1186
|
-
if (!message.toLowerCase().includes("unauthorized"))
|
|
1187
|
-
throw error;
|
|
824
|
+
if (!message.toLowerCase().includes("unauthorized")) throw error;
|
|
1188
825
|
const internals = this.sdk;
|
|
1189
826
|
const address = internals.walletClient?.account?.address;
|
|
1190
|
-
if (address)
|
|
1191
|
-
clearSession(address, chainId);
|
|
827
|
+
if (address) clearSession(address, chainId);
|
|
1192
828
|
return await this.sdk.connectAccount(provider, chainId);
|
|
1193
829
|
}
|
|
1194
830
|
}
|
|
@@ -1201,6 +837,22 @@ var ZyfaiAgent = class {
|
|
|
1201
837
|
this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
|
|
1202
838
|
if (!wallet.isDeployed) {
|
|
1203
839
|
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
|
+
}
|
|
1204
856
|
}
|
|
1205
857
|
const user = await this.sdk.getUserDetails();
|
|
1206
858
|
if (!user.hasActiveSessionKey) {
|
|
@@ -1242,7 +894,7 @@ var ZyfaiAgent = class {
|
|
|
1242
894
|
throw error;
|
|
1243
895
|
}
|
|
1244
896
|
}
|
|
1245
|
-
async deposit(state, chainId, amount, depositCallback) {
|
|
897
|
+
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
1246
898
|
try {
|
|
1247
899
|
await this.connectFull(state, chainId);
|
|
1248
900
|
const validChainId = isValidChainId(chainId);
|
|
@@ -1256,13 +908,15 @@ var ZyfaiAgent = class {
|
|
|
1256
908
|
validChainId,
|
|
1257
909
|
amount
|
|
1258
910
|
);
|
|
1259
|
-
|
|
911
|
+
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
912
|
+
await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
|
|
1260
913
|
return { txHash, smartWallet: wallet.address, amount };
|
|
1261
914
|
}
|
|
1262
915
|
const raw = await this.sdk.depositFunds(
|
|
1263
916
|
this.getAddress(),
|
|
1264
917
|
validChainId,
|
|
1265
|
-
amount
|
|
918
|
+
amount,
|
|
919
|
+
asset
|
|
1266
920
|
);
|
|
1267
921
|
return mapDeposit(raw);
|
|
1268
922
|
} catch (error) {
|
|
@@ -1300,10 +954,10 @@ var ZyfaiAgent = class {
|
|
|
1300
954
|
const raw = await this.sdk.getOnchainEarnings(smartWallet);
|
|
1301
955
|
return mapEarnings(raw, smartWallet);
|
|
1302
956
|
}
|
|
1303
|
-
async getAccountApy(state, chainId, days) {
|
|
957
|
+
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
1304
958
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1305
959
|
const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
1306
|
-
return mapApyHistory(raw, chainId);
|
|
960
|
+
return mapApyHistory(raw, chainId, tokenSymbol);
|
|
1307
961
|
}
|
|
1308
962
|
async getHistory(state, chainId, options) {
|
|
1309
963
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -1358,6 +1012,20 @@ var ZyfaiAgent = class {
|
|
|
1358
1012
|
}
|
|
1359
1013
|
return mapUserProfile(raw, this.connectedAddress);
|
|
1360
1014
|
}
|
|
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
|
+
}
|
|
1361
1029
|
// --- IAgent: Discovery (no wallet required) ---
|
|
1362
1030
|
async getAgentApy(days, options) {
|
|
1363
1031
|
const raw = await this.sdk.getAPYPerStrategy(
|
|
@@ -1371,11 +1039,7 @@ var ZyfaiAgent = class {
|
|
|
1371
1039
|
}
|
|
1372
1040
|
};
|
|
1373
1041
|
|
|
1374
|
-
// src/client.ts
|
|
1375
|
-
init_errors();
|
|
1376
|
-
|
|
1377
1042
|
// src/lib/routing-api.ts
|
|
1378
|
-
init_errors();
|
|
1379
1043
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1380
1044
|
async function fetchAgentKeys(apiKey) {
|
|
1381
1045
|
const url = `${ROUTING_API_BASE_URL}/api/v1/agent/keys`;
|
|
@@ -1406,7 +1070,6 @@ async function fetchAgentKeys(apiKey) {
|
|
|
1406
1070
|
}
|
|
1407
1071
|
|
|
1408
1072
|
// src/lib/health-report.ts
|
|
1409
|
-
init_errors();
|
|
1410
1073
|
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1411
1074
|
async function reportAgentFailure(apiKey, agentType, errorCode) {
|
|
1412
1075
|
try {
|
|
@@ -1448,8 +1111,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
1448
1111
|
const tokenBalance = agentBalance?.tokens.find(
|
|
1449
1112
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
1450
1113
|
);
|
|
1451
|
-
if (!tokenBalance)
|
|
1452
|
-
return { agent, balance: 0n };
|
|
1114
|
+
if (!tokenBalance) return { agent, balance: 0n };
|
|
1453
1115
|
return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
|
|
1454
1116
|
});
|
|
1455
1117
|
}
|
|
@@ -1466,11 +1128,9 @@ function planProportionalShares(balances, requested, totalAvailable) {
|
|
|
1466
1128
|
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
1467
1129
|
});
|
|
1468
1130
|
for (const p of byHeadroom) {
|
|
1469
|
-
if (remainder === 0n)
|
|
1470
|
-
break;
|
|
1131
|
+
if (remainder === 0n) break;
|
|
1471
1132
|
const headroom = p.balance - p.planned;
|
|
1472
|
-
if (headroom <= 0n)
|
|
1473
|
-
continue;
|
|
1133
|
+
if (headroom <= 0n) continue;
|
|
1474
1134
|
const take = headroom < remainder ? headroom : remainder;
|
|
1475
1135
|
p.planned += take;
|
|
1476
1136
|
remainder -= take;
|
|
@@ -1499,8 +1159,7 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
|
1499
1159
|
(s, c) => s + (c.balance - c.planned),
|
|
1500
1160
|
0n
|
|
1501
1161
|
);
|
|
1502
|
-
if (totalHeadroom === 0n)
|
|
1503
|
-
return;
|
|
1162
|
+
if (totalHeadroom === 0n) return;
|
|
1504
1163
|
let distributed = 0n;
|
|
1505
1164
|
for (const c of candidates) {
|
|
1506
1165
|
const headroom = c.balance - c.planned;
|
|
@@ -1511,11 +1170,9 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
|
1511
1170
|
}
|
|
1512
1171
|
let leftover = amount - distributed;
|
|
1513
1172
|
for (const c of candidates) {
|
|
1514
|
-
if (leftover === 0n)
|
|
1515
|
-
break;
|
|
1173
|
+
if (leftover === 0n) break;
|
|
1516
1174
|
const headroom = c.balance - c.planned;
|
|
1517
|
-
if (headroom <= 0n)
|
|
1518
|
-
continue;
|
|
1175
|
+
if (headroom <= 0n) continue;
|
|
1519
1176
|
const take = headroom < leftover ? headroom : leftover;
|
|
1520
1177
|
c.planned += take;
|
|
1521
1178
|
leftover -= take;
|
|
@@ -1532,16 +1189,13 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
1532
1189
|
for (const id of Object.keys(agentApys)) {
|
|
1533
1190
|
const cells = agentApys[id].apyByChainAndAsset;
|
|
1534
1191
|
const balance = agentBalances[id] ?? 0;
|
|
1535
|
-
if (!cells || balance <= 0)
|
|
1536
|
-
continue;
|
|
1192
|
+
if (!cells || balance <= 0) continue;
|
|
1537
1193
|
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
1538
|
-
if (!perAsset)
|
|
1539
|
-
continue;
|
|
1194
|
+
if (!perAsset) continue;
|
|
1540
1195
|
const chainId = Number(chainKey);
|
|
1541
1196
|
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
1542
1197
|
const apy = Number(apyValue ?? 0);
|
|
1543
|
-
if (apy === 0)
|
|
1544
|
-
continue;
|
|
1198
|
+
if (apy === 0) continue;
|
|
1545
1199
|
sums[chainId] ??= {};
|
|
1546
1200
|
weights[chainId] ??= {};
|
|
1547
1201
|
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
@@ -1555,8 +1209,7 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
1555
1209
|
const perAssetOut = {};
|
|
1556
1210
|
for (const asset of Object.keys(sums[chainId])) {
|
|
1557
1211
|
const w = weights[chainId][asset];
|
|
1558
|
-
if (w > 0)
|
|
1559
|
-
perAssetOut[asset] = sums[chainId][asset] / w;
|
|
1212
|
+
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
1560
1213
|
}
|
|
1561
1214
|
if (Object.keys(perAssetOut).length > 0) {
|
|
1562
1215
|
out[chainId] = perAssetOut;
|
|
@@ -1569,9 +1222,6 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
1569
1222
|
var import_viem5 = require("viem");
|
|
1570
1223
|
var import_chains2 = require("viem/chains");
|
|
1571
1224
|
|
|
1572
|
-
// src/lib/sponsored-deposit.ts
|
|
1573
|
-
init_errors();
|
|
1574
|
-
|
|
1575
1225
|
// src/lib/transfer-auth.ts
|
|
1576
1226
|
var import_viem3 = require("viem");
|
|
1577
1227
|
var ERC20_META_ABI = [
|
|
@@ -1609,7 +1259,6 @@ function randomAuthNonce() {
|
|
|
1609
1259
|
}
|
|
1610
1260
|
|
|
1611
1261
|
// src/lib/sponsor-client.ts
|
|
1612
|
-
init_errors();
|
|
1613
1262
|
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1614
1263
|
async function postSponsorTransferAuth(input) {
|
|
1615
1264
|
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
@@ -1617,7 +1266,10 @@ async function postSponsorTransferAuth(input) {
|
|
|
1617
1266
|
try {
|
|
1618
1267
|
res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
1619
1268
|
method: "POST",
|
|
1620
|
-
headers: {
|
|
1269
|
+
headers: {
|
|
1270
|
+
"content-type": "application/json",
|
|
1271
|
+
"x-owney-api-key": input.apiKey
|
|
1272
|
+
},
|
|
1621
1273
|
body: JSON.stringify(input.body)
|
|
1622
1274
|
});
|
|
1623
1275
|
} catch (networkError) {
|
|
@@ -1637,11 +1289,91 @@ async function postSponsorTransferAuth(input) {
|
|
|
1637
1289
|
throw new OwneyError(
|
|
1638
1290
|
"SPONSOR_REQUEST_FAILED",
|
|
1639
1291
|
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1640
|
-
{
|
|
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
|
+
}
|
|
1641
1299
|
);
|
|
1642
1300
|
}
|
|
1643
1301
|
return parsed.data;
|
|
1644
1302
|
}
|
|
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
|
+
}
|
|
1645
1377
|
|
|
1646
1378
|
// src/lib/sponsored-deposit.ts
|
|
1647
1379
|
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
@@ -1693,6 +1425,114 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
1693
1425
|
};
|
|
1694
1426
|
}
|
|
1695
1427
|
|
|
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
|
+
|
|
1696
1536
|
// src/client.ts
|
|
1697
1537
|
function encodeMultiAgentCursor(map) {
|
|
1698
1538
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
@@ -1716,13 +1556,22 @@ function decodeMultiAgentCursor(token) {
|
|
|
1716
1556
|
}
|
|
1717
1557
|
var SPONSORED_USDC_BY_CHAIN = {
|
|
1718
1558
|
8453: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
|
|
1719
|
-
42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
|
|
1559
|
+
42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
|
|
1560
|
+
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
1720
1561
|
};
|
|
1721
1562
|
var VIEM_CHAIN2 = {
|
|
1722
1563
|
8453: import_chains2.base,
|
|
1723
|
-
42161: import_chains2.arbitrum
|
|
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"
|
|
1724
1571
|
};
|
|
1725
|
-
|
|
1572
|
+
function shouldFallbackToUserPaid(error, asset, appCallback) {
|
|
1573
|
+
return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
|
|
1574
|
+
}
|
|
1726
1575
|
var OwneySDK = class {
|
|
1727
1576
|
agents = /* @__PURE__ */ new Map();
|
|
1728
1577
|
activeAgents = /* @__PURE__ */ new Set();
|
|
@@ -1735,11 +1584,14 @@ var OwneySDK = class {
|
|
|
1735
1584
|
state = null;
|
|
1736
1585
|
apiKey;
|
|
1737
1586
|
zyfaiRpcUrls;
|
|
1587
|
+
routingApiBaseUrl;
|
|
1738
1588
|
cachedSponsoredCallback = null;
|
|
1589
|
+
cachedWethSponsoredCallback = null;
|
|
1739
1590
|
initializingAgentsPromise = null;
|
|
1740
1591
|
constructor(config) {
|
|
1741
1592
|
this.apiKey = config.apiKey;
|
|
1742
1593
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
1594
|
+
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
1743
1595
|
}
|
|
1744
1596
|
/**
|
|
1745
1597
|
* Establish connection state for all subsequent SDK calls.
|
|
@@ -1760,6 +1612,7 @@ var OwneySDK = class {
|
|
|
1760
1612
|
}
|
|
1761
1613
|
this.state = { provider, walletAddress, chainId: null };
|
|
1762
1614
|
this.cachedSponsoredCallback = null;
|
|
1615
|
+
this.cachedWethSponsoredCallback = null;
|
|
1763
1616
|
}
|
|
1764
1617
|
/**
|
|
1765
1618
|
* Disconnect from all agents and clear connection state.
|
|
@@ -1773,6 +1626,7 @@ var OwneySDK = class {
|
|
|
1773
1626
|
this.disabledAgents.clear();
|
|
1774
1627
|
this.state = null;
|
|
1775
1628
|
this.cachedSponsoredCallback = null;
|
|
1629
|
+
this.cachedWethSponsoredCallback = null;
|
|
1776
1630
|
}
|
|
1777
1631
|
isAgentDisabled(agentId) {
|
|
1778
1632
|
return this.disabledAgents.has(agentId);
|
|
@@ -1792,8 +1646,7 @@ var OwneySDK = class {
|
|
|
1792
1646
|
return this.state?.chainId ?? null;
|
|
1793
1647
|
}
|
|
1794
1648
|
requireState() {
|
|
1795
|
-
if (!this.state)
|
|
1796
|
-
throw new NotConnectedError();
|
|
1649
|
+
if (!this.state) throw new NotConnectedError();
|
|
1797
1650
|
return this.state;
|
|
1798
1651
|
}
|
|
1799
1652
|
requireChainId() {
|
|
@@ -1823,12 +1676,12 @@ var OwneySDK = class {
|
|
|
1823
1676
|
* `TransferWithAuthorization`, then POSTs to the sponsor API.
|
|
1824
1677
|
*/
|
|
1825
1678
|
getDefaultSponsoredCallback() {
|
|
1826
|
-
if (this.cachedSponsoredCallback)
|
|
1827
|
-
return this.cachedSponsoredCallback;
|
|
1679
|
+
if (this.cachedSponsoredCallback) return this.cachedSponsoredCallback;
|
|
1828
1680
|
const provider = this.requireConnectedProvider();
|
|
1829
1681
|
const owner = this.state.walletAddress;
|
|
1830
1682
|
this.cachedSponsoredCallback = makeSponsoredDepositCallback({
|
|
1831
1683
|
apiKey: this.apiKey,
|
|
1684
|
+
baseUrl: this.routingApiBaseUrl,
|
|
1832
1685
|
ownerAddress: owner,
|
|
1833
1686
|
tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
|
|
1834
1687
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
@@ -1846,6 +1699,37 @@ var OwneySDK = class {
|
|
|
1846
1699
|
});
|
|
1847
1700
|
return this.cachedSponsoredCallback;
|
|
1848
1701
|
}
|
|
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
|
+
}
|
|
1849
1733
|
getAgent(agentId) {
|
|
1850
1734
|
const agent = this.agents.get(agentId);
|
|
1851
1735
|
if (!agent) {
|
|
@@ -1857,22 +1741,19 @@ var OwneySDK = class {
|
|
|
1857
1741
|
const active = /* @__PURE__ */ new Map();
|
|
1858
1742
|
for (const id of this.activeAgents) {
|
|
1859
1743
|
const agent = this.agents.get(id);
|
|
1860
|
-
if (agent)
|
|
1861
|
-
active.set(id, agent);
|
|
1744
|
+
if (agent) active.set(id, agent);
|
|
1862
1745
|
}
|
|
1863
1746
|
return active;
|
|
1864
1747
|
}
|
|
1865
1748
|
async ensureAgentsInitialized() {
|
|
1866
|
-
if (this.agents.size > 0)
|
|
1867
|
-
return;
|
|
1749
|
+
if (this.agents.size > 0) return;
|
|
1868
1750
|
if (!this.initializingAgentsPromise) {
|
|
1869
1751
|
this.initializingAgentsPromise = (async () => {
|
|
1870
1752
|
const agentKeys = await fetchAgentKeys(this.apiKey);
|
|
1871
1753
|
this.disabledAgents.clear();
|
|
1872
|
-
for (const { key, agent_type,
|
|
1873
|
-
const agent =
|
|
1874
|
-
if (!agent)
|
|
1875
|
-
continue;
|
|
1754
|
+
for (const { key, agent_type, is_enabled } of agentKeys) {
|
|
1755
|
+
const agent = this.createAgent(agent_type, key);
|
|
1756
|
+
if (!agent) continue;
|
|
1876
1757
|
this.agents.set(agent_type, agent);
|
|
1877
1758
|
if (is_enabled === false) {
|
|
1878
1759
|
this.disabledAgents.add(agent_type);
|
|
@@ -1892,16 +1773,10 @@ var OwneySDK = class {
|
|
|
1892
1773
|
this.initializingAgentsPromise = null;
|
|
1893
1774
|
}
|
|
1894
1775
|
}
|
|
1895
|
-
|
|
1776
|
+
createAgent(agentId, key) {
|
|
1896
1777
|
if (agentId === "zyfai") {
|
|
1897
1778
|
return new ZyfaiAgent(key, this.zyfaiRpcUrls);
|
|
1898
1779
|
}
|
|
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
|
-
}
|
|
1905
1780
|
return null;
|
|
1906
1781
|
}
|
|
1907
1782
|
// --- Account lifecycle ---
|
|
@@ -2005,7 +1880,7 @@ var OwneySDK = class {
|
|
|
2005
1880
|
}
|
|
2006
1881
|
const state = this.requireState();
|
|
2007
1882
|
const chainId = this.requireChainId();
|
|
2008
|
-
const effectiveCallback = depositCallback ?? this.getDefaultSponsoredCallback();
|
|
1883
|
+
const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback() : this.getDefaultSponsoredCallback());
|
|
2009
1884
|
if (agentId) {
|
|
2010
1885
|
if (this.isAgentDisabled(agentId)) {
|
|
2011
1886
|
throw new OwneyError(
|
|
@@ -2017,10 +1892,14 @@ var OwneySDK = class {
|
|
|
2017
1892
|
const agent = this.getAgent(agentId);
|
|
2018
1893
|
this.validateAssetSupport(agent, chainId, asset);
|
|
2019
1894
|
await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
|
|
2020
|
-
return
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
1895
|
+
return this.depositWithFallback(
|
|
1896
|
+
agent,
|
|
1897
|
+
state,
|
|
1898
|
+
chainId,
|
|
1899
|
+
amount,
|
|
1900
|
+
asset,
|
|
1901
|
+
effectiveCallback,
|
|
1902
|
+
depositCallback
|
|
2024
1903
|
);
|
|
2025
1904
|
}
|
|
2026
1905
|
const totalAmount = BigInt(amount);
|
|
@@ -2050,26 +1929,77 @@ var OwneySDK = class {
|
|
|
2050
1929
|
);
|
|
2051
1930
|
}
|
|
2052
1931
|
const agentResults = {};
|
|
2053
|
-
const agentErrors = {};
|
|
2054
1932
|
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 (; ; ) {
|
|
2055
1984
|
try {
|
|
2056
|
-
|
|
2057
|
-
this.apiKey,
|
|
2058
|
-
agent.id,
|
|
2059
|
-
() => agent.deposit(state, chainId, agentAmount, effectiveCallback)
|
|
2060
|
-
);
|
|
1985
|
+
return await attempt(effectiveCallback);
|
|
2061
1986
|
} catch (error) {
|
|
2062
|
-
|
|
1987
|
+
if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
1988
|
+
approvalAttempted = true;
|
|
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
|
|
1999
|
+
);
|
|
2000
|
+
return attempt(void 0);
|
|
2063
2001
|
}
|
|
2064
2002
|
}
|
|
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 };
|
|
2073
2003
|
}
|
|
2074
2004
|
getMinDepositAmount(agent, chainId, asset) {
|
|
2075
2005
|
const chainAssets = agent.supportedAssets.find(
|
|
@@ -2079,8 +2009,7 @@ var OwneySDK = class {
|
|
|
2079
2009
|
return BigInt(assetEntry?.minDepositAmount ?? "0");
|
|
2080
2010
|
}
|
|
2081
2011
|
splitDepositAmount(totalAmount, agents, chainId, asset, exempt = /* @__PURE__ */ new Set()) {
|
|
2082
|
-
if (agents.length === 0)
|
|
2083
|
-
return [];
|
|
2012
|
+
if (agents.length === 0) return [];
|
|
2084
2013
|
const perAgent = totalAmount / BigInt(agents.length);
|
|
2085
2014
|
const remainder = totalAmount % BigInt(agents.length);
|
|
2086
2015
|
const splits = agents.map((agent, i) => ({
|
|
@@ -2104,10 +2033,8 @@ var OwneySDK = class {
|
|
|
2104
2033
|
async validateMinDepositAmount(agent, state, chainId, asset, amount) {
|
|
2105
2034
|
const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
|
|
2106
2035
|
const parsedAmount = BigInt(amount);
|
|
2107
|
-
if (parsedAmount >= minDepositAmount)
|
|
2108
|
-
|
|
2109
|
-
if (await this.hasExistingBalance(agent, state, chainId, asset))
|
|
2110
|
-
return;
|
|
2036
|
+
if (parsedAmount >= minDepositAmount) return;
|
|
2037
|
+
if (await this.hasExistingBalance(agent, state, chainId, asset)) return;
|
|
2111
2038
|
throw new OwneyError(
|
|
2112
2039
|
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
2113
2040
|
`Amount "${amount}" is below the required minimum deposit "${minDepositAmount.toString()}" for agent "${agent.id}" on chain ${chainId} for asset "${asset}".`,
|
|
@@ -2170,34 +2097,6 @@ var OwneySDK = class {
|
|
|
2170
2097
|
}
|
|
2171
2098
|
return eligible;
|
|
2172
2099
|
}
|
|
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
|
-
}
|
|
2201
2100
|
// --- Fund operations ---
|
|
2202
2101
|
/**
|
|
2203
2102
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -2233,17 +2132,9 @@ var OwneySDK = class {
|
|
|
2233
2132
|
}
|
|
2234
2133
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
2235
2134
|
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);
|
|
2244
2135
|
const results2 = {};
|
|
2245
2136
|
const agentErrors2 = {};
|
|
2246
|
-
for (const
|
|
2137
|
+
for (const agent of eligibleAgents) {
|
|
2247
2138
|
try {
|
|
2248
2139
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
2249
2140
|
} catch (err) {
|
|
@@ -2260,10 +2151,9 @@ var OwneySDK = class {
|
|
|
2260
2151
|
{ asset, agentErrors: agentErrors2 }
|
|
2261
2152
|
);
|
|
2262
2153
|
}
|
|
2263
|
-
const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
|
|
2264
2154
|
return {
|
|
2265
2155
|
agentResult: results2,
|
|
2266
|
-
totalWithdrawn:
|
|
2156
|
+
totalWithdrawn: sumWithdrawnAmount(results2).toString()
|
|
2267
2157
|
};
|
|
2268
2158
|
}
|
|
2269
2159
|
const requested = BigInt(amount);
|
|
@@ -2308,8 +2198,7 @@ var OwneySDK = class {
|
|
|
2308
2198
|
const agentErrors = {};
|
|
2309
2199
|
for (let i = 0; i < plans.length; i++) {
|
|
2310
2200
|
const p = plans[i];
|
|
2311
|
-
if (p.planned === 0n)
|
|
2312
|
-
continue;
|
|
2201
|
+
if (p.planned === 0n) continue;
|
|
2313
2202
|
try {
|
|
2314
2203
|
results[p.agent.id] = await p.agent.withdraw(
|
|
2315
2204
|
state,
|
|
@@ -2443,19 +2332,30 @@ var OwneySDK = class {
|
|
|
2443
2332
|
*/
|
|
2444
2333
|
async getAccountApy({
|
|
2445
2334
|
agentId,
|
|
2446
|
-
days
|
|
2335
|
+
days,
|
|
2336
|
+
tokenSymbol
|
|
2447
2337
|
}) {
|
|
2448
2338
|
const state = this.requireState();
|
|
2449
2339
|
const chainId = this.requireChainId();
|
|
2450
2340
|
if (agentId) {
|
|
2451
|
-
return this.getAgent(agentId).getAccountApy(
|
|
2341
|
+
return this.getAgent(agentId).getAccountApy(
|
|
2342
|
+
state,
|
|
2343
|
+
chainId,
|
|
2344
|
+
days,
|
|
2345
|
+
tokenSymbol
|
|
2346
|
+
);
|
|
2452
2347
|
}
|
|
2453
2348
|
const activeAgents = this.getActiveAgents();
|
|
2454
2349
|
const entries = [...activeAgents.entries()];
|
|
2455
2350
|
const [apyResults, balanceResults] = await Promise.all([
|
|
2456
2351
|
Promise.all(
|
|
2457
2352
|
entries.map(async ([id, agent]) => {
|
|
2458
|
-
const apy = await agent.getAccountApy(
|
|
2353
|
+
const apy = await agent.getAccountApy(
|
|
2354
|
+
state,
|
|
2355
|
+
chainId,
|
|
2356
|
+
days,
|
|
2357
|
+
tokenSymbol
|
|
2358
|
+
);
|
|
2459
2359
|
return [id, apy];
|
|
2460
2360
|
})
|
|
2461
2361
|
),
|
|
@@ -2478,15 +2378,13 @@ var OwneySDK = class {
|
|
|
2478
2378
|
let relevantBalance = 0;
|
|
2479
2379
|
for (const id of Object.keys(results)) {
|
|
2480
2380
|
const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
|
|
2481
|
-
if (agentApy === 0)
|
|
2482
|
-
continue;
|
|
2381
|
+
if (agentApy === 0) continue;
|
|
2483
2382
|
relevantBalance += balances[id];
|
|
2484
2383
|
}
|
|
2485
2384
|
if (relevantBalance > 0) {
|
|
2486
2385
|
for (const id of Object.keys(results)) {
|
|
2487
2386
|
const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
|
|
2488
|
-
if (agentApy === 0)
|
|
2489
|
-
continue;
|
|
2387
|
+
if (agentApy === 0) continue;
|
|
2490
2388
|
const weight = balances[id] / relevantBalance;
|
|
2491
2389
|
totalApy += agentApy * weight;
|
|
2492
2390
|
}
|
|
@@ -2541,8 +2439,7 @@ var OwneySDK = class {
|
|
|
2541
2439
|
const nextCursorMap = {};
|
|
2542
2440
|
let hasMore = false;
|
|
2543
2441
|
for (const { agentId: id, page } of perAgent) {
|
|
2544
|
-
if (!page)
|
|
2545
|
-
continue;
|
|
2442
|
+
if (!page) continue;
|
|
2546
2443
|
merged.push(...page.data);
|
|
2547
2444
|
if (page.hasMore && page.nextCursor) {
|
|
2548
2445
|
nextCursorMap[id] = page.nextCursor;
|
|
@@ -2551,16 +2448,12 @@ var OwneySDK = class {
|
|
|
2551
2448
|
}
|
|
2552
2449
|
merged.sort((a, b) => {
|
|
2553
2450
|
const dateDiff = new Date(b.date).getTime() - new Date(a.date).getTime();
|
|
2554
|
-
if (dateDiff !== 0)
|
|
2555
|
-
|
|
2556
|
-
if (a.agent !== b.agent)
|
|
2557
|
-
return a.agent < b.agent ? -1 : 1;
|
|
2451
|
+
if (dateDiff !== 0) return dateDiff;
|
|
2452
|
+
if (a.agent !== b.agent) return a.agent < b.agent ? -1 : 1;
|
|
2558
2453
|
const aTx = a.transactions?.[0]?.txHashes?.[0] ?? "";
|
|
2559
2454
|
const bTx = b.transactions?.[0]?.txHashes?.[0] ?? "";
|
|
2560
|
-
if (aTx !== bTx)
|
|
2561
|
-
|
|
2562
|
-
if (a.action !== b.action)
|
|
2563
|
-
return a.action < b.action ? -1 : 1;
|
|
2455
|
+
if (aTx !== bTx) return aTx < bTx ? -1 : 1;
|
|
2456
|
+
if (a.action !== b.action) return a.action < b.action ? -1 : 1;
|
|
2564
2457
|
return 0;
|
|
2565
2458
|
});
|
|
2566
2459
|
return {
|
|
@@ -2595,6 +2488,67 @@ var OwneySDK = class {
|
|
|
2595
2488
|
agentUserProfile: results
|
|
2596
2489
|
};
|
|
2597
2490
|
}
|
|
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
|
+
}
|
|
2598
2552
|
// --- Discovery (no wallet required) ---
|
|
2599
2553
|
/**
|
|
2600
2554
|
* Get the agent's average APY performance over a time period. Does not require a wallet connection.
|
|
@@ -2692,9 +2646,6 @@ var OwneySDK = class {
|
|
|
2692
2646
|
}
|
|
2693
2647
|
};
|
|
2694
2648
|
|
|
2695
|
-
// src/index.ts
|
|
2696
|
-
init_errors();
|
|
2697
|
-
|
|
2698
2649
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
2699
2650
|
var import_viem6 = require("viem");
|
|
2700
2651
|
var import_siwe = require("siwe");
|
|
@@ -2703,8 +2654,7 @@ var import_sdk2 = require("@zyfai/sdk");
|
|
|
2703
2654
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
2704
2655
|
var KEY_PREFIX2 = "owney.siwx.session";
|
|
2705
2656
|
var storage2 = () => {
|
|
2706
|
-
if (typeof window === "undefined")
|
|
2707
|
-
return null;
|
|
2657
|
+
if (typeof window === "undefined") return null;
|
|
2708
2658
|
try {
|
|
2709
2659
|
return window.localStorage;
|
|
2710
2660
|
} catch {
|
|
@@ -2714,11 +2664,9 @@ var storage2 = () => {
|
|
|
2714
2664
|
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2715
2665
|
var readSiwxSession = (address, chainId) => {
|
|
2716
2666
|
const store = storage2();
|
|
2717
|
-
if (!store)
|
|
2718
|
-
return null;
|
|
2667
|
+
if (!store) return null;
|
|
2719
2668
|
const raw = store.getItem(buildKey2(address, chainId));
|
|
2720
|
-
if (!raw)
|
|
2721
|
-
return null;
|
|
2669
|
+
if (!raw) return null;
|
|
2722
2670
|
try {
|
|
2723
2671
|
return JSON.parse(raw);
|
|
2724
2672
|
} catch {
|
|
@@ -2728,8 +2676,7 @@ var readSiwxSession = (address, chainId) => {
|
|
|
2728
2676
|
};
|
|
2729
2677
|
var writeSiwxSession = (address, chainId, session) => {
|
|
2730
2678
|
const store = storage2();
|
|
2731
|
-
if (!store)
|
|
2732
|
-
return;
|
|
2679
|
+
if (!store) return;
|
|
2733
2680
|
try {
|
|
2734
2681
|
store.setItem(buildKey2(address, chainId), JSON.stringify(session));
|
|
2735
2682
|
} catch {
|
|
@@ -2737,8 +2684,7 @@ var writeSiwxSession = (address, chainId, session) => {
|
|
|
2737
2684
|
};
|
|
2738
2685
|
var clearSiwxSession = (address, chainId) => {
|
|
2739
2686
|
const store = storage2();
|
|
2740
|
-
if (!store)
|
|
2741
|
-
return;
|
|
2687
|
+
if (!store) return;
|
|
2742
2688
|
try {
|
|
2743
2689
|
store.removeItem(buildKey2(address, chainId));
|
|
2744
2690
|
} catch {
|
|
@@ -2801,12 +2747,10 @@ function buildSIWXConfig(deps) {
|
|
|
2801
2747
|
const jwt = readSession(address, id);
|
|
2802
2748
|
const stored = readSiwxSession(address, id);
|
|
2803
2749
|
if (!jwt) {
|
|
2804
|
-
if (stored)
|
|
2805
|
-
clearSiwxSession(address, id);
|
|
2750
|
+
if (stored) clearSiwxSession(address, id);
|
|
2806
2751
|
return [];
|
|
2807
2752
|
}
|
|
2808
|
-
if (!stored)
|
|
2809
|
-
return [];
|
|
2753
|
+
if (!stored) return [];
|
|
2810
2754
|
const session = {
|
|
2811
2755
|
// `stored.data.chainId` is a plain string in the cache (kept AppKit-free);
|
|
2812
2756
|
// it always holds a valid CaipNetworkId here, so narrow at the boundary.
|