@owney/sdk 0.7.11 → 0.7.12-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-PWIKIQOX.js +140 -0
- package/dist/index.cjs +930 -222
- package/dist/index.d.cts +52 -2
- package/dist/index.d.ts +52 -2
- package/dist/index.js +249 -198
- package/dist/surfliquid.agent-VNGQUSXN.js +473 -0
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,14 @@ 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, err) => function __init() {
|
|
7
|
+
if (err) throw err[0];
|
|
8
|
+
try {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
} catch (e) {
|
|
11
|
+
throw err = [e], e;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
6
14
|
var __export = (target, all) => {
|
|
7
15
|
for (var name in all)
|
|
8
16
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -17,6 +25,645 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
25
|
};
|
|
18
26
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
27
|
|
|
28
|
+
// src/errors.ts
|
|
29
|
+
var OwneyError, AgentNotFoundError, NotConnectedError, AgentChainIncompatibleError;
|
|
30
|
+
var init_errors = __esm({
|
|
31
|
+
"src/errors.ts"() {
|
|
32
|
+
"use strict";
|
|
33
|
+
OwneyError = class extends Error {
|
|
34
|
+
code;
|
|
35
|
+
details;
|
|
36
|
+
agentId;
|
|
37
|
+
constructor(code, message, details, agentId) {
|
|
38
|
+
const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
|
|
39
|
+
super(`${prefix} ${message}`);
|
|
40
|
+
this.name = "OwneyError";
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.details = details;
|
|
43
|
+
this.agentId = agentId;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
AgentNotFoundError = class extends OwneyError {
|
|
47
|
+
constructor(agentId, available) {
|
|
48
|
+
super(
|
|
49
|
+
"AGENT_NOT_FOUND",
|
|
50
|
+
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
51
|
+
{ agentId, available },
|
|
52
|
+
agentId
|
|
53
|
+
);
|
|
54
|
+
this.name = "AgentNotFoundError";
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
NotConnectedError = class extends OwneyError {
|
|
58
|
+
constructor() {
|
|
59
|
+
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
60
|
+
this.name = "NotConnectedError";
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
AgentChainIncompatibleError = class extends OwneyError {
|
|
64
|
+
incompatibleAgents;
|
|
65
|
+
connectedChainId;
|
|
66
|
+
constructor(incompatibleAgents, connectedChainId) {
|
|
67
|
+
const details = incompatibleAgents.map(
|
|
68
|
+
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
69
|
+
).join("; ");
|
|
70
|
+
super(
|
|
71
|
+
"AGENT_CHAIN_INCOMPATIBLE",
|
|
72
|
+
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
73
|
+
{ incompatibleAgents, connectedChainId }
|
|
74
|
+
);
|
|
75
|
+
this.name = "AgentChainIncompatibleError";
|
|
76
|
+
this.incompatibleAgents = incompatibleAgents;
|
|
77
|
+
this.connectedChainId = connectedChainId;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// src/lib/permit2.ts
|
|
84
|
+
function buildPermitTransferFromTypedData(input) {
|
|
85
|
+
return {
|
|
86
|
+
domain: {
|
|
87
|
+
name: "Permit2",
|
|
88
|
+
chainId: input.chainId,
|
|
89
|
+
verifyingContract: PERMIT2_ADDRESS
|
|
90
|
+
},
|
|
91
|
+
types: {
|
|
92
|
+
PermitTransferFrom: [
|
|
93
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
94
|
+
{ name: "spender", type: "address" },
|
|
95
|
+
{ name: "nonce", type: "uint256" },
|
|
96
|
+
{ name: "deadline", type: "uint256" }
|
|
97
|
+
],
|
|
98
|
+
TokenPermissions: [
|
|
99
|
+
{ name: "token", type: "address" },
|
|
100
|
+
{ name: "amount", type: "uint256" }
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
primaryType: "PermitTransferFrom",
|
|
104
|
+
message: input.message
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function randomPermit2Nonce() {
|
|
108
|
+
const bytes = new Uint8Array(32);
|
|
109
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
110
|
+
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
111
|
+
}
|
|
112
|
+
async function readPermit2Allowance(publicClient, token, owner) {
|
|
113
|
+
return publicClient.readContract({
|
|
114
|
+
address: token,
|
|
115
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
116
|
+
functionName: "allowance",
|
|
117
|
+
args: [owner, PERMIT2_ADDRESS]
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
121
|
+
return publicClient.readContract({
|
|
122
|
+
address: token,
|
|
123
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
124
|
+
functionName: "balanceOf",
|
|
125
|
+
args: [owner]
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
var import_viem4, PERMIT2_ADDRESS, MAX_UINT256, ERC20_ALLOWANCE_ABI;
|
|
129
|
+
var init_permit2 = __esm({
|
|
130
|
+
"src/lib/permit2.ts"() {
|
|
131
|
+
"use strict";
|
|
132
|
+
import_viem4 = require("viem");
|
|
133
|
+
PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
134
|
+
MAX_UINT256 = 2n ** 256n - 1n;
|
|
135
|
+
ERC20_ALLOWANCE_ABI = [
|
|
136
|
+
{
|
|
137
|
+
type: "function",
|
|
138
|
+
name: "allowance",
|
|
139
|
+
stateMutability: "view",
|
|
140
|
+
inputs: [
|
|
141
|
+
{ name: "owner", type: "address" },
|
|
142
|
+
{ name: "spender", type: "address" }
|
|
143
|
+
],
|
|
144
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: "function",
|
|
148
|
+
name: "approve",
|
|
149
|
+
stateMutability: "nonpayable",
|
|
150
|
+
inputs: [
|
|
151
|
+
{ name: "spender", type: "address" },
|
|
152
|
+
{ name: "amount", type: "uint256" }
|
|
153
|
+
],
|
|
154
|
+
outputs: [{ name: "", type: "bool" }]
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
type: "function",
|
|
158
|
+
name: "balanceOf",
|
|
159
|
+
stateMutability: "view",
|
|
160
|
+
inputs: [{ name: "account", type: "address" }],
|
|
161
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
162
|
+
}
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// src/agents/surfliquid/surfliquid.constants.ts
|
|
168
|
+
var SURFLIQUID_CHAIN_ID, SURFLIQUID_CHAIN_NAME, SURFLIQUID_USDC_ADDRESS, SURFLIQUID_USDC_DECIMALS, SURFLIQUID_MIN_DEPOSIT, SURFLIQUID_APPROVAL_TARGET, SURFLIQUID_SUPPORTED_ASSETS, SURFLIQUID_ACTION_MAP;
|
|
169
|
+
var init_surfliquid_constants = __esm({
|
|
170
|
+
"src/agents/surfliquid/surfliquid.constants.ts"() {
|
|
171
|
+
"use strict";
|
|
172
|
+
SURFLIQUID_CHAIN_ID = 8453;
|
|
173
|
+
SURFLIQUID_CHAIN_NAME = "BASE";
|
|
174
|
+
SURFLIQUID_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
175
|
+
SURFLIQUID_USDC_DECIMALS = 6;
|
|
176
|
+
SURFLIQUID_MIN_DEPOSIT = "0";
|
|
177
|
+
SURFLIQUID_APPROVAL_TARGET = 100000000000n;
|
|
178
|
+
SURFLIQUID_SUPPORTED_ASSETS = [
|
|
179
|
+
{
|
|
180
|
+
chainId: SURFLIQUID_CHAIN_ID,
|
|
181
|
+
chain: SURFLIQUID_CHAIN_NAME,
|
|
182
|
+
assets: [{ symbol: "USDC", minDepositAmount: SURFLIQUID_MIN_DEPOSIT }]
|
|
183
|
+
}
|
|
184
|
+
];
|
|
185
|
+
SURFLIQUID_ACTION_MAP = {
|
|
186
|
+
// INITIAL_DEPOSIT is the user's real first deposit, so surface it like any
|
|
187
|
+
// other funding ("Top up"). The mini-app intentionally hides the "Deposit"
|
|
188
|
+
// action (it marks Zyfai's smart-account-creation event), which would
|
|
189
|
+
// otherwise drop a SurfLiquid user's only history row.
|
|
190
|
+
INITIAL_DEPOSIT: "Top up",
|
|
191
|
+
DEPOSIT: "Top up",
|
|
192
|
+
USER_DEPOSIT: "Top up",
|
|
193
|
+
WITHDRAWAL: "Withdraw",
|
|
194
|
+
USER_WITHDRAWAL: "Withdraw",
|
|
195
|
+
REBALANCE: "Rebalance",
|
|
196
|
+
REBALANCE_COMPLETED: "Rebalance",
|
|
197
|
+
CROSS_CHAIN_REBALANCE: "Rebalance",
|
|
198
|
+
MERKL_CLAIM: "Earned"
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// src/agents/surfliquid/surfliquid.mapper.ts
|
|
204
|
+
function mapMessage(message) {
|
|
205
|
+
const action = SURFLIQUID_ACTION_MAP[message.transactionType];
|
|
206
|
+
if (!action) return null;
|
|
207
|
+
return {
|
|
208
|
+
agent: "surfliquid",
|
|
209
|
+
action,
|
|
210
|
+
date: message.timestamp,
|
|
211
|
+
oldApy: message.apyBefore != null ? String(message.apyBefore) : null,
|
|
212
|
+
newApy: message.apyAfter != null ? String(message.apyAfter) : null,
|
|
213
|
+
transactions: [
|
|
214
|
+
{
|
|
215
|
+
txHashes: [message.txHash],
|
|
216
|
+
chainId: message.chainId,
|
|
217
|
+
tokenSymbol: message.token ?? void 0,
|
|
218
|
+
amount: message.amount != null ? String(message.amount) : void 0
|
|
219
|
+
}
|
|
220
|
+
],
|
|
221
|
+
rebalanceLog: []
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function mapHistory(result, _chainId) {
|
|
225
|
+
const data = result.messages.map(mapMessage).filter((entry) => entry !== null);
|
|
226
|
+
const hasMore = result.page < result.pages;
|
|
227
|
+
return {
|
|
228
|
+
data,
|
|
229
|
+
hasMore,
|
|
230
|
+
nextCursor: hasMore ? String(result.page + 1) : void 0
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function mapBalances2(vault, chainId, morphoStats) {
|
|
234
|
+
const assets = (vault.assets ?? []).filter(
|
|
235
|
+
(asset) => asset.assetSymbol === "USDC" && asset.chainId === chainId
|
|
236
|
+
);
|
|
237
|
+
const tokens = assets.map((asset) => ({
|
|
238
|
+
chain: SURFLIQUID_CHAIN_NAME,
|
|
239
|
+
chainId: SURFLIQUID_CHAIN_ID,
|
|
240
|
+
asset: "USDC",
|
|
241
|
+
amount: String(asset.currentValueUSD)
|
|
242
|
+
}));
|
|
243
|
+
const positions = assets.map((asset) => {
|
|
244
|
+
const stats = morphoStats?.get(asset.morphoVaultAddress?.toLowerCase() ?? "");
|
|
245
|
+
return {
|
|
246
|
+
chain: SURFLIQUID_CHAIN_NAME,
|
|
247
|
+
protocol: "Morpho",
|
|
248
|
+
// The specific MetaMorpho vault (e.g. "Gauntlet USDC Frontier"), read from
|
|
249
|
+
// Morpho — renders as "Morpho <vault>" alongside zyfai's vault detail.
|
|
250
|
+
pool: stats?.name ?? void 0,
|
|
251
|
+
asset: "USDC",
|
|
252
|
+
amount: String(asset.currentValueUSD),
|
|
253
|
+
apy: asset.currentAPY,
|
|
254
|
+
// TVL + liquidity come straight from Morpho (SurfLiquid's API omits them).
|
|
255
|
+
tvl: stats?.tvlUsd,
|
|
256
|
+
liquidity: stats?.liquidityUsd
|
|
257
|
+
};
|
|
258
|
+
});
|
|
259
|
+
const total = assets.reduce(
|
|
260
|
+
(sum, asset) => sum + asset.currentValueUSD,
|
|
261
|
+
0
|
|
262
|
+
);
|
|
263
|
+
return {
|
|
264
|
+
smartWallet: vault.userVaultAddress ?? void 0,
|
|
265
|
+
totalBalance: String(total),
|
|
266
|
+
totalBalanceAsset: "usdc",
|
|
267
|
+
tokens,
|
|
268
|
+
positions
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function mapAccountApy(vault, days) {
|
|
272
|
+
const breakdown = vault.apyBreakdown;
|
|
273
|
+
const windowed = breakdown ? breakdown[APY_WINDOW_KEY[days]] : void 0;
|
|
274
|
+
const apy = windowed != null ? windowed : breakdown?.currentAPY ?? 0;
|
|
275
|
+
return {
|
|
276
|
+
walletAddress: vault.walletAddress ?? "",
|
|
277
|
+
weightedApyAfterFee: apy,
|
|
278
|
+
apyByChainAndAsset: { [SURFLIQUID_CHAIN_ID]: { USDC: apy } },
|
|
279
|
+
// Parity with Sail: SurfLiquid's core-sdk exposes no daily net-APY series.
|
|
280
|
+
history: []
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function mapEarnings2(vault, vaultAddress) {
|
|
284
|
+
return {
|
|
285
|
+
smartWallet: vaultAddress,
|
|
286
|
+
lifetimeEarnings: vault.earned?.totalEarningsUSD ?? 0,
|
|
287
|
+
tokens: []
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function mapUserProfile2(vault, address) {
|
|
291
|
+
return {
|
|
292
|
+
address,
|
|
293
|
+
smartWallet: vault.userVaultAddress ?? "",
|
|
294
|
+
chains: [SURFLIQUID_CHAIN_ID],
|
|
295
|
+
// SurfLiquid uses cookie-based session auth — no session-key concept.
|
|
296
|
+
hasActiveSessionKey: false,
|
|
297
|
+
protocols: ["SurfLiquid"]
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
var APY_WINDOW_KEY;
|
|
301
|
+
var init_surfliquid_mapper = __esm({
|
|
302
|
+
"src/agents/surfliquid/surfliquid.mapper.ts"() {
|
|
303
|
+
"use strict";
|
|
304
|
+
init_surfliquid_constants();
|
|
305
|
+
APY_WINDOW_KEY = {
|
|
306
|
+
"7D": "apy7d",
|
|
307
|
+
"14D": "apy14d",
|
|
308
|
+
"30D": "apy30d"
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// src/agents/surfliquid/surfliquid.wallet-adapter.ts
|
|
314
|
+
var import_core_sdk, OwneyWalletAdapter;
|
|
315
|
+
var init_surfliquid_wallet_adapter = __esm({
|
|
316
|
+
"src/agents/surfliquid/surfliquid.wallet-adapter.ts"() {
|
|
317
|
+
"use strict";
|
|
318
|
+
import_core_sdk = require("@surf_liquid/core-sdk");
|
|
319
|
+
OwneyWalletAdapter = class extends import_core_sdk.BaseWalletAdapter {
|
|
320
|
+
constructor(eip1193Provider) {
|
|
321
|
+
super();
|
|
322
|
+
this.eip1193Provider = eip1193Provider;
|
|
323
|
+
}
|
|
324
|
+
eip1193Provider;
|
|
325
|
+
name = "owney";
|
|
326
|
+
get installed() {
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
resolveProvider() {
|
|
330
|
+
return this.eip1193Provider;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// src/agents/surfliquid/surfliquid.morpho.ts
|
|
337
|
+
async function queryMorpho(query, vaultAddress, chainId) {
|
|
338
|
+
try {
|
|
339
|
+
const res = await fetch(MORPHO_API_URL, {
|
|
340
|
+
method: "POST",
|
|
341
|
+
headers: { "Content-Type": "application/json" },
|
|
342
|
+
body: JSON.stringify({
|
|
343
|
+
query,
|
|
344
|
+
variables: { address: vaultAddress, chainId }
|
|
345
|
+
})
|
|
346
|
+
});
|
|
347
|
+
if (!res.ok) return null;
|
|
348
|
+
const json = await res.json();
|
|
349
|
+
if (json.errors) return null;
|
|
350
|
+
return json.data ?? null;
|
|
351
|
+
} catch (error) {
|
|
352
|
+
console.warn("surfliquid: Morpho vault stats fetch failed", {
|
|
353
|
+
vaultAddress,
|
|
354
|
+
chainId,
|
|
355
|
+
error
|
|
356
|
+
});
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function toStats(name, tvlUsd, liquidityUsd) {
|
|
361
|
+
if (typeof tvlUsd !== "number" || typeof liquidityUsd !== "number") {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
name: typeof name === "string" ? name : null,
|
|
366
|
+
tvlUsd,
|
|
367
|
+
liquidityUsd
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
async function fetchMorphoVaultStats(vaultAddress, chainId) {
|
|
371
|
+
const v1 = await queryMorpho(VAULT_STATS_QUERY, vaultAddress, chainId);
|
|
372
|
+
const vault = v1?.vaultByAddress;
|
|
373
|
+
const v1Stats = toStats(
|
|
374
|
+
vault?.name,
|
|
375
|
+
vault?.state?.totalAssetsUsd,
|
|
376
|
+
vault?.liquidity?.usd
|
|
377
|
+
);
|
|
378
|
+
if (v1Stats) return v1Stats;
|
|
379
|
+
const v2 = await queryMorpho(VAULT_V2_STATS_QUERY, vaultAddress, chainId);
|
|
380
|
+
const vaultV2 = v2?.vaultV2ByAddress;
|
|
381
|
+
return toStats(vaultV2?.name, vaultV2?.totalAssetsUsd, vaultV2?.liquidityUsd);
|
|
382
|
+
}
|
|
383
|
+
async function fetchMorphoStatsByVault(vaultAddresses, chainId) {
|
|
384
|
+
const unique = [...new Set(vaultAddresses.map((a) => a.toLowerCase()))];
|
|
385
|
+
const entries = await Promise.all(
|
|
386
|
+
unique.map(
|
|
387
|
+
async (address) => [address, await fetchMorphoVaultStats(address, chainId)]
|
|
388
|
+
)
|
|
389
|
+
);
|
|
390
|
+
const byVault = /* @__PURE__ */ new Map();
|
|
391
|
+
for (const [address, stats] of entries) {
|
|
392
|
+
if (stats) byVault.set(address, stats);
|
|
393
|
+
}
|
|
394
|
+
return byVault;
|
|
395
|
+
}
|
|
396
|
+
var MORPHO_API_URL, VAULT_STATS_QUERY, VAULT_V2_STATS_QUERY;
|
|
397
|
+
var init_surfliquid_morpho = __esm({
|
|
398
|
+
"src/agents/surfliquid/surfliquid.morpho.ts"() {
|
|
399
|
+
"use strict";
|
|
400
|
+
MORPHO_API_URL = "https://api.morpho.org/graphql";
|
|
401
|
+
VAULT_STATS_QUERY = `
|
|
402
|
+
query VaultStats($address: String!, $chainId: Int!) {
|
|
403
|
+
vaultByAddress(address: $address, chainId: $chainId) {
|
|
404
|
+
name
|
|
405
|
+
state { totalAssetsUsd }
|
|
406
|
+
liquidity { usd }
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
`;
|
|
410
|
+
VAULT_V2_STATS_QUERY = `
|
|
411
|
+
query VaultV2Stats($address: String!, $chainId: Int!) {
|
|
412
|
+
vaultV2ByAddress(address: $address, chainId: $chainId) {
|
|
413
|
+
name
|
|
414
|
+
totalAssetsUsd
|
|
415
|
+
liquidityUsd
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
`;
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// src/agents/surfliquid/surfliquid.agent.ts
|
|
423
|
+
var surfliquid_agent_exports = {};
|
|
424
|
+
__export(surfliquid_agent_exports, {
|
|
425
|
+
SurfLiquidAgent: () => SurfLiquidAgent
|
|
426
|
+
});
|
|
427
|
+
var import_core_sdk2, import_viem6, import_chains2, APY_WINDOW_KEY2, SurfLiquidAgent;
|
|
428
|
+
var init_surfliquid_agent = __esm({
|
|
429
|
+
"src/agents/surfliquid/surfliquid.agent.ts"() {
|
|
430
|
+
"use strict";
|
|
431
|
+
import_core_sdk2 = require("@surf_liquid/core-sdk");
|
|
432
|
+
import_viem6 = require("viem");
|
|
433
|
+
import_chains2 = require("viem/chains");
|
|
434
|
+
init_errors();
|
|
435
|
+
init_surfliquid_constants();
|
|
436
|
+
init_permit2();
|
|
437
|
+
init_surfliquid_mapper();
|
|
438
|
+
init_surfliquid_wallet_adapter();
|
|
439
|
+
init_surfliquid_morpho();
|
|
440
|
+
APY_WINDOW_KEY2 = {
|
|
441
|
+
"7D": "apy7d",
|
|
442
|
+
"14D": "apy14d",
|
|
443
|
+
"30D": "apy30d"
|
|
444
|
+
};
|
|
445
|
+
SurfLiquidAgent = class {
|
|
446
|
+
constructor(config) {
|
|
447
|
+
this.config = config;
|
|
448
|
+
}
|
|
449
|
+
config;
|
|
450
|
+
id = "surfliquid";
|
|
451
|
+
supportedChainIds = [SURFLIQUID_CHAIN_ID];
|
|
452
|
+
supportedAssets = SURFLIQUID_SUPPORTED_ASSETS;
|
|
453
|
+
surf = null;
|
|
454
|
+
connectedAddress = null;
|
|
455
|
+
// In-flight connect+auth, shared by concurrent callers to dedupe SIWE.
|
|
456
|
+
connectPromise = null;
|
|
457
|
+
ensureSurf() {
|
|
458
|
+
if (this.surf) return this.surf;
|
|
459
|
+
this.surf = import_core_sdk2.SurfClient.create({
|
|
460
|
+
projectName: this.config.projectName,
|
|
461
|
+
appId: this.config.appId,
|
|
462
|
+
environment: "mainnet",
|
|
463
|
+
chainId: SURFLIQUID_CHAIN_ID,
|
|
464
|
+
apiBaseUrl: this.config.apiBaseUrl,
|
|
465
|
+
rpcUrl: this.config.rpcUrl,
|
|
466
|
+
// Let the SDK auto-approve USDC to the vault before depositing.
|
|
467
|
+
autoApprove: true
|
|
468
|
+
});
|
|
469
|
+
return this.surf;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Connects + authenticates the SurfClient against owney's provider. Skips the
|
|
473
|
+
* SIWE prompt when already authenticated for this wallet (cookie session).
|
|
474
|
+
*/
|
|
475
|
+
async connect(state) {
|
|
476
|
+
const surf = this.ensureSurf();
|
|
477
|
+
const sameWallet = this.connectedAddress?.toLowerCase() === state.walletAddress.toLowerCase();
|
|
478
|
+
if (sameWallet && surf.getAuthState().authenticated) {
|
|
479
|
+
return surf;
|
|
480
|
+
}
|
|
481
|
+
if (this.connectPromise) return this.connectPromise;
|
|
482
|
+
this.connectPromise = (async () => {
|
|
483
|
+
surf.registerWalletAdapter(
|
|
484
|
+
"owney",
|
|
485
|
+
new OwneyWalletAdapter(state.provider)
|
|
486
|
+
);
|
|
487
|
+
await surf.connectWallet("owney");
|
|
488
|
+
await surf.authenticate();
|
|
489
|
+
this.connectedAddress = state.walletAddress;
|
|
490
|
+
return surf;
|
|
491
|
+
})();
|
|
492
|
+
try {
|
|
493
|
+
return await this.connectPromise;
|
|
494
|
+
} finally {
|
|
495
|
+
this.connectPromise = null;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Returns the user's vault address, deploying one on first use. SurfLiquid's
|
|
500
|
+
* deposit requires the vault to exist, so this must run before depositing.
|
|
501
|
+
* Existing vault → returned as-is; otherwise `deployVault()` is sent
|
|
502
|
+
* (user-pays) and the freshly deployed address is returned.
|
|
503
|
+
*/
|
|
504
|
+
async resolveVaultAddress(surf, owner) {
|
|
505
|
+
const vault = await surf.getVault(owner).catch(() => null);
|
|
506
|
+
if (vault?.exists && vault.userVaultAddress) {
|
|
507
|
+
return vault.userVaultAddress;
|
|
508
|
+
}
|
|
509
|
+
const { vaultAddress } = await surf.deployVault();
|
|
510
|
+
return vaultAddress;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Grants the user's vault a standing USDC allowance so SurfLiquid's
|
|
514
|
+
* `autoApprove` has nothing to do — see {@link SURFLIQUID_APPROVAL_TARGET}
|
|
515
|
+
* for why one prompt per deposit is worth removing.
|
|
516
|
+
*
|
|
517
|
+
* Fails OPEN on a read error: if the allowance can't be checked we simply
|
|
518
|
+
* return and let SurfLiquid's own per-deposit approve run, because a flaky
|
|
519
|
+
* RPC must never block a deposit. A failed or rejected APPROVE does
|
|
520
|
+
* propagate — the user declined a transaction, and letting SurfLiquid
|
|
521
|
+
* immediately ask again for a smaller one is exactly the double prompt this
|
|
522
|
+
* removes.
|
|
523
|
+
*/
|
|
524
|
+
async ensureVaultAllowance(state, vaultAddress, amount) {
|
|
525
|
+
const spender = vaultAddress;
|
|
526
|
+
const transport = (0, import_viem6.custom)(state.provider);
|
|
527
|
+
const publicClient = (0, import_viem6.createPublicClient)({ chain: import_chains2.base, transport });
|
|
528
|
+
let allowance;
|
|
529
|
+
try {
|
|
530
|
+
allowance = await publicClient.readContract({
|
|
531
|
+
address: SURFLIQUID_USDC_ADDRESS,
|
|
532
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
533
|
+
functionName: "allowance",
|
|
534
|
+
args: [state.walletAddress, spender]
|
|
535
|
+
});
|
|
536
|
+
} catch (error) {
|
|
537
|
+
console.warn(
|
|
538
|
+
"[owney-sdk] SurfLiquid allowance pre-check failed; deferring to its per-deposit approve:",
|
|
539
|
+
error instanceof Error ? error.message : String(error)
|
|
540
|
+
);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (allowance >= amount) return;
|
|
544
|
+
const target = amount > SURFLIQUID_APPROVAL_TARGET ? amount : SURFLIQUID_APPROVAL_TARGET;
|
|
545
|
+
const wallet = (0, import_viem6.createWalletClient)({
|
|
546
|
+
account: state.walletAddress,
|
|
547
|
+
chain: import_chains2.base,
|
|
548
|
+
transport
|
|
549
|
+
});
|
|
550
|
+
const hash = await wallet.writeContract({
|
|
551
|
+
address: SURFLIQUID_USDC_ADDRESS,
|
|
552
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
553
|
+
functionName: "approve",
|
|
554
|
+
args: [spender, target],
|
|
555
|
+
account: state.walletAddress,
|
|
556
|
+
chain: import_chains2.base
|
|
557
|
+
});
|
|
558
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
559
|
+
hash,
|
|
560
|
+
confirmations: 1
|
|
561
|
+
});
|
|
562
|
+
if (receipt.status !== "success") {
|
|
563
|
+
throw new Error(`SurfLiquid USDC approval reverted (tx ${hash})`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// --- IAgent: lifecycle ---
|
|
567
|
+
async disconnect() {
|
|
568
|
+
if (this.surf) {
|
|
569
|
+
await this.surf.disconnectWallet();
|
|
570
|
+
}
|
|
571
|
+
this.connectedAddress = null;
|
|
572
|
+
}
|
|
573
|
+
async activateAgent(state) {
|
|
574
|
+
await this.connect(state);
|
|
575
|
+
}
|
|
576
|
+
// --- IAgent: funds (user-pays; asset is always USDC, callback ignored) ---
|
|
577
|
+
async deposit(state, _chainId, amount, _asset, _depositCallback) {
|
|
578
|
+
const surf = await this.connect(state);
|
|
579
|
+
const human = (0, import_viem6.formatUnits)(BigInt(amount), SURFLIQUID_USDC_DECIMALS);
|
|
580
|
+
const smartWallet = await this.resolveVaultAddress(
|
|
581
|
+
surf,
|
|
582
|
+
state.walletAddress
|
|
583
|
+
);
|
|
584
|
+
await this.ensureVaultAllowance(state, smartWallet, BigInt(amount));
|
|
585
|
+
const tx = await surf.deposit({
|
|
586
|
+
asset: SURFLIQUID_USDC_ADDRESS,
|
|
587
|
+
amount: human
|
|
588
|
+
});
|
|
589
|
+
await tx.wait();
|
|
590
|
+
return { txHash: tx.hash, smartWallet, amount };
|
|
591
|
+
}
|
|
592
|
+
async withdraw(state, _chainId, _token, amount) {
|
|
593
|
+
const surf = await this.connect(state);
|
|
594
|
+
const human = amount != null ? (0, import_viem6.formatUnits)(BigInt(amount), SURFLIQUID_USDC_DECIMALS) : void 0;
|
|
595
|
+
try {
|
|
596
|
+
const tx = await surf.withdraw({
|
|
597
|
+
asset: SURFLIQUID_USDC_ADDRESS,
|
|
598
|
+
amount: human
|
|
599
|
+
});
|
|
600
|
+
await tx.wait();
|
|
601
|
+
return {
|
|
602
|
+
txHash: tx.hash,
|
|
603
|
+
type: amount != null ? "partial" : "full",
|
|
604
|
+
amount: amount ?? "0"
|
|
605
|
+
};
|
|
606
|
+
} catch (error) {
|
|
607
|
+
throw new OwneyError(
|
|
608
|
+
"WITHDRAW_FAILED",
|
|
609
|
+
error instanceof Error ? error.message : "SurfLiquid withdraw failed.",
|
|
610
|
+
{ chainId: SURFLIQUID_CHAIN_ID, amount },
|
|
611
|
+
this.id
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
// --- IAgent: portfolio reads ---
|
|
616
|
+
async getBalances(state, chainId) {
|
|
617
|
+
const surf = await this.connect(state);
|
|
618
|
+
const vault = await surf.getVault(state.walletAddress);
|
|
619
|
+
const morphoVaultAddresses = (vault.assets ?? []).filter((asset) => asset.assetSymbol === "USDC" && asset.chainId === chainId).map((asset) => asset.morphoVaultAddress).filter((address) => Boolean(address));
|
|
620
|
+
const morphoStats = await fetchMorphoStatsByVault(
|
|
621
|
+
morphoVaultAddresses,
|
|
622
|
+
chainId
|
|
623
|
+
);
|
|
624
|
+
return mapBalances2(vault, chainId, morphoStats);
|
|
625
|
+
}
|
|
626
|
+
async getEarnings(state) {
|
|
627
|
+
const surf = await this.connect(state);
|
|
628
|
+
const vault = await surf.getVault(state.walletAddress);
|
|
629
|
+
return mapEarnings2(vault, vault.userVaultAddress ?? state.walletAddress);
|
|
630
|
+
}
|
|
631
|
+
async getAccountApy(state, _chainId, days, _tokenSymbol) {
|
|
632
|
+
const surf = await this.connect(state);
|
|
633
|
+
const vault = await surf.getVault(state.walletAddress);
|
|
634
|
+
return mapAccountApy(vault, days);
|
|
635
|
+
}
|
|
636
|
+
async getHistory(state, chainId, options) {
|
|
637
|
+
const surf = await this.connect(state);
|
|
638
|
+
const page = options?.cursor ? Number(options.cursor) : 1;
|
|
639
|
+
const limit = options?.limit ?? 10;
|
|
640
|
+
const result = await surf.getAgentMessages(
|
|
641
|
+
state.walletAddress,
|
|
642
|
+
page,
|
|
643
|
+
limit,
|
|
644
|
+
options?.fromDate,
|
|
645
|
+
options?.toDate
|
|
646
|
+
);
|
|
647
|
+
return mapHistory(result, chainId);
|
|
648
|
+
}
|
|
649
|
+
async getUserProfile(state) {
|
|
650
|
+
const surf = await this.connect(state);
|
|
651
|
+
const vault = await surf.getVault(state.walletAddress);
|
|
652
|
+
return mapUserProfile2(vault, state.walletAddress);
|
|
653
|
+
}
|
|
654
|
+
// --- IAgent: discovery (no wallet) ---
|
|
655
|
+
async getAgentApy(days, _options) {
|
|
656
|
+
const surf = this.ensureSurf();
|
|
657
|
+
const assets = await surf.getSupportedAssets(SURFLIQUID_CHAIN_ID);
|
|
658
|
+
const usdc = assets.find((asset) => asset.assetSymbol === "USDC");
|
|
659
|
+
if (!usdc) return { averageApy: 0 };
|
|
660
|
+
const windowed = usdc[APY_WINDOW_KEY2[days]];
|
|
661
|
+
return { averageApy: windowed != null ? windowed : usdc.currentAPY };
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
|
|
20
667
|
// src/index.ts
|
|
21
668
|
var index_exports = {};
|
|
22
669
|
__export(index_exports, {
|
|
@@ -89,56 +736,8 @@ var SupportedAssets = [
|
|
|
89
736
|
}
|
|
90
737
|
];
|
|
91
738
|
|
|
92
|
-
// src/errors.ts
|
|
93
|
-
var OwneyError = class extends Error {
|
|
94
|
-
code;
|
|
95
|
-
details;
|
|
96
|
-
agentId;
|
|
97
|
-
constructor(code, message, details, agentId) {
|
|
98
|
-
const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
|
|
99
|
-
super(`${prefix} ${message}`);
|
|
100
|
-
this.name = "OwneyError";
|
|
101
|
-
this.code = code;
|
|
102
|
-
this.details = details;
|
|
103
|
-
this.agentId = agentId;
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
var AgentNotFoundError = class extends OwneyError {
|
|
107
|
-
constructor(agentId, available) {
|
|
108
|
-
super(
|
|
109
|
-
"AGENT_NOT_FOUND",
|
|
110
|
-
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
111
|
-
{ agentId, available },
|
|
112
|
-
agentId
|
|
113
|
-
);
|
|
114
|
-
this.name = "AgentNotFoundError";
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
var NotConnectedError = class extends OwneyError {
|
|
118
|
-
constructor() {
|
|
119
|
-
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
120
|
-
this.name = "NotConnectedError";
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
var AgentChainIncompatibleError = class extends OwneyError {
|
|
124
|
-
incompatibleAgents;
|
|
125
|
-
connectedChainId;
|
|
126
|
-
constructor(incompatibleAgents, connectedChainId) {
|
|
127
|
-
const details = incompatibleAgents.map(
|
|
128
|
-
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
129
|
-
).join("; ");
|
|
130
|
-
super(
|
|
131
|
-
"AGENT_CHAIN_INCOMPATIBLE",
|
|
132
|
-
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
133
|
-
{ incompatibleAgents, connectedChainId }
|
|
134
|
-
);
|
|
135
|
-
this.name = "AgentChainIncompatibleError";
|
|
136
|
-
this.incompatibleAgents = incompatibleAgents;
|
|
137
|
-
this.connectedChainId = connectedChainId;
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
|
|
141
739
|
// src/lib/utils.ts
|
|
740
|
+
init_errors();
|
|
142
741
|
var isValidChainId = (chainId) => {
|
|
143
742
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
|
|
144
743
|
throw new OwneyError(
|
|
@@ -149,6 +748,12 @@ var isValidChainId = (chainId) => {
|
|
|
149
748
|
}
|
|
150
749
|
return chainId;
|
|
151
750
|
};
|
|
751
|
+
function isSameAsset(tokenSymbol, asset) {
|
|
752
|
+
const token = tokenSymbol.toLowerCase();
|
|
753
|
+
const target = asset.toLowerCase();
|
|
754
|
+
if (token === target) return true;
|
|
755
|
+
return target === "weth" && token === "eth" || target === "eth" && token === "weth";
|
|
756
|
+
}
|
|
152
757
|
function hexToDecimal(hex, decimals = 6) {
|
|
153
758
|
const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
|
|
154
759
|
const parsed = BigInt(normalized);
|
|
@@ -182,6 +787,10 @@ var DECIMALS_BY_SYMBOL = {
|
|
|
182
787
|
wbtc: 8,
|
|
183
788
|
cbbtc: 8
|
|
184
789
|
};
|
|
790
|
+
function canonicalSymbol(symbol) {
|
|
791
|
+
const upper = symbol.toUpperCase();
|
|
792
|
+
return upper === "ETH" ? "WETH" : upper;
|
|
793
|
+
}
|
|
185
794
|
function tokenDecimals(symbol, explicit) {
|
|
186
795
|
if (typeof explicit === "number" && Number.isFinite(explicit))
|
|
187
796
|
return explicit;
|
|
@@ -246,55 +855,89 @@ function mapPendingAllocations(raw) {
|
|
|
246
855
|
function mapBalances(raw, _chainId, smartWallet) {
|
|
247
856
|
const portfolio = raw.portfolio;
|
|
248
857
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
858
|
+
const rawPositions = portfolio.positions ?? [];
|
|
859
|
+
const detailByKey = /* @__PURE__ */ new Map();
|
|
860
|
+
for (const p of rawPositions) {
|
|
861
|
+
const cid = resolveChainId(p.chain ?? "");
|
|
862
|
+
const symbol = canonicalSymbol(p.token_symbol ?? "");
|
|
863
|
+
if (Number.isFinite(cid) && symbol) detailByKey.set(`${cid}:${symbol}`, p);
|
|
864
|
+
}
|
|
249
865
|
let totalBalance = 0;
|
|
250
866
|
const tokens = [];
|
|
867
|
+
const positions = [];
|
|
251
868
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
|
|
252
869
|
const positionChainId = Number(chainIdKey);
|
|
870
|
+
const chainName2 = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
|
|
253
871
|
for (const [
|
|
254
872
|
symbol,
|
|
255
873
|
{ balance, balanceWithFee, decimals }
|
|
256
874
|
] of Object.entries(tokensBySymbol ?? {})) {
|
|
257
|
-
const amount = hexToDecimal(
|
|
875
|
+
const amount = hexToDecimal(
|
|
876
|
+
balanceWithFee ?? balance ?? "0",
|
|
877
|
+
tokenDecimals(symbol, decimals)
|
|
878
|
+
);
|
|
258
879
|
totalBalance += amount;
|
|
259
880
|
tokens.push({
|
|
260
|
-
chain:
|
|
881
|
+
chain: chainName2,
|
|
261
882
|
chainId: positionChainId,
|
|
262
883
|
asset: symbol,
|
|
263
884
|
amount: String(amount)
|
|
264
885
|
});
|
|
886
|
+
const detail = detailByKey.get(
|
|
887
|
+
`${positionChainId}:${canonicalSymbol(symbol)}`
|
|
888
|
+
);
|
|
889
|
+
positions.push({
|
|
890
|
+
chain: chainName2,
|
|
891
|
+
protocol: detail?.protocol_name ?? detail?.protocol_id ?? "",
|
|
892
|
+
protocolId: detail?.protocol_id,
|
|
893
|
+
pool: detail?.pool,
|
|
894
|
+
// Report the token the way the rest of owney spells it ("WETH", as the
|
|
895
|
+
// pre-portfolioByChain mapper did), not Zyfai's internal "eth".
|
|
896
|
+
asset: canonicalSymbol(symbol),
|
|
897
|
+
amount: String(amount),
|
|
898
|
+
apy: detail?.pool_apy,
|
|
899
|
+
tvl: detail?.pool_tvl,
|
|
900
|
+
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
901
|
+
// structural read keeps it undefined today and auto-populates later.
|
|
902
|
+
liquidity: detail?.liquidity
|
|
903
|
+
});
|
|
265
904
|
}
|
|
266
905
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
906
|
+
if (positions.length === 0) {
|
|
907
|
+
positions.push(
|
|
908
|
+
...(portfolio.positions ?? []).map((p) => ({
|
|
909
|
+
chain: p.chain ?? "",
|
|
910
|
+
protocol: p.protocol_name ?? p.protocol_id ?? "",
|
|
911
|
+
protocolId: p.protocol_id,
|
|
912
|
+
pool: p.pool,
|
|
913
|
+
asset: p.token_symbol ?? "",
|
|
914
|
+
// Fee-adjusted underlying amounts are decimal base-unit strings; the
|
|
915
|
+
// legacy `amount` fallback is hex. Runtime getBalances payloads often
|
|
916
|
+
// carry `decimals` (e.g. 18 for WETH) — prefer it when present,
|
|
917
|
+
// otherwise fall back to the per-symbol lookup.
|
|
918
|
+
amount: String(
|
|
919
|
+
p.underlyingAmountWithFee ?? p.underlyingAmount ? normalizeDecimalPlace(
|
|
920
|
+
p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0",
|
|
921
|
+
tokenDecimals(
|
|
922
|
+
p.token_symbol,
|
|
923
|
+
p.decimals
|
|
924
|
+
)
|
|
925
|
+
) : hexToDecimal(
|
|
926
|
+
p.amount || "0",
|
|
927
|
+
tokenDecimals(
|
|
928
|
+
p.token_symbol,
|
|
929
|
+
p.decimals
|
|
930
|
+
)
|
|
931
|
+
)
|
|
932
|
+
),
|
|
933
|
+
apy: p.pool_apy,
|
|
934
|
+
tvl: p.pool_tvl,
|
|
935
|
+
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
936
|
+
// structural read keeps it undefined today and auto-populates later.
|
|
937
|
+
liquidity: p.liquidity
|
|
938
|
+
}))
|
|
939
|
+
);
|
|
940
|
+
}
|
|
298
941
|
const pendingAllocations = mapPendingAllocations(
|
|
299
942
|
portfolio.staleBalances
|
|
300
943
|
);
|
|
@@ -589,6 +1232,9 @@ function decodeHistoryCursor(token) {
|
|
|
589
1232
|
return parsed;
|
|
590
1233
|
}
|
|
591
1234
|
|
|
1235
|
+
// src/agents/zyfai/zyfai.agent.ts
|
|
1236
|
+
init_errors();
|
|
1237
|
+
|
|
592
1238
|
// src/agents/zyfai/zyfai.auth-cache.ts
|
|
593
1239
|
var KEY_PREFIX = "owney.zyfai.session";
|
|
594
1240
|
var storage = () => {
|
|
@@ -1377,6 +2023,37 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1377
2023
|
);
|
|
1378
2024
|
}
|
|
1379
2025
|
}
|
|
2026
|
+
/**
|
|
2027
|
+
* Records the deposit with Zyfai, retrying transient failures.
|
|
2028
|
+
*
|
|
2029
|
+
* This runs AFTER the transfer has already landed on-chain, so it must never
|
|
2030
|
+
* fail the deposit — the funds moved. But it is also the ONLY source of the
|
|
2031
|
+
* "Top up wallet" entry the history is built from: Zyfai auto-deploys the
|
|
2032
|
+
* balance it detects either way, so when this call is lost the user's deposit
|
|
2033
|
+
* never appears in Activity (an earlier withdrawal stays the newest row) and
|
|
2034
|
+
* nothing ever backfills it. Retry, then log loudly enough to be recoverable.
|
|
2035
|
+
*/
|
|
2036
|
+
async logDepositWithRetry(chainId, txHash, amount, tokenAddress) {
|
|
2037
|
+
const ATTEMPTS = 3;
|
|
2038
|
+
const RETRY_DELAY_MS = 1e3;
|
|
2039
|
+
for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
|
|
2040
|
+
try {
|
|
2041
|
+
await (tokenAddress ? this.sdk.logDeposit(chainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(chainId, txHash, amount));
|
|
2042
|
+
return;
|
|
2043
|
+
} catch (logError) {
|
|
2044
|
+
if (attempt === ATTEMPTS) {
|
|
2045
|
+
console.error(
|
|
2046
|
+
"[owney-sdk] Deposit landed on-chain but logDeposit failed \u2014 it will be missing from Zyfai history:",
|
|
2047
|
+
{ txHash, chainId, amount, tokenAddress, error: logError }
|
|
2048
|
+
);
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
2051
|
+
await new Promise(
|
|
2052
|
+
(resolve) => setTimeout(resolve, RETRY_DELAY_MS * attempt)
|
|
2053
|
+
);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
1380
2057
|
/**
|
|
1381
2058
|
* True when `smartWallet` is the backend-managed pool wallet Zyfai assigned
|
|
1382
2059
|
* to this EOA — the "new wallet" kind that is predeployed on the chains it
|
|
@@ -1680,14 +2357,12 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1680
2357
|
);
|
|
1681
2358
|
const txHash = await depositCallback(smartWallet, validChainId, amount);
|
|
1682
2359
|
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
);
|
|
1690
|
-
}
|
|
2360
|
+
await this.logDepositWithRetry(
|
|
2361
|
+
validChainId,
|
|
2362
|
+
txHash,
|
|
2363
|
+
amount,
|
|
2364
|
+
tokenAddress
|
|
2365
|
+
);
|
|
1691
2366
|
return { txHash, smartWallet, amount };
|
|
1692
2367
|
}
|
|
1693
2368
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
@@ -1868,7 +2543,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1868
2543
|
}
|
|
1869
2544
|
};
|
|
1870
2545
|
|
|
2546
|
+
// src/client.ts
|
|
2547
|
+
init_errors();
|
|
2548
|
+
|
|
1871
2549
|
// src/lib/routing-api.ts
|
|
2550
|
+
init_errors();
|
|
1872
2551
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1873
2552
|
async function fetchOrgAgentConfig(apiKey) {
|
|
1874
2553
|
const url = `${ROUTING_API_BASE_URL}/api/v1/agent/org-config`;
|
|
@@ -1933,6 +2612,7 @@ async function fetchAgentKeys(apiKey) {
|
|
|
1933
2612
|
}
|
|
1934
2613
|
|
|
1935
2614
|
// src/lib/health-report.ts
|
|
2615
|
+
init_errors();
|
|
1936
2616
|
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1937
2617
|
async function reportAgentFailure(apiKey, agentType, errorCode) {
|
|
1938
2618
|
try {
|
|
@@ -1968,11 +2648,10 @@ async function withFailureReporting(apiKey, agentType, fn) {
|
|
|
1968
2648
|
// src/lib/helpers/withdraw-helper.ts
|
|
1969
2649
|
var import_viem2 = require("viem");
|
|
1970
2650
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
1971
|
-
const target = asset.toUpperCase();
|
|
1972
2651
|
return agents.map((agent) => {
|
|
1973
2652
|
const agentBalance = aggregated[agent.id];
|
|
1974
2653
|
const tokenBalance = agentBalance?.tokens.find(
|
|
1975
|
-
(t) => t.chainId === chainId && t.asset
|
|
2654
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
1976
2655
|
);
|
|
1977
2656
|
if (!tokenBalance) return { agent, balance: 0n };
|
|
1978
2657
|
return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
|
|
@@ -2082,8 +2761,11 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
2082
2761
|
}
|
|
2083
2762
|
|
|
2084
2763
|
// src/client.ts
|
|
2085
|
-
var
|
|
2086
|
-
var
|
|
2764
|
+
var import_viem7 = require("viem");
|
|
2765
|
+
var import_chains3 = require("viem/chains");
|
|
2766
|
+
|
|
2767
|
+
// src/lib/sponsored-deposit.ts
|
|
2768
|
+
init_errors();
|
|
2087
2769
|
|
|
2088
2770
|
// src/lib/transfer-auth.ts
|
|
2089
2771
|
var import_viem3 = require("viem");
|
|
@@ -2122,12 +2804,13 @@ function randomAuthNonce() {
|
|
|
2122
2804
|
}
|
|
2123
2805
|
|
|
2124
2806
|
// src/lib/sponsor-client.ts
|
|
2807
|
+
init_errors();
|
|
2125
2808
|
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2126
2809
|
async function postSponsorTransferAuth(input) {
|
|
2127
|
-
const
|
|
2810
|
+
const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2128
2811
|
let res;
|
|
2129
2812
|
try {
|
|
2130
|
-
res = await fetch(`${
|
|
2813
|
+
res = await fetch(`${base4}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2131
2814
|
method: "POST",
|
|
2132
2815
|
headers: {
|
|
2133
2816
|
"content-type": "application/json",
|
|
@@ -2164,10 +2847,10 @@ async function postSponsorTransferAuth(input) {
|
|
|
2164
2847
|
return parsed.data;
|
|
2165
2848
|
}
|
|
2166
2849
|
async function postSponsorPermit2Transfer(input) {
|
|
2167
|
-
const
|
|
2850
|
+
const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2168
2851
|
let res;
|
|
2169
2852
|
try {
|
|
2170
|
-
res = await fetch(`${
|
|
2853
|
+
res = await fetch(`${base4}/api/v1/sponsor/permit2-transfer`, {
|
|
2171
2854
|
method: "POST",
|
|
2172
2855
|
headers: {
|
|
2173
2856
|
"content-type": "application/json",
|
|
@@ -2202,11 +2885,11 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2202
2885
|
return parsed.data;
|
|
2203
2886
|
}
|
|
2204
2887
|
async function getSponsorRelayerAddress(input) {
|
|
2205
|
-
const
|
|
2888
|
+
const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2206
2889
|
let res;
|
|
2207
2890
|
try {
|
|
2208
2891
|
res = await fetch(
|
|
2209
|
-
`${
|
|
2892
|
+
`${base4}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2210
2893
|
{
|
|
2211
2894
|
headers: { "x-owney-api-key": input.apiKey }
|
|
2212
2895
|
}
|
|
@@ -2238,85 +2921,11 @@ async function getSponsorRelayerAddress(input) {
|
|
|
2238
2921
|
return parsed.data.relayer;
|
|
2239
2922
|
}
|
|
2240
2923
|
|
|
2241
|
-
// src/lib/
|
|
2242
|
-
|
|
2243
|
-
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2244
|
-
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2245
|
-
var ERC20_ALLOWANCE_ABI = [
|
|
2246
|
-
{
|
|
2247
|
-
type: "function",
|
|
2248
|
-
name: "allowance",
|
|
2249
|
-
stateMutability: "view",
|
|
2250
|
-
inputs: [
|
|
2251
|
-
{ name: "owner", type: "address" },
|
|
2252
|
-
{ name: "spender", type: "address" }
|
|
2253
|
-
],
|
|
2254
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2255
|
-
},
|
|
2256
|
-
{
|
|
2257
|
-
type: "function",
|
|
2258
|
-
name: "approve",
|
|
2259
|
-
stateMutability: "nonpayable",
|
|
2260
|
-
inputs: [
|
|
2261
|
-
{ name: "spender", type: "address" },
|
|
2262
|
-
{ name: "amount", type: "uint256" }
|
|
2263
|
-
],
|
|
2264
|
-
outputs: [{ name: "", type: "bool" }]
|
|
2265
|
-
},
|
|
2266
|
-
{
|
|
2267
|
-
type: "function",
|
|
2268
|
-
name: "balanceOf",
|
|
2269
|
-
stateMutability: "view",
|
|
2270
|
-
inputs: [{ name: "account", type: "address" }],
|
|
2271
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2272
|
-
}
|
|
2273
|
-
];
|
|
2274
|
-
function buildPermitTransferFromTypedData(input) {
|
|
2275
|
-
return {
|
|
2276
|
-
domain: {
|
|
2277
|
-
name: "Permit2",
|
|
2278
|
-
chainId: input.chainId,
|
|
2279
|
-
verifyingContract: PERMIT2_ADDRESS
|
|
2280
|
-
},
|
|
2281
|
-
types: {
|
|
2282
|
-
PermitTransferFrom: [
|
|
2283
|
-
{ name: "permitted", type: "TokenPermissions" },
|
|
2284
|
-
{ name: "spender", type: "address" },
|
|
2285
|
-
{ name: "nonce", type: "uint256" },
|
|
2286
|
-
{ name: "deadline", type: "uint256" }
|
|
2287
|
-
],
|
|
2288
|
-
TokenPermissions: [
|
|
2289
|
-
{ name: "token", type: "address" },
|
|
2290
|
-
{ name: "amount", type: "uint256" }
|
|
2291
|
-
]
|
|
2292
|
-
},
|
|
2293
|
-
primaryType: "PermitTransferFrom",
|
|
2294
|
-
message: input.message
|
|
2295
|
-
};
|
|
2296
|
-
}
|
|
2297
|
-
function randomPermit2Nonce() {
|
|
2298
|
-
const bytes = new Uint8Array(32);
|
|
2299
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2300
|
-
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
2301
|
-
}
|
|
2302
|
-
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2303
|
-
return publicClient.readContract({
|
|
2304
|
-
address: token,
|
|
2305
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2306
|
-
functionName: "allowance",
|
|
2307
|
-
args: [owner, PERMIT2_ADDRESS]
|
|
2308
|
-
});
|
|
2309
|
-
}
|
|
2310
|
-
async function readErc20Balance(publicClient, token, owner) {
|
|
2311
|
-
return publicClient.readContract({
|
|
2312
|
-
address: token,
|
|
2313
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2314
|
-
functionName: "balanceOf",
|
|
2315
|
-
args: [owner]
|
|
2316
|
-
});
|
|
2317
|
-
}
|
|
2924
|
+
// src/lib/sponsored-deposit.ts
|
|
2925
|
+
init_permit2();
|
|
2318
2926
|
|
|
2319
2927
|
// src/lib/chain-guard.ts
|
|
2928
|
+
init_errors();
|
|
2320
2929
|
var CHAIN_NAMES = {
|
|
2321
2930
|
1: "Ethereum",
|
|
2322
2931
|
8453: "Base",
|
|
@@ -2430,6 +3039,8 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2430
3039
|
}
|
|
2431
3040
|
|
|
2432
3041
|
// src/lib/sponsored-weth-deposit.ts
|
|
3042
|
+
init_errors();
|
|
3043
|
+
init_permit2();
|
|
2433
3044
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
2434
3045
|
function makeSponsoredWethCallback(deps) {
|
|
2435
3046
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
@@ -2514,6 +3125,7 @@ function makeSponsoredWethCallback(deps) {
|
|
|
2514
3125
|
|
|
2515
3126
|
// src/lib/sponsored-calls-deposit.ts
|
|
2516
3127
|
var import_viem5 = require("viem");
|
|
3128
|
+
init_errors();
|
|
2517
3129
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
2518
3130
|
var DEFAULT_MAX_POLLS = 30;
|
|
2519
3131
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -2604,6 +3216,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2604
3216
|
}
|
|
2605
3217
|
|
|
2606
3218
|
// src/client.ts
|
|
3219
|
+
init_permit2();
|
|
2607
3220
|
function encodeMultiAgentCursor(map) {
|
|
2608
3221
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
2609
3222
|
}
|
|
@@ -2630,10 +3243,11 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
2630
3243
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
2631
3244
|
};
|
|
2632
3245
|
var VIEM_CHAIN2 = {
|
|
2633
|
-
8453:
|
|
2634
|
-
42161:
|
|
2635
|
-
1:
|
|
3246
|
+
8453: import_chains3.base,
|
|
3247
|
+
42161: import_chains3.arbitrum,
|
|
3248
|
+
1: import_chains3.mainnet
|
|
2636
3249
|
};
|
|
3250
|
+
var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
|
|
2637
3251
|
var SPONSORED_WETH_BY_CHAIN = {
|
|
2638
3252
|
8453: "0x4200000000000000000000000000000000000006",
|
|
2639
3253
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -2662,6 +3276,7 @@ var OwneySDK = class {
|
|
|
2662
3276
|
zyfaiRpcUrls;
|
|
2663
3277
|
routingApiBaseUrl;
|
|
2664
3278
|
referralSource;
|
|
3279
|
+
surfliquidApiBaseUrl;
|
|
2665
3280
|
cachedSponsoredCallback = null;
|
|
2666
3281
|
cachedWethSponsoredCallback = null;
|
|
2667
3282
|
paymasterServiceUrl;
|
|
@@ -2674,6 +3289,7 @@ var OwneySDK = class {
|
|
|
2674
3289
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2675
3290
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
2676
3291
|
this.referralSource = config.referralSource;
|
|
3292
|
+
this.surfliquidApiBaseUrl = config.surfliquidApiBaseUrl;
|
|
2677
3293
|
}
|
|
2678
3294
|
/**
|
|
2679
3295
|
* Establish connection state for all subsequent SDK calls.
|
|
@@ -2773,14 +3389,14 @@ var OwneySDK = class {
|
|
|
2773
3389
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2774
3390
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2775
3391
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2776
|
-
getPublicClient: (cid) => (0,
|
|
3392
|
+
getPublicClient: (cid) => (0, import_viem7.createPublicClient)({
|
|
2777
3393
|
chain: VIEM_CHAIN2[cid],
|
|
2778
|
-
transport: (0,
|
|
3394
|
+
transport: (0, import_viem7.custom)(provider)
|
|
2779
3395
|
}),
|
|
2780
|
-
getWalletClient: (cid) => (0,
|
|
3396
|
+
getWalletClient: (cid) => (0, import_viem7.createWalletClient)({
|
|
2781
3397
|
account: owner,
|
|
2782
3398
|
chain: VIEM_CHAIN2[cid],
|
|
2783
|
-
transport: (0,
|
|
3399
|
+
transport: (0, import_viem7.custom)(provider)
|
|
2784
3400
|
})
|
|
2785
3401
|
});
|
|
2786
3402
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -2826,14 +3442,14 @@ var OwneySDK = class {
|
|
|
2826
3442
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2827
3443
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2828
3444
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2829
|
-
getPublicClient: (cid) => (0,
|
|
3445
|
+
getPublicClient: (cid) => (0, import_viem7.createPublicClient)({
|
|
2830
3446
|
chain: VIEM_CHAIN2[cid],
|
|
2831
|
-
transport: (0,
|
|
3447
|
+
transport: (0, import_viem7.custom)(provider)
|
|
2832
3448
|
}),
|
|
2833
|
-
getWalletClient: (cid) => (0,
|
|
3449
|
+
getWalletClient: (cid) => (0, import_viem7.createWalletClient)({
|
|
2834
3450
|
account: owner,
|
|
2835
3451
|
chain: VIEM_CHAIN2[cid],
|
|
2836
|
-
transport: (0,
|
|
3452
|
+
transport: (0, import_viem7.custom)(provider)
|
|
2837
3453
|
})
|
|
2838
3454
|
});
|
|
2839
3455
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -2903,8 +3519,8 @@ var OwneySDK = class {
|
|
|
2903
3519
|
this.initializingAgentsPromise = (async () => {
|
|
2904
3520
|
const agentKeys = await fetchAgentKeys(this.apiKey);
|
|
2905
3521
|
this.disabledAgents.clear();
|
|
2906
|
-
for (const { key: key2, agent_type, is_enabled } of agentKeys) {
|
|
2907
|
-
const agent = this.createAgent(agent_type, key2);
|
|
3522
|
+
for (const { key: key2, agent_type, project_name, is_enabled } of agentKeys) {
|
|
3523
|
+
const agent = await this.createAgent(agent_type, key2, project_name);
|
|
2908
3524
|
if (!agent) continue;
|
|
2909
3525
|
this.agents.set(agent_type, agent);
|
|
2910
3526
|
if (is_enabled === false) {
|
|
@@ -2925,10 +3541,22 @@ var OwneySDK = class {
|
|
|
2925
3541
|
this.initializingAgentsPromise = null;
|
|
2926
3542
|
}
|
|
2927
3543
|
}
|
|
2928
|
-
createAgent(agentId, key2) {
|
|
3544
|
+
async createAgent(agentId, key2, projectName) {
|
|
2929
3545
|
if (agentId === "zyfai") {
|
|
2930
3546
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
2931
3547
|
}
|
|
3548
|
+
if (agentId === "surfliquid") {
|
|
3549
|
+
if (!projectName) return null;
|
|
3550
|
+
const { SurfLiquidAgent: SurfLiquidAgent2 } = await Promise.resolve().then(() => (init_surfliquid_agent(), surfliquid_agent_exports));
|
|
3551
|
+
return new SurfLiquidAgent2({
|
|
3552
|
+
projectName,
|
|
3553
|
+
appId: key2,
|
|
3554
|
+
// Same-origin proxy override — SurfLiquid auth is an httpOnly cookie,
|
|
3555
|
+
// which WebKit (iOS) discards when it is third-party. See
|
|
3556
|
+
// OwneySDKConfig.surfliquidApiBaseUrl.
|
|
3557
|
+
apiBaseUrl: this.surfliquidApiBaseUrl
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
2932
3560
|
return null;
|
|
2933
3561
|
}
|
|
2934
3562
|
// --- Account lifecycle ---
|
|
@@ -2978,12 +3606,7 @@ var OwneySDK = class {
|
|
|
2978
3606
|
this.activeAgents.add(id);
|
|
2979
3607
|
}
|
|
2980
3608
|
state.chainId = chainId;
|
|
2981
|
-
|
|
2982
|
-
agents.map(async (agent) => {
|
|
2983
|
-
await agent.activateAgent(state, chainId);
|
|
2984
|
-
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
2985
|
-
})
|
|
2986
|
-
).catch((error) => {
|
|
3609
|
+
this.activateAgentsInTurn(agents, state, chainId).catch((error) => {
|
|
2987
3610
|
console.error("activateAgent background init failed:", error);
|
|
2988
3611
|
});
|
|
2989
3612
|
return;
|
|
@@ -3006,14 +3629,48 @@ var OwneySDK = class {
|
|
|
3006
3629
|
const enabledCompatible = compatible.filter(
|
|
3007
3630
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
3008
3631
|
);
|
|
3009
|
-
|
|
3010
|
-
|
|
3632
|
+
this.activateAgentsInTurn(enabledCompatible, state, chainId).catch(
|
|
3633
|
+
(error) => {
|
|
3634
|
+
console.error("activateAgent background init failed:", error);
|
|
3635
|
+
}
|
|
3636
|
+
);
|
|
3637
|
+
}
|
|
3638
|
+
/**
|
|
3639
|
+
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
3640
|
+
*
|
|
3641
|
+
* Every agent opens its activation with its own wallet signature — Zyfai's
|
|
3642
|
+
* SIWX (exchanged for a JWT) and SurfLiquid's SIWE (exchanged for an httpOnly
|
|
3643
|
+
* cookie). Run in parallel, that puts two `personal_sign` requests on one
|
|
3644
|
+
* wallet connection in the same instant. Over WalletConnect both are
|
|
3645
|
+
* delivered to the wallet, which renders one signing request at a time and
|
|
3646
|
+
* queues or silently drops the other: neither promise settles, so activation
|
|
3647
|
+
* hangs until the caller's timeout with no prompt the user can act on. The
|
|
3648
|
+
* single-agent era never hit this — there was no second prompt to collide
|
|
3649
|
+
* with — so it appeared the moment a second agent became eligible.
|
|
3650
|
+
*
|
|
3651
|
+
* Serializing costs no real wall-clock: the user can only approve one prompt
|
|
3652
|
+
* at a time anyway.
|
|
3653
|
+
*
|
|
3654
|
+
* Every agent is attempted even if an earlier one fails, so one declined
|
|
3655
|
+
* signature can't deny the remaining agents their turn. The first failure is
|
|
3656
|
+
* rethrown (matching the previous `Promise.all` rejection) once all agents
|
|
3657
|
+
* have had a chance to activate.
|
|
3658
|
+
*/
|
|
3659
|
+
async activateAgentsInTurn(agents, state, chainId) {
|
|
3660
|
+
let firstError = null;
|
|
3661
|
+
for (const agent of agents) {
|
|
3662
|
+
try {
|
|
3011
3663
|
await agent.activateAgent(state, chainId);
|
|
3012
3664
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
3013
|
-
})
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3665
|
+
} catch (error) {
|
|
3666
|
+
if (firstError === null) {
|
|
3667
|
+
firstError = error;
|
|
3668
|
+
} else {
|
|
3669
|
+
console.error(`activateAgent(${agent.id}) failed:`, error);
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
if (firstError !== null) throw firstError;
|
|
3017
3674
|
}
|
|
3018
3675
|
/**
|
|
3019
3676
|
* Deposit funds into a specific agent, or split equally across all agents if agentId is omitted.
|
|
@@ -3083,18 +3740,30 @@ var OwneySDK = class {
|
|
|
3083
3740
|
);
|
|
3084
3741
|
}
|
|
3085
3742
|
const agentResults = {};
|
|
3743
|
+
const agentErrors = {};
|
|
3086
3744
|
for (const { agent, amount: agentAmount } of agentAmounts) {
|
|
3087
|
-
|
|
3088
|
-
agent
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3745
|
+
try {
|
|
3746
|
+
agentResults[agent.id] = await this.depositWithFallback(
|
|
3747
|
+
agent,
|
|
3748
|
+
state,
|
|
3749
|
+
chainId,
|
|
3750
|
+
agentAmount,
|
|
3751
|
+
asset,
|
|
3752
|
+
effectiveCallback,
|
|
3753
|
+
depositCallback
|
|
3754
|
+
);
|
|
3755
|
+
} catch (error) {
|
|
3756
|
+
agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
if (Object.keys(agentResults).length === 0) {
|
|
3760
|
+
throw new OwneyError(
|
|
3761
|
+
"DEPOSIT_ALL_FAILED",
|
|
3762
|
+
`All ${agentAmounts.length} agent deposit(s) failed.`,
|
|
3763
|
+
{ agentErrors }
|
|
3095
3764
|
);
|
|
3096
3765
|
}
|
|
3097
|
-
return { agentResults };
|
|
3766
|
+
return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
|
|
3098
3767
|
}
|
|
3099
3768
|
/**
|
|
3100
3769
|
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
@@ -3210,9 +3879,8 @@ var OwneySDK = class {
|
|
|
3210
3879
|
async hasExistingBalance(agent, state, chainId, asset) {
|
|
3211
3880
|
try {
|
|
3212
3881
|
const balance = await agent.getBalances(state, chainId);
|
|
3213
|
-
const target = asset.toLowerCase();
|
|
3214
3882
|
const token = balance.tokens.find(
|
|
3215
|
-
(t) => t.chainId === chainId && t.asset
|
|
3883
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
3216
3884
|
);
|
|
3217
3885
|
return !!token && Number(token.amount) > 0;
|
|
3218
3886
|
} catch {
|
|
@@ -3251,6 +3919,34 @@ var OwneySDK = class {
|
|
|
3251
3919
|
}
|
|
3252
3920
|
return eligible;
|
|
3253
3921
|
}
|
|
3922
|
+
/**
|
|
3923
|
+
* Agent ids the routing API provisioned for this org that support the given
|
|
3924
|
+
* chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
|
|
3925
|
+
* surfliquid first). Returns `[]` when the org has no compatible agent — never
|
|
3926
|
+
* throws on an empty org. Loads agent keys on first call (apiKey only, no
|
|
3927
|
+
* wallet), so the UI can resolve which agent to use before the user connects.
|
|
3928
|
+
*
|
|
3929
|
+
* This is the source of truth for agent availability: an agent appears here
|
|
3930
|
+
* iff the routing API returned its key. No per-app feature flags.
|
|
3931
|
+
*/
|
|
3932
|
+
async getEligibleAgentIds(chainId, asset) {
|
|
3933
|
+
try {
|
|
3934
|
+
await this.ensureAgentsInitialized();
|
|
3935
|
+
} catch (error) {
|
|
3936
|
+
if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
|
|
3937
|
+
return [];
|
|
3938
|
+
}
|
|
3939
|
+
throw error;
|
|
3940
|
+
}
|
|
3941
|
+
return [...this.agents.values()].filter((agent) => {
|
|
3942
|
+
const chainAssets = agent.supportedAssets.find(
|
|
3943
|
+
(sa) => sa.chainId === chainId
|
|
3944
|
+
);
|
|
3945
|
+
return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
|
|
3946
|
+
}).map((agent) => agent.id).sort(
|
|
3947
|
+
(a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
|
|
3948
|
+
);
|
|
3949
|
+
}
|
|
3254
3950
|
// --- Fund operations ---
|
|
3255
3951
|
/**
|
|
3256
3952
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -3286,9 +3982,17 @@ var OwneySDK = class {
|
|
|
3286
3982
|
}
|
|
3287
3983
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
3288
3984
|
if (!amount) {
|
|
3985
|
+
const aggregated2 = await this.getBalances();
|
|
3986
|
+
const funded = projectAgentBalancesForAsset(
|
|
3987
|
+
eligibleAgents,
|
|
3988
|
+
aggregated2.agentBalances,
|
|
3989
|
+
chainId,
|
|
3990
|
+
asset,
|
|
3991
|
+
assetInfo.decimals
|
|
3992
|
+
).filter((b) => b.balance > 0n);
|
|
3289
3993
|
const results2 = {};
|
|
3290
3994
|
const agentErrors2 = {};
|
|
3291
|
-
for (const agent of
|
|
3995
|
+
for (const { agent } of funded) {
|
|
3292
3996
|
try {
|
|
3293
3997
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
3294
3998
|
} catch (err) {
|
|
@@ -3305,9 +4009,10 @@ var OwneySDK = class {
|
|
|
3305
4009
|
{ asset, agentErrors: agentErrors2 }
|
|
3306
4010
|
);
|
|
3307
4011
|
}
|
|
4012
|
+
const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
|
|
3308
4013
|
return {
|
|
3309
4014
|
agentResult: results2,
|
|
3310
|
-
totalWithdrawn:
|
|
4015
|
+
totalWithdrawn: totalWithdrawn.toString()
|
|
3311
4016
|
};
|
|
3312
4017
|
}
|
|
3313
4018
|
const requested = BigInt(amount);
|
|
@@ -3699,10 +4404,10 @@ var OwneySDK = class {
|
|
|
3699
4404
|
);
|
|
3700
4405
|
}
|
|
3701
4406
|
const provider = this.requireConnectedProvider();
|
|
3702
|
-
const wallet = (0,
|
|
4407
|
+
const wallet = (0, import_viem7.createWalletClient)({
|
|
3703
4408
|
account: state.walletAddress,
|
|
3704
4409
|
chain: VIEM_CHAIN2[chainId],
|
|
3705
|
-
transport: (0,
|
|
4410
|
+
transport: (0, import_viem7.custom)(provider)
|
|
3706
4411
|
});
|
|
3707
4412
|
const hash = await wallet.writeContract({
|
|
3708
4413
|
address: token,
|
|
@@ -3712,9 +4417,9 @@ var OwneySDK = class {
|
|
|
3712
4417
|
account: state.walletAddress,
|
|
3713
4418
|
chain: VIEM_CHAIN2[chainId]
|
|
3714
4419
|
});
|
|
3715
|
-
const publicClient = (0,
|
|
4420
|
+
const publicClient = (0, import_viem7.createPublicClient)({
|
|
3716
4421
|
chain: VIEM_CHAIN2[chainId],
|
|
3717
|
-
transport: (0,
|
|
4422
|
+
transport: (0, import_viem7.custom)(provider)
|
|
3718
4423
|
});
|
|
3719
4424
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3720
4425
|
hash,
|
|
@@ -3822,8 +4527,11 @@ var OwneySDK = class {
|
|
|
3822
4527
|
}
|
|
3823
4528
|
};
|
|
3824
4529
|
|
|
4530
|
+
// src/index.ts
|
|
4531
|
+
init_errors();
|
|
4532
|
+
|
|
3825
4533
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
3826
|
-
var
|
|
4534
|
+
var import_viem8 = require("viem");
|
|
3827
4535
|
var import_siwe = require("siwe");
|
|
3828
4536
|
var import_sdk2 = require("@zyfai/sdk");
|
|
3829
4537
|
|
|
@@ -3950,7 +4658,7 @@ function buildSIWXConfig(deps) {
|
|
|
3950
4658
|
issuedAt,
|
|
3951
4659
|
toString() {
|
|
3952
4660
|
return new import_siwe.SiweMessage({
|
|
3953
|
-
address: (0,
|
|
4661
|
+
address: (0, import_viem8.getAddress)(accountAddress),
|
|
3954
4662
|
chainId: numericChainId(chainId),
|
|
3955
4663
|
domain,
|
|
3956
4664
|
uri,
|