@owney/sdk 0.7.8 → 0.7.10-beta.0
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 +906 -214
- package/dist/index.d.cts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +225 -190
- 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,54 +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);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
870
|
+
const chainName2 = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
|
|
871
|
+
for (const [
|
|
872
|
+
symbol,
|
|
873
|
+
{ balance, balanceWithFee, decimals }
|
|
874
|
+
] of Object.entries(tokensBySymbol ?? {})) {
|
|
875
|
+
const amount = hexToDecimal(
|
|
876
|
+
balanceWithFee ?? balance ?? "0",
|
|
877
|
+
tokenDecimals(symbol, decimals)
|
|
878
|
+
);
|
|
257
879
|
totalBalance += amount;
|
|
258
880
|
tokens.push({
|
|
259
|
-
chain:
|
|
881
|
+
chain: chainName2,
|
|
260
882
|
chainId: positionChainId,
|
|
261
883
|
asset: symbol,
|
|
262
884
|
amount: String(amount)
|
|
263
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
|
+
});
|
|
264
904
|
}
|
|
265
905
|
}
|
|
266
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
297
941
|
const pendingAllocations = mapPendingAllocations(
|
|
298
942
|
portfolio.staleBalances
|
|
299
943
|
);
|
|
@@ -391,6 +1035,11 @@ function mapEntries(rawEntries, chainId) {
|
|
|
391
1035
|
"Top up wallet": "Top up",
|
|
392
1036
|
"Partial withdraw from protocol": "Withdraw",
|
|
393
1037
|
"Withdraw from protocol": "Withdraw",
|
|
1038
|
+
// The agent's own leg of a top up: the funds the user sent moving into the
|
|
1039
|
+
// pool that earns the APY. Zyfai emits it as a separate entry seconds after
|
|
1040
|
+
// "Top up wallet" and for slightly less (gas). `Deposit` already exists in
|
|
1041
|
+
// the public HistoryAction union, so no consumer type change is needed.
|
|
1042
|
+
"Deposit to protocol": "Deposit",
|
|
394
1043
|
Rebalance: "Rebalance"
|
|
395
1044
|
};
|
|
396
1045
|
void chainId;
|
|
@@ -431,12 +1080,19 @@ function mapEntries(rawEntries, chainId) {
|
|
|
431
1080
|
const mappedAction = (entry.action ? actionMap[entry.action] : void 0) ?? entry.action;
|
|
432
1081
|
const rawAmount = entry?.positions?.[0]?.amount;
|
|
433
1082
|
const hasAmount = mappedAction !== "Withdraw" && typeof rawAmount === "string" && rawAmount !== "" && rawAmount !== "0x" && rawAmount !== "0x0" && rawAmount !== "0";
|
|
1083
|
+
const mappedPositions = positions.filter((p) => p.protocol_name).map((p) => ({
|
|
1084
|
+
protocol: p.protocol_name,
|
|
1085
|
+
pool: p.pool ?? void 0,
|
|
1086
|
+
tokenSymbol: p.token_symbol ?? void 0,
|
|
1087
|
+
amount: mappedAction !== "Withdraw" && typeof p.amount === "string" && p.amount !== "" && p.amount !== "0x" ? String(hexToDecimal(p.amount, tokenDecimals(p.token_symbol))) : void 0
|
|
1088
|
+
}));
|
|
434
1089
|
return {
|
|
435
1090
|
agent: "zyfai",
|
|
436
1091
|
action: mappedAction,
|
|
437
1092
|
date: entry.date || "",
|
|
438
1093
|
newApy: newApy === null ? null : String(newApy),
|
|
439
1094
|
oldApy: oldApy === null ? null : String(oldApy),
|
|
1095
|
+
...mappedPositions.length ? { positions: mappedPositions } : {},
|
|
440
1096
|
transactions: [
|
|
441
1097
|
{
|
|
442
1098
|
txHashes: entry.transactionHash ? [entry.transactionHash] : [],
|
|
@@ -576,6 +1232,9 @@ function decodeHistoryCursor(token) {
|
|
|
576
1232
|
return parsed;
|
|
577
1233
|
}
|
|
578
1234
|
|
|
1235
|
+
// src/agents/zyfai/zyfai.agent.ts
|
|
1236
|
+
init_errors();
|
|
1237
|
+
|
|
579
1238
|
// src/agents/zyfai/zyfai.auth-cache.ts
|
|
580
1239
|
var KEY_PREFIX = "owney.zyfai.session";
|
|
581
1240
|
var storage = () => {
|
|
@@ -1364,6 +2023,37 @@ var ZyfaiAgent = class {
|
|
|
1364
2023
|
);
|
|
1365
2024
|
}
|
|
1366
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
|
+
}
|
|
1367
2057
|
/**
|
|
1368
2058
|
* True when `smartWallet` is the backend-managed pool wallet Zyfai assigned
|
|
1369
2059
|
* to this EOA — the "new wallet" kind that is predeployed on the chains it
|
|
@@ -1667,14 +2357,12 @@ var ZyfaiAgent = class {
|
|
|
1667
2357
|
);
|
|
1668
2358
|
const txHash = await depositCallback(smartWallet, validChainId, amount);
|
|
1669
2359
|
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
);
|
|
1677
|
-
}
|
|
2360
|
+
await this.logDepositWithRetry(
|
|
2361
|
+
validChainId,
|
|
2362
|
+
txHash,
|
|
2363
|
+
amount,
|
|
2364
|
+
tokenAddress
|
|
2365
|
+
);
|
|
1678
2366
|
return { txHash, smartWallet, amount };
|
|
1679
2367
|
}
|
|
1680
2368
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
@@ -1761,7 +2449,7 @@ var ZyfaiAgent = class {
|
|
|
1761
2449
|
toDate
|
|
1762
2450
|
});
|
|
1763
2451
|
raw.data.forEach((entry, idx) => {
|
|
1764
|
-
if (entry.chainId === validChainId
|
|
2452
|
+
if (entry.chainId === validChainId) {
|
|
1765
2453
|
matched.push({ entry, rawIdx: offset + idx });
|
|
1766
2454
|
}
|
|
1767
2455
|
});
|
|
@@ -1829,7 +2517,11 @@ var ZyfaiAgent = class {
|
|
|
1829
2517
|
}
|
|
1830
2518
|
};
|
|
1831
2519
|
|
|
2520
|
+
// src/client.ts
|
|
2521
|
+
init_errors();
|
|
2522
|
+
|
|
1832
2523
|
// src/lib/routing-api.ts
|
|
2524
|
+
init_errors();
|
|
1833
2525
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1834
2526
|
async function fetchOrgAgentConfig(apiKey) {
|
|
1835
2527
|
const url = `${ROUTING_API_BASE_URL}/api/v1/agent/org-config`;
|
|
@@ -1894,6 +2586,7 @@ async function fetchAgentKeys(apiKey) {
|
|
|
1894
2586
|
}
|
|
1895
2587
|
|
|
1896
2588
|
// src/lib/health-report.ts
|
|
2589
|
+
init_errors();
|
|
1897
2590
|
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1898
2591
|
async function reportAgentFailure(apiKey, agentType, errorCode) {
|
|
1899
2592
|
try {
|
|
@@ -1929,11 +2622,10 @@ async function withFailureReporting(apiKey, agentType, fn) {
|
|
|
1929
2622
|
// src/lib/helpers/withdraw-helper.ts
|
|
1930
2623
|
var import_viem2 = require("viem");
|
|
1931
2624
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
1932
|
-
const target = asset.toUpperCase();
|
|
1933
2625
|
return agents.map((agent) => {
|
|
1934
2626
|
const agentBalance = aggregated[agent.id];
|
|
1935
2627
|
const tokenBalance = agentBalance?.tokens.find(
|
|
1936
|
-
(t) => t.chainId === chainId && t.asset
|
|
2628
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
1937
2629
|
);
|
|
1938
2630
|
if (!tokenBalance) return { agent, balance: 0n };
|
|
1939
2631
|
return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
|
|
@@ -2043,8 +2735,11 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
2043
2735
|
}
|
|
2044
2736
|
|
|
2045
2737
|
// src/client.ts
|
|
2046
|
-
var
|
|
2047
|
-
var
|
|
2738
|
+
var import_viem7 = require("viem");
|
|
2739
|
+
var import_chains3 = require("viem/chains");
|
|
2740
|
+
|
|
2741
|
+
// src/lib/sponsored-deposit.ts
|
|
2742
|
+
init_errors();
|
|
2048
2743
|
|
|
2049
2744
|
// src/lib/transfer-auth.ts
|
|
2050
2745
|
var import_viem3 = require("viem");
|
|
@@ -2083,12 +2778,13 @@ function randomAuthNonce() {
|
|
|
2083
2778
|
}
|
|
2084
2779
|
|
|
2085
2780
|
// src/lib/sponsor-client.ts
|
|
2781
|
+
init_errors();
|
|
2086
2782
|
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2087
2783
|
async function postSponsorTransferAuth(input) {
|
|
2088
|
-
const
|
|
2784
|
+
const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2089
2785
|
let res;
|
|
2090
2786
|
try {
|
|
2091
|
-
res = await fetch(`${
|
|
2787
|
+
res = await fetch(`${base4}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2092
2788
|
method: "POST",
|
|
2093
2789
|
headers: {
|
|
2094
2790
|
"content-type": "application/json",
|
|
@@ -2125,10 +2821,10 @@ async function postSponsorTransferAuth(input) {
|
|
|
2125
2821
|
return parsed.data;
|
|
2126
2822
|
}
|
|
2127
2823
|
async function postSponsorPermit2Transfer(input) {
|
|
2128
|
-
const
|
|
2824
|
+
const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2129
2825
|
let res;
|
|
2130
2826
|
try {
|
|
2131
|
-
res = await fetch(`${
|
|
2827
|
+
res = await fetch(`${base4}/api/v1/sponsor/permit2-transfer`, {
|
|
2132
2828
|
method: "POST",
|
|
2133
2829
|
headers: {
|
|
2134
2830
|
"content-type": "application/json",
|
|
@@ -2163,11 +2859,11 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2163
2859
|
return parsed.data;
|
|
2164
2860
|
}
|
|
2165
2861
|
async function getSponsorRelayerAddress(input) {
|
|
2166
|
-
const
|
|
2862
|
+
const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2167
2863
|
let res;
|
|
2168
2864
|
try {
|
|
2169
2865
|
res = await fetch(
|
|
2170
|
-
`${
|
|
2866
|
+
`${base4}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2171
2867
|
{
|
|
2172
2868
|
headers: { "x-owney-api-key": input.apiKey }
|
|
2173
2869
|
}
|
|
@@ -2199,85 +2895,11 @@ async function getSponsorRelayerAddress(input) {
|
|
|
2199
2895
|
return parsed.data.relayer;
|
|
2200
2896
|
}
|
|
2201
2897
|
|
|
2202
|
-
// src/lib/
|
|
2203
|
-
|
|
2204
|
-
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2205
|
-
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2206
|
-
var ERC20_ALLOWANCE_ABI = [
|
|
2207
|
-
{
|
|
2208
|
-
type: "function",
|
|
2209
|
-
name: "allowance",
|
|
2210
|
-
stateMutability: "view",
|
|
2211
|
-
inputs: [
|
|
2212
|
-
{ name: "owner", type: "address" },
|
|
2213
|
-
{ name: "spender", type: "address" }
|
|
2214
|
-
],
|
|
2215
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2216
|
-
},
|
|
2217
|
-
{
|
|
2218
|
-
type: "function",
|
|
2219
|
-
name: "approve",
|
|
2220
|
-
stateMutability: "nonpayable",
|
|
2221
|
-
inputs: [
|
|
2222
|
-
{ name: "spender", type: "address" },
|
|
2223
|
-
{ name: "amount", type: "uint256" }
|
|
2224
|
-
],
|
|
2225
|
-
outputs: [{ name: "", type: "bool" }]
|
|
2226
|
-
},
|
|
2227
|
-
{
|
|
2228
|
-
type: "function",
|
|
2229
|
-
name: "balanceOf",
|
|
2230
|
-
stateMutability: "view",
|
|
2231
|
-
inputs: [{ name: "account", type: "address" }],
|
|
2232
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2233
|
-
}
|
|
2234
|
-
];
|
|
2235
|
-
function buildPermitTransferFromTypedData(input) {
|
|
2236
|
-
return {
|
|
2237
|
-
domain: {
|
|
2238
|
-
name: "Permit2",
|
|
2239
|
-
chainId: input.chainId,
|
|
2240
|
-
verifyingContract: PERMIT2_ADDRESS
|
|
2241
|
-
},
|
|
2242
|
-
types: {
|
|
2243
|
-
PermitTransferFrom: [
|
|
2244
|
-
{ name: "permitted", type: "TokenPermissions" },
|
|
2245
|
-
{ name: "spender", type: "address" },
|
|
2246
|
-
{ name: "nonce", type: "uint256" },
|
|
2247
|
-
{ name: "deadline", type: "uint256" }
|
|
2248
|
-
],
|
|
2249
|
-
TokenPermissions: [
|
|
2250
|
-
{ name: "token", type: "address" },
|
|
2251
|
-
{ name: "amount", type: "uint256" }
|
|
2252
|
-
]
|
|
2253
|
-
},
|
|
2254
|
-
primaryType: "PermitTransferFrom",
|
|
2255
|
-
message: input.message
|
|
2256
|
-
};
|
|
2257
|
-
}
|
|
2258
|
-
function randomPermit2Nonce() {
|
|
2259
|
-
const bytes = new Uint8Array(32);
|
|
2260
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2261
|
-
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
2262
|
-
}
|
|
2263
|
-
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2264
|
-
return publicClient.readContract({
|
|
2265
|
-
address: token,
|
|
2266
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2267
|
-
functionName: "allowance",
|
|
2268
|
-
args: [owner, PERMIT2_ADDRESS]
|
|
2269
|
-
});
|
|
2270
|
-
}
|
|
2271
|
-
async function readErc20Balance(publicClient, token, owner) {
|
|
2272
|
-
return publicClient.readContract({
|
|
2273
|
-
address: token,
|
|
2274
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2275
|
-
functionName: "balanceOf",
|
|
2276
|
-
args: [owner]
|
|
2277
|
-
});
|
|
2278
|
-
}
|
|
2898
|
+
// src/lib/sponsored-deposit.ts
|
|
2899
|
+
init_permit2();
|
|
2279
2900
|
|
|
2280
2901
|
// src/lib/chain-guard.ts
|
|
2902
|
+
init_errors();
|
|
2281
2903
|
var CHAIN_NAMES = {
|
|
2282
2904
|
1: "Ethereum",
|
|
2283
2905
|
8453: "Base",
|
|
@@ -2391,6 +3013,8 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2391
3013
|
}
|
|
2392
3014
|
|
|
2393
3015
|
// src/lib/sponsored-weth-deposit.ts
|
|
3016
|
+
init_errors();
|
|
3017
|
+
init_permit2();
|
|
2394
3018
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
2395
3019
|
function makeSponsoredWethCallback(deps) {
|
|
2396
3020
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
@@ -2475,6 +3099,7 @@ function makeSponsoredWethCallback(deps) {
|
|
|
2475
3099
|
|
|
2476
3100
|
// src/lib/sponsored-calls-deposit.ts
|
|
2477
3101
|
var import_viem5 = require("viem");
|
|
3102
|
+
init_errors();
|
|
2478
3103
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
2479
3104
|
var DEFAULT_MAX_POLLS = 30;
|
|
2480
3105
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -2565,6 +3190,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2565
3190
|
}
|
|
2566
3191
|
|
|
2567
3192
|
// src/client.ts
|
|
3193
|
+
init_permit2();
|
|
2568
3194
|
function encodeMultiAgentCursor(map) {
|
|
2569
3195
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
2570
3196
|
}
|
|
@@ -2591,10 +3217,11 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
2591
3217
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
2592
3218
|
};
|
|
2593
3219
|
var VIEM_CHAIN2 = {
|
|
2594
|
-
8453:
|
|
2595
|
-
42161:
|
|
2596
|
-
1:
|
|
3220
|
+
8453: import_chains3.base,
|
|
3221
|
+
42161: import_chains3.arbitrum,
|
|
3222
|
+
1: import_chains3.mainnet
|
|
2597
3223
|
};
|
|
3224
|
+
var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
|
|
2598
3225
|
var SPONSORED_WETH_BY_CHAIN = {
|
|
2599
3226
|
8453: "0x4200000000000000000000000000000000000006",
|
|
2600
3227
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -2623,6 +3250,7 @@ var OwneySDK = class {
|
|
|
2623
3250
|
zyfaiRpcUrls;
|
|
2624
3251
|
routingApiBaseUrl;
|
|
2625
3252
|
referralSource;
|
|
3253
|
+
surfliquidApiBaseUrl;
|
|
2626
3254
|
cachedSponsoredCallback = null;
|
|
2627
3255
|
cachedWethSponsoredCallback = null;
|
|
2628
3256
|
paymasterServiceUrl;
|
|
@@ -2635,6 +3263,7 @@ var OwneySDK = class {
|
|
|
2635
3263
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2636
3264
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
2637
3265
|
this.referralSource = config.referralSource;
|
|
3266
|
+
this.surfliquidApiBaseUrl = config.surfliquidApiBaseUrl;
|
|
2638
3267
|
}
|
|
2639
3268
|
/**
|
|
2640
3269
|
* Establish connection state for all subsequent SDK calls.
|
|
@@ -2734,14 +3363,14 @@ var OwneySDK = class {
|
|
|
2734
3363
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2735
3364
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2736
3365
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2737
|
-
getPublicClient: (cid) => (0,
|
|
3366
|
+
getPublicClient: (cid) => (0, import_viem7.createPublicClient)({
|
|
2738
3367
|
chain: VIEM_CHAIN2[cid],
|
|
2739
|
-
transport: (0,
|
|
3368
|
+
transport: (0, import_viem7.custom)(provider)
|
|
2740
3369
|
}),
|
|
2741
|
-
getWalletClient: (cid) => (0,
|
|
3370
|
+
getWalletClient: (cid) => (0, import_viem7.createWalletClient)({
|
|
2742
3371
|
account: owner,
|
|
2743
3372
|
chain: VIEM_CHAIN2[cid],
|
|
2744
|
-
transport: (0,
|
|
3373
|
+
transport: (0, import_viem7.custom)(provider)
|
|
2745
3374
|
})
|
|
2746
3375
|
});
|
|
2747
3376
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -2787,14 +3416,14 @@ var OwneySDK = class {
|
|
|
2787
3416
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2788
3417
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2789
3418
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2790
|
-
getPublicClient: (cid) => (0,
|
|
3419
|
+
getPublicClient: (cid) => (0, import_viem7.createPublicClient)({
|
|
2791
3420
|
chain: VIEM_CHAIN2[cid],
|
|
2792
|
-
transport: (0,
|
|
3421
|
+
transport: (0, import_viem7.custom)(provider)
|
|
2793
3422
|
}),
|
|
2794
|
-
getWalletClient: (cid) => (0,
|
|
3423
|
+
getWalletClient: (cid) => (0, import_viem7.createWalletClient)({
|
|
2795
3424
|
account: owner,
|
|
2796
3425
|
chain: VIEM_CHAIN2[cid],
|
|
2797
|
-
transport: (0,
|
|
3426
|
+
transport: (0, import_viem7.custom)(provider)
|
|
2798
3427
|
})
|
|
2799
3428
|
});
|
|
2800
3429
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -2864,8 +3493,8 @@ var OwneySDK = class {
|
|
|
2864
3493
|
this.initializingAgentsPromise = (async () => {
|
|
2865
3494
|
const agentKeys = await fetchAgentKeys(this.apiKey);
|
|
2866
3495
|
this.disabledAgents.clear();
|
|
2867
|
-
for (const { key: key2, agent_type, is_enabled } of agentKeys) {
|
|
2868
|
-
const agent = this.createAgent(agent_type, key2);
|
|
3496
|
+
for (const { key: key2, agent_type, project_name, is_enabled } of agentKeys) {
|
|
3497
|
+
const agent = await this.createAgent(agent_type, key2, project_name);
|
|
2869
3498
|
if (!agent) continue;
|
|
2870
3499
|
this.agents.set(agent_type, agent);
|
|
2871
3500
|
if (is_enabled === false) {
|
|
@@ -2886,10 +3515,22 @@ var OwneySDK = class {
|
|
|
2886
3515
|
this.initializingAgentsPromise = null;
|
|
2887
3516
|
}
|
|
2888
3517
|
}
|
|
2889
|
-
createAgent(agentId, key2) {
|
|
3518
|
+
async createAgent(agentId, key2, projectName) {
|
|
2890
3519
|
if (agentId === "zyfai") {
|
|
2891
3520
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
2892
3521
|
}
|
|
3522
|
+
if (agentId === "surfliquid") {
|
|
3523
|
+
if (!projectName) return null;
|
|
3524
|
+
const { SurfLiquidAgent: SurfLiquidAgent2 } = await Promise.resolve().then(() => (init_surfliquid_agent(), surfliquid_agent_exports));
|
|
3525
|
+
return new SurfLiquidAgent2({
|
|
3526
|
+
projectName,
|
|
3527
|
+
appId: key2,
|
|
3528
|
+
// Same-origin proxy override — SurfLiquid auth is an httpOnly cookie,
|
|
3529
|
+
// which WebKit (iOS) discards when it is third-party. See
|
|
3530
|
+
// OwneySDKConfig.surfliquidApiBaseUrl.
|
|
3531
|
+
apiBaseUrl: this.surfliquidApiBaseUrl
|
|
3532
|
+
});
|
|
3533
|
+
}
|
|
2893
3534
|
return null;
|
|
2894
3535
|
}
|
|
2895
3536
|
// --- Account lifecycle ---
|
|
@@ -3044,18 +3685,30 @@ var OwneySDK = class {
|
|
|
3044
3685
|
);
|
|
3045
3686
|
}
|
|
3046
3687
|
const agentResults = {};
|
|
3688
|
+
const agentErrors = {};
|
|
3047
3689
|
for (const { agent, amount: agentAmount } of agentAmounts) {
|
|
3048
|
-
|
|
3049
|
-
agent
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3690
|
+
try {
|
|
3691
|
+
agentResults[agent.id] = await this.depositWithFallback(
|
|
3692
|
+
agent,
|
|
3693
|
+
state,
|
|
3694
|
+
chainId,
|
|
3695
|
+
agentAmount,
|
|
3696
|
+
asset,
|
|
3697
|
+
effectiveCallback,
|
|
3698
|
+
depositCallback
|
|
3699
|
+
);
|
|
3700
|
+
} catch (error) {
|
|
3701
|
+
agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
if (Object.keys(agentResults).length === 0) {
|
|
3705
|
+
throw new OwneyError(
|
|
3706
|
+
"DEPOSIT_ALL_FAILED",
|
|
3707
|
+
`All ${agentAmounts.length} agent deposit(s) failed.`,
|
|
3708
|
+
{ agentErrors }
|
|
3056
3709
|
);
|
|
3057
3710
|
}
|
|
3058
|
-
return { agentResults };
|
|
3711
|
+
return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
|
|
3059
3712
|
}
|
|
3060
3713
|
/**
|
|
3061
3714
|
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
@@ -3171,9 +3824,8 @@ var OwneySDK = class {
|
|
|
3171
3824
|
async hasExistingBalance(agent, state, chainId, asset) {
|
|
3172
3825
|
try {
|
|
3173
3826
|
const balance = await agent.getBalances(state, chainId);
|
|
3174
|
-
const target = asset.toLowerCase();
|
|
3175
3827
|
const token = balance.tokens.find(
|
|
3176
|
-
(t) => t.chainId === chainId && t.asset
|
|
3828
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
3177
3829
|
);
|
|
3178
3830
|
return !!token && Number(token.amount) > 0;
|
|
3179
3831
|
} catch {
|
|
@@ -3212,6 +3864,34 @@ var OwneySDK = class {
|
|
|
3212
3864
|
}
|
|
3213
3865
|
return eligible;
|
|
3214
3866
|
}
|
|
3867
|
+
/**
|
|
3868
|
+
* Agent ids the routing API provisioned for this org that support the given
|
|
3869
|
+
* chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
|
|
3870
|
+
* surfliquid first). Returns `[]` when the org has no compatible agent — never
|
|
3871
|
+
* throws on an empty org. Loads agent keys on first call (apiKey only, no
|
|
3872
|
+
* wallet), so the UI can resolve which agent to use before the user connects.
|
|
3873
|
+
*
|
|
3874
|
+
* This is the source of truth for agent availability: an agent appears here
|
|
3875
|
+
* iff the routing API returned its key. No per-app feature flags.
|
|
3876
|
+
*/
|
|
3877
|
+
async getEligibleAgentIds(chainId, asset) {
|
|
3878
|
+
try {
|
|
3879
|
+
await this.ensureAgentsInitialized();
|
|
3880
|
+
} catch (error) {
|
|
3881
|
+
if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
|
|
3882
|
+
return [];
|
|
3883
|
+
}
|
|
3884
|
+
throw error;
|
|
3885
|
+
}
|
|
3886
|
+
return [...this.agents.values()].filter((agent) => {
|
|
3887
|
+
const chainAssets = agent.supportedAssets.find(
|
|
3888
|
+
(sa) => sa.chainId === chainId
|
|
3889
|
+
);
|
|
3890
|
+
return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
|
|
3891
|
+
}).map((agent) => agent.id).sort(
|
|
3892
|
+
(a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
|
|
3893
|
+
);
|
|
3894
|
+
}
|
|
3215
3895
|
// --- Fund operations ---
|
|
3216
3896
|
/**
|
|
3217
3897
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -3247,9 +3927,17 @@ var OwneySDK = class {
|
|
|
3247
3927
|
}
|
|
3248
3928
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
3249
3929
|
if (!amount) {
|
|
3930
|
+
const aggregated2 = await this.getBalances();
|
|
3931
|
+
const funded = projectAgentBalancesForAsset(
|
|
3932
|
+
eligibleAgents,
|
|
3933
|
+
aggregated2.agentBalances,
|
|
3934
|
+
chainId,
|
|
3935
|
+
asset,
|
|
3936
|
+
assetInfo.decimals
|
|
3937
|
+
).filter((b) => b.balance > 0n);
|
|
3250
3938
|
const results2 = {};
|
|
3251
3939
|
const agentErrors2 = {};
|
|
3252
|
-
for (const agent of
|
|
3940
|
+
for (const { agent } of funded) {
|
|
3253
3941
|
try {
|
|
3254
3942
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
3255
3943
|
} catch (err) {
|
|
@@ -3266,9 +3954,10 @@ var OwneySDK = class {
|
|
|
3266
3954
|
{ asset, agentErrors: agentErrors2 }
|
|
3267
3955
|
);
|
|
3268
3956
|
}
|
|
3957
|
+
const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
|
|
3269
3958
|
return {
|
|
3270
3959
|
agentResult: results2,
|
|
3271
|
-
totalWithdrawn:
|
|
3960
|
+
totalWithdrawn: totalWithdrawn.toString()
|
|
3272
3961
|
};
|
|
3273
3962
|
}
|
|
3274
3963
|
const requested = BigInt(amount);
|
|
@@ -3660,10 +4349,10 @@ var OwneySDK = class {
|
|
|
3660
4349
|
);
|
|
3661
4350
|
}
|
|
3662
4351
|
const provider = this.requireConnectedProvider();
|
|
3663
|
-
const wallet = (0,
|
|
4352
|
+
const wallet = (0, import_viem7.createWalletClient)({
|
|
3664
4353
|
account: state.walletAddress,
|
|
3665
4354
|
chain: VIEM_CHAIN2[chainId],
|
|
3666
|
-
transport: (0,
|
|
4355
|
+
transport: (0, import_viem7.custom)(provider)
|
|
3667
4356
|
});
|
|
3668
4357
|
const hash = await wallet.writeContract({
|
|
3669
4358
|
address: token,
|
|
@@ -3673,9 +4362,9 @@ var OwneySDK = class {
|
|
|
3673
4362
|
account: state.walletAddress,
|
|
3674
4363
|
chain: VIEM_CHAIN2[chainId]
|
|
3675
4364
|
});
|
|
3676
|
-
const publicClient = (0,
|
|
4365
|
+
const publicClient = (0, import_viem7.createPublicClient)({
|
|
3677
4366
|
chain: VIEM_CHAIN2[chainId],
|
|
3678
|
-
transport: (0,
|
|
4367
|
+
transport: (0, import_viem7.custom)(provider)
|
|
3679
4368
|
});
|
|
3680
4369
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3681
4370
|
hash,
|
|
@@ -3783,8 +4472,11 @@ var OwneySDK = class {
|
|
|
3783
4472
|
}
|
|
3784
4473
|
};
|
|
3785
4474
|
|
|
4475
|
+
// src/index.ts
|
|
4476
|
+
init_errors();
|
|
4477
|
+
|
|
3786
4478
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
3787
|
-
var
|
|
4479
|
+
var import_viem8 = require("viem");
|
|
3788
4480
|
var import_siwe = require("siwe");
|
|
3789
4481
|
var import_sdk2 = require("@zyfai/sdk");
|
|
3790
4482
|
|
|
@@ -3911,7 +4603,7 @@ function buildSIWXConfig(deps) {
|
|
|
3911
4603
|
issuedAt,
|
|
3912
4604
|
toString() {
|
|
3913
4605
|
return new import_siwe.SiweMessage({
|
|
3914
|
-
address: (0,
|
|
4606
|
+
address: (0, import_viem8.getAddress)(accountAddress),
|
|
3915
4607
|
chainId: numericChainId(chainId),
|
|
3916
4608
|
domain,
|
|
3917
4609
|
uri,
|