@owney/sdk 0.6.9-beta.6 → 0.6.9-beta.8
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 +238 -113
- package/dist/index.js +55 -93
- package/dist/{surfliquid.agent-OMOYXWLP.js → surfliquid.agent-VNGQUSXN.js} +100 -12
- package/package.json +1 -1
- package/dist/chunk-GMNQBWUB.js +0 -55
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var OwneyError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
details;
|
|
5
|
+
agentId;
|
|
6
|
+
constructor(code, message, details, agentId) {
|
|
7
|
+
const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
|
|
8
|
+
super(`${prefix} ${message}`);
|
|
9
|
+
this.name = "OwneyError";
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.details = details;
|
|
12
|
+
this.agentId = agentId;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var AgentNotFoundError = class extends OwneyError {
|
|
16
|
+
constructor(agentId, available) {
|
|
17
|
+
super(
|
|
18
|
+
"AGENT_NOT_FOUND",
|
|
19
|
+
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
20
|
+
{ agentId, available },
|
|
21
|
+
agentId
|
|
22
|
+
);
|
|
23
|
+
this.name = "AgentNotFoundError";
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var NotConnectedError = class extends OwneyError {
|
|
27
|
+
constructor() {
|
|
28
|
+
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
29
|
+
this.name = "NotConnectedError";
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var AgentChainIncompatibleError = class extends OwneyError {
|
|
33
|
+
incompatibleAgents;
|
|
34
|
+
connectedChainId;
|
|
35
|
+
constructor(incompatibleAgents, connectedChainId) {
|
|
36
|
+
const details = incompatibleAgents.map(
|
|
37
|
+
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
38
|
+
).join("; ");
|
|
39
|
+
super(
|
|
40
|
+
"AGENT_CHAIN_INCOMPATIBLE",
|
|
41
|
+
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
42
|
+
{ incompatibleAgents, connectedChainId }
|
|
43
|
+
);
|
|
44
|
+
this.name = "AgentChainIncompatibleError";
|
|
45
|
+
this.incompatibleAgents = incompatibleAgents;
|
|
46
|
+
this.connectedChainId = connectedChainId;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// src/lib/permit2.ts
|
|
51
|
+
import { bytesToHex } from "viem";
|
|
52
|
+
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
53
|
+
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
54
|
+
var ERC20_ALLOWANCE_ABI = [
|
|
55
|
+
{
|
|
56
|
+
type: "function",
|
|
57
|
+
name: "allowance",
|
|
58
|
+
stateMutability: "view",
|
|
59
|
+
inputs: [
|
|
60
|
+
{ name: "owner", type: "address" },
|
|
61
|
+
{ name: "spender", type: "address" }
|
|
62
|
+
],
|
|
63
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
type: "function",
|
|
67
|
+
name: "approve",
|
|
68
|
+
stateMutability: "nonpayable",
|
|
69
|
+
inputs: [
|
|
70
|
+
{ name: "spender", type: "address" },
|
|
71
|
+
{ name: "amount", type: "uint256" }
|
|
72
|
+
],
|
|
73
|
+
outputs: [{ name: "", type: "bool" }]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
type: "function",
|
|
77
|
+
name: "balanceOf",
|
|
78
|
+
stateMutability: "view",
|
|
79
|
+
inputs: [{ name: "account", type: "address" }],
|
|
80
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
81
|
+
}
|
|
82
|
+
];
|
|
83
|
+
function buildPermitTransferFromTypedData(input) {
|
|
84
|
+
return {
|
|
85
|
+
domain: {
|
|
86
|
+
name: "Permit2",
|
|
87
|
+
chainId: input.chainId,
|
|
88
|
+
verifyingContract: PERMIT2_ADDRESS
|
|
89
|
+
},
|
|
90
|
+
types: {
|
|
91
|
+
PermitTransferFrom: [
|
|
92
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
93
|
+
{ name: "spender", type: "address" },
|
|
94
|
+
{ name: "nonce", type: "uint256" },
|
|
95
|
+
{ name: "deadline", type: "uint256" }
|
|
96
|
+
],
|
|
97
|
+
TokenPermissions: [
|
|
98
|
+
{ name: "token", type: "address" },
|
|
99
|
+
{ name: "amount", type: "uint256" }
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
primaryType: "PermitTransferFrom",
|
|
103
|
+
message: input.message
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function randomPermit2Nonce() {
|
|
107
|
+
const bytes = new Uint8Array(32);
|
|
108
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
109
|
+
return BigInt(bytesToHex(bytes));
|
|
110
|
+
}
|
|
111
|
+
async function readPermit2Allowance(publicClient, token, owner) {
|
|
112
|
+
return publicClient.readContract({
|
|
113
|
+
address: token,
|
|
114
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
115
|
+
functionName: "allowance",
|
|
116
|
+
args: [owner, PERMIT2_ADDRESS]
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
120
|
+
return publicClient.readContract({
|
|
121
|
+
address: token,
|
|
122
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
123
|
+
functionName: "balanceOf",
|
|
124
|
+
args: [owner]
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export {
|
|
129
|
+
OwneyError,
|
|
130
|
+
AgentNotFoundError,
|
|
131
|
+
NotConnectedError,
|
|
132
|
+
AgentChainIncompatibleError,
|
|
133
|
+
PERMIT2_ADDRESS,
|
|
134
|
+
MAX_UINT256,
|
|
135
|
+
ERC20_ALLOWANCE_ABI,
|
|
136
|
+
buildPermitTransferFromTypedData,
|
|
137
|
+
randomPermit2Nonce,
|
|
138
|
+
readPermit2Allowance,
|
|
139
|
+
readErc20Balance
|
|
140
|
+
};
|
package/dist/index.cjs
CHANGED
|
@@ -80,8 +80,92 @@ var init_errors = __esm({
|
|
|
80
80
|
}
|
|
81
81
|
});
|
|
82
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
|
+
|
|
83
167
|
// src/agents/surfliquid/surfliquid.constants.ts
|
|
84
|
-
var SURFLIQUID_CHAIN_ID, SURFLIQUID_CHAIN_NAME, SURFLIQUID_USDC_ADDRESS, SURFLIQUID_USDC_DECIMALS, SURFLIQUID_MIN_DEPOSIT, SURFLIQUID_SUPPORTED_ASSETS, SURFLIQUID_ACTION_MAP;
|
|
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;
|
|
85
169
|
var init_surfliquid_constants = __esm({
|
|
86
170
|
"src/agents/surfliquid/surfliquid.constants.ts"() {
|
|
87
171
|
"use strict";
|
|
@@ -90,6 +174,7 @@ var init_surfliquid_constants = __esm({
|
|
|
90
174
|
SURFLIQUID_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
91
175
|
SURFLIQUID_USDC_DECIMALS = 6;
|
|
92
176
|
SURFLIQUID_MIN_DEPOSIT = "0";
|
|
177
|
+
SURFLIQUID_APPROVAL_TARGET = 100000000000n;
|
|
93
178
|
SURFLIQUID_SUPPORTED_ASSETS = [
|
|
94
179
|
{
|
|
95
180
|
chainId: SURFLIQUID_CHAIN_ID,
|
|
@@ -249,27 +334,20 @@ var init_surfliquid_wallet_adapter = __esm({
|
|
|
249
334
|
});
|
|
250
335
|
|
|
251
336
|
// src/agents/surfliquid/surfliquid.morpho.ts
|
|
252
|
-
async function
|
|
337
|
+
async function queryMorpho(query, vaultAddress, chainId) {
|
|
253
338
|
try {
|
|
254
339
|
const res = await fetch(MORPHO_API_URL, {
|
|
255
340
|
method: "POST",
|
|
256
341
|
headers: { "Content-Type": "application/json" },
|
|
257
342
|
body: JSON.stringify({
|
|
258
|
-
query
|
|
343
|
+
query,
|
|
259
344
|
variables: { address: vaultAddress, chainId }
|
|
260
345
|
})
|
|
261
346
|
});
|
|
262
347
|
if (!res.ok) return null;
|
|
263
348
|
const json = await res.json();
|
|
264
349
|
if (json.errors) return null;
|
|
265
|
-
|
|
266
|
-
const tvlUsd = vault?.state?.totalAssetsUsd;
|
|
267
|
-
const liquidityUsd = vault?.liquidity?.usd;
|
|
268
|
-
if (typeof tvlUsd !== "number" || typeof liquidityUsd !== "number") {
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
const name = typeof vault?.name === "string" ? vault.name : null;
|
|
272
|
-
return { name, tvlUsd, liquidityUsd };
|
|
350
|
+
return json.data ?? null;
|
|
273
351
|
} catch (error) {
|
|
274
352
|
console.warn("surfliquid: Morpho vault stats fetch failed", {
|
|
275
353
|
vaultAddress,
|
|
@@ -279,6 +357,29 @@ async function fetchMorphoVaultStats(vaultAddress, chainId) {
|
|
|
279
357
|
return null;
|
|
280
358
|
}
|
|
281
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
|
+
}
|
|
282
383
|
async function fetchMorphoStatsByVault(vaultAddresses, chainId) {
|
|
283
384
|
const unique = [...new Set(vaultAddresses.map((a) => a.toLowerCase()))];
|
|
284
385
|
const entries = await Promise.all(
|
|
@@ -292,7 +393,7 @@ async function fetchMorphoStatsByVault(vaultAddresses, chainId) {
|
|
|
292
393
|
}
|
|
293
394
|
return byVault;
|
|
294
395
|
}
|
|
295
|
-
var MORPHO_API_URL, VAULT_STATS_QUERY;
|
|
396
|
+
var MORPHO_API_URL, VAULT_STATS_QUERY, VAULT_V2_STATS_QUERY;
|
|
296
397
|
var init_surfliquid_morpho = __esm({
|
|
297
398
|
"src/agents/surfliquid/surfliquid.morpho.ts"() {
|
|
298
399
|
"use strict";
|
|
@@ -305,6 +406,15 @@ var init_surfliquid_morpho = __esm({
|
|
|
305
406
|
liquidity { usd }
|
|
306
407
|
}
|
|
307
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
|
+
}
|
|
308
418
|
`;
|
|
309
419
|
}
|
|
310
420
|
});
|
|
@@ -314,14 +424,16 @@ var surfliquid_agent_exports = {};
|
|
|
314
424
|
__export(surfliquid_agent_exports, {
|
|
315
425
|
SurfLiquidAgent: () => SurfLiquidAgent
|
|
316
426
|
});
|
|
317
|
-
var import_core_sdk2, import_viem5, APY_WINDOW_KEY2, SurfLiquidAgent;
|
|
427
|
+
var import_core_sdk2, import_viem5, import_chains2, APY_WINDOW_KEY2, SurfLiquidAgent;
|
|
318
428
|
var init_surfliquid_agent = __esm({
|
|
319
429
|
"src/agents/surfliquid/surfliquid.agent.ts"() {
|
|
320
430
|
"use strict";
|
|
321
431
|
import_core_sdk2 = require("@surf_liquid/core-sdk");
|
|
322
432
|
import_viem5 = require("viem");
|
|
433
|
+
import_chains2 = require("viem/chains");
|
|
323
434
|
init_errors();
|
|
324
435
|
init_surfliquid_constants();
|
|
436
|
+
init_permit2();
|
|
325
437
|
init_surfliquid_mapper();
|
|
326
438
|
init_surfliquid_wallet_adapter();
|
|
327
439
|
init_surfliquid_morpho();
|
|
@@ -397,6 +509,60 @@ var init_surfliquid_agent = __esm({
|
|
|
397
509
|
const { vaultAddress } = await surf.deployVault();
|
|
398
510
|
return vaultAddress;
|
|
399
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_viem5.custom)(state.provider);
|
|
527
|
+
const publicClient = (0, import_viem5.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_viem5.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
|
+
}
|
|
400
566
|
// --- IAgent: lifecycle ---
|
|
401
567
|
async disconnect() {
|
|
402
568
|
if (this.surf) {
|
|
@@ -415,6 +581,7 @@ var init_surfliquid_agent = __esm({
|
|
|
415
581
|
surf,
|
|
416
582
|
state.walletAddress
|
|
417
583
|
);
|
|
584
|
+
await this.ensureVaultAllowance(state, smartWallet, BigInt(amount));
|
|
418
585
|
const tx = await surf.deposit({
|
|
419
586
|
asset: SURFLIQUID_USDC_ADDRESS,
|
|
420
587
|
amount: human
|
|
@@ -581,6 +748,12 @@ var isValidChainId = (chainId) => {
|
|
|
581
748
|
}
|
|
582
749
|
return chainId;
|
|
583
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
|
+
}
|
|
584
757
|
function hexToDecimal(hex, decimals = 6) {
|
|
585
758
|
const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
|
|
586
759
|
const parsed = BigInt(normalized);
|
|
@@ -1789,6 +1962,37 @@ var ZyfaiAgent = class {
|
|
|
1789
1962
|
);
|
|
1790
1963
|
}
|
|
1791
1964
|
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Records the deposit with Zyfai, retrying transient failures.
|
|
1967
|
+
*
|
|
1968
|
+
* This runs AFTER the transfer has already landed on-chain, so it must never
|
|
1969
|
+
* fail the deposit — the funds moved. But it is also the ONLY source of the
|
|
1970
|
+
* "Top up wallet" entry the history is built from: Zyfai auto-deploys the
|
|
1971
|
+
* balance it detects either way, so when this call is lost the user's deposit
|
|
1972
|
+
* never appears in Activity (an earlier withdrawal stays the newest row) and
|
|
1973
|
+
* nothing ever backfills it. Retry, then log loudly enough to be recoverable.
|
|
1974
|
+
*/
|
|
1975
|
+
async logDepositWithRetry(chainId, txHash, amount, tokenAddress) {
|
|
1976
|
+
const ATTEMPTS = 3;
|
|
1977
|
+
const RETRY_DELAY_MS = 1e3;
|
|
1978
|
+
for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
|
|
1979
|
+
try {
|
|
1980
|
+
await (tokenAddress ? this.sdk.logDeposit(chainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(chainId, txHash, amount));
|
|
1981
|
+
return;
|
|
1982
|
+
} catch (logError) {
|
|
1983
|
+
if (attempt === ATTEMPTS) {
|
|
1984
|
+
console.error(
|
|
1985
|
+
"[owney-sdk] Deposit landed on-chain but logDeposit failed \u2014 it will be missing from Zyfai history:",
|
|
1986
|
+
{ txHash, chainId, amount, tokenAddress, error: logError }
|
|
1987
|
+
);
|
|
1988
|
+
return;
|
|
1989
|
+
}
|
|
1990
|
+
await new Promise(
|
|
1991
|
+
(resolve) => setTimeout(resolve, RETRY_DELAY_MS * attempt)
|
|
1992
|
+
);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1792
1996
|
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
1793
1997
|
try {
|
|
1794
1998
|
await this.connectFull(state, chainId);
|
|
@@ -1804,14 +2008,12 @@ var ZyfaiAgent = class {
|
|
|
1804
2008
|
amount
|
|
1805
2009
|
);
|
|
1806
2010
|
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
);
|
|
1814
|
-
}
|
|
2011
|
+
await this.logDepositWithRetry(
|
|
2012
|
+
validChainId,
|
|
2013
|
+
txHash,
|
|
2014
|
+
amount,
|
|
2015
|
+
tokenAddress
|
|
2016
|
+
);
|
|
1815
2017
|
return { txHash, smartWallet: wallet.address, amount };
|
|
1816
2018
|
}
|
|
1817
2019
|
const raw = await this.sdk.depositFunds(
|
|
@@ -2055,11 +2257,10 @@ async function withFailureReporting(apiKey, agentType, fn) {
|
|
|
2055
2257
|
// src/lib/helpers/withdraw-helper.ts
|
|
2056
2258
|
var import_viem2 = require("viem");
|
|
2057
2259
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
2058
|
-
const target = asset.toUpperCase();
|
|
2059
2260
|
return agents.map((agent) => {
|
|
2060
2261
|
const agentBalance = aggregated[agent.id];
|
|
2061
2262
|
const tokenBalance = agentBalance?.tokens.find(
|
|
2062
|
-
(t) => t.chainId === chainId && t.asset
|
|
2263
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
2063
2264
|
);
|
|
2064
2265
|
if (!tokenBalance) return { agent, balance: 0n };
|
|
2065
2266
|
return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
|
|
@@ -2170,7 +2371,7 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
2170
2371
|
|
|
2171
2372
|
// src/client.ts
|
|
2172
2373
|
var import_viem6 = require("viem");
|
|
2173
|
-
var
|
|
2374
|
+
var import_chains3 = require("viem/chains");
|
|
2174
2375
|
|
|
2175
2376
|
// src/lib/sponsored-deposit.ts
|
|
2176
2377
|
init_errors();
|
|
@@ -2215,10 +2416,10 @@ function randomAuthNonce() {
|
|
|
2215
2416
|
init_errors();
|
|
2216
2417
|
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2217
2418
|
async function postSponsorTransferAuth(input) {
|
|
2218
|
-
const
|
|
2419
|
+
const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2219
2420
|
let res;
|
|
2220
2421
|
try {
|
|
2221
|
-
res = await fetch(`${
|
|
2422
|
+
res = await fetch(`${base4}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2222
2423
|
method: "POST",
|
|
2223
2424
|
headers: {
|
|
2224
2425
|
"content-type": "application/json",
|
|
@@ -2255,10 +2456,10 @@ async function postSponsorTransferAuth(input) {
|
|
|
2255
2456
|
return parsed.data;
|
|
2256
2457
|
}
|
|
2257
2458
|
async function postSponsorPermit2Transfer(input) {
|
|
2258
|
-
const
|
|
2459
|
+
const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2259
2460
|
let res;
|
|
2260
2461
|
try {
|
|
2261
|
-
res = await fetch(`${
|
|
2462
|
+
res = await fetch(`${base4}/api/v1/sponsor/permit2-transfer`, {
|
|
2262
2463
|
method: "POST",
|
|
2263
2464
|
headers: {
|
|
2264
2465
|
"content-type": "application/json",
|
|
@@ -2293,11 +2494,11 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2293
2494
|
return parsed.data;
|
|
2294
2495
|
}
|
|
2295
2496
|
async function getSponsorRelayerAddress(input) {
|
|
2296
|
-
const
|
|
2497
|
+
const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2297
2498
|
let res;
|
|
2298
2499
|
try {
|
|
2299
2500
|
res = await fetch(
|
|
2300
|
-
`${
|
|
2501
|
+
`${base4}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2301
2502
|
{
|
|
2302
2503
|
headers: { "x-owney-api-key": input.apiKey }
|
|
2303
2504
|
}
|
|
@@ -2329,85 +2530,8 @@ async function getSponsorRelayerAddress(input) {
|
|
|
2329
2530
|
return parsed.data.relayer;
|
|
2330
2531
|
}
|
|
2331
2532
|
|
|
2332
|
-
// src/lib/permit2.ts
|
|
2333
|
-
var import_viem4 = require("viem");
|
|
2334
|
-
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2335
|
-
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2336
|
-
var ERC20_ALLOWANCE_ABI = [
|
|
2337
|
-
{
|
|
2338
|
-
type: "function",
|
|
2339
|
-
name: "allowance",
|
|
2340
|
-
stateMutability: "view",
|
|
2341
|
-
inputs: [
|
|
2342
|
-
{ name: "owner", type: "address" },
|
|
2343
|
-
{ name: "spender", type: "address" }
|
|
2344
|
-
],
|
|
2345
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2346
|
-
},
|
|
2347
|
-
{
|
|
2348
|
-
type: "function",
|
|
2349
|
-
name: "approve",
|
|
2350
|
-
stateMutability: "nonpayable",
|
|
2351
|
-
inputs: [
|
|
2352
|
-
{ name: "spender", type: "address" },
|
|
2353
|
-
{ name: "amount", type: "uint256" }
|
|
2354
|
-
],
|
|
2355
|
-
outputs: [{ name: "", type: "bool" }]
|
|
2356
|
-
},
|
|
2357
|
-
{
|
|
2358
|
-
type: "function",
|
|
2359
|
-
name: "balanceOf",
|
|
2360
|
-
stateMutability: "view",
|
|
2361
|
-
inputs: [{ name: "account", type: "address" }],
|
|
2362
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2363
|
-
}
|
|
2364
|
-
];
|
|
2365
|
-
function buildPermitTransferFromTypedData(input) {
|
|
2366
|
-
return {
|
|
2367
|
-
domain: {
|
|
2368
|
-
name: "Permit2",
|
|
2369
|
-
chainId: input.chainId,
|
|
2370
|
-
verifyingContract: PERMIT2_ADDRESS
|
|
2371
|
-
},
|
|
2372
|
-
types: {
|
|
2373
|
-
PermitTransferFrom: [
|
|
2374
|
-
{ name: "permitted", type: "TokenPermissions" },
|
|
2375
|
-
{ name: "spender", type: "address" },
|
|
2376
|
-
{ name: "nonce", type: "uint256" },
|
|
2377
|
-
{ name: "deadline", type: "uint256" }
|
|
2378
|
-
],
|
|
2379
|
-
TokenPermissions: [
|
|
2380
|
-
{ name: "token", type: "address" },
|
|
2381
|
-
{ name: "amount", type: "uint256" }
|
|
2382
|
-
]
|
|
2383
|
-
},
|
|
2384
|
-
primaryType: "PermitTransferFrom",
|
|
2385
|
-
message: input.message
|
|
2386
|
-
};
|
|
2387
|
-
}
|
|
2388
|
-
function randomPermit2Nonce() {
|
|
2389
|
-
const bytes = new Uint8Array(32);
|
|
2390
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2391
|
-
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
2392
|
-
}
|
|
2393
|
-
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2394
|
-
return publicClient.readContract({
|
|
2395
|
-
address: token,
|
|
2396
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2397
|
-
functionName: "allowance",
|
|
2398
|
-
args: [owner, PERMIT2_ADDRESS]
|
|
2399
|
-
});
|
|
2400
|
-
}
|
|
2401
|
-
async function readErc20Balance(publicClient, token, owner) {
|
|
2402
|
-
return publicClient.readContract({
|
|
2403
|
-
address: token,
|
|
2404
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2405
|
-
functionName: "balanceOf",
|
|
2406
|
-
args: [owner]
|
|
2407
|
-
});
|
|
2408
|
-
}
|
|
2409
|
-
|
|
2410
2533
|
// src/lib/sponsored-deposit.ts
|
|
2534
|
+
init_permit2();
|
|
2411
2535
|
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2412
2536
|
function makeSponsoredDepositCallback(deps) {
|
|
2413
2537
|
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
@@ -2482,6 +2606,7 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2482
2606
|
|
|
2483
2607
|
// src/lib/sponsored-weth-deposit.ts
|
|
2484
2608
|
init_errors();
|
|
2609
|
+
init_permit2();
|
|
2485
2610
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
2486
2611
|
function makeSponsoredWethCallback(deps) {
|
|
2487
2612
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
@@ -2560,6 +2685,7 @@ function makeSponsoredWethCallback(deps) {
|
|
|
2560
2685
|
}
|
|
2561
2686
|
|
|
2562
2687
|
// src/client.ts
|
|
2688
|
+
init_permit2();
|
|
2563
2689
|
function encodeMultiAgentCursor(map) {
|
|
2564
2690
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
2565
2691
|
}
|
|
@@ -2586,9 +2712,9 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
2586
2712
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
2587
2713
|
};
|
|
2588
2714
|
var VIEM_CHAIN2 = {
|
|
2589
|
-
8453:
|
|
2590
|
-
42161:
|
|
2591
|
-
1:
|
|
2715
|
+
8453: import_chains3.base,
|
|
2716
|
+
42161: import_chains3.arbitrum,
|
|
2717
|
+
1: import_chains3.mainnet
|
|
2592
2718
|
};
|
|
2593
2719
|
var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
|
|
2594
2720
|
var SPONSORED_WETH_BY_CHAIN = {
|
|
@@ -3168,9 +3294,8 @@ var OwneySDK = class {
|
|
|
3168
3294
|
async hasExistingBalance(agent, state, chainId, asset) {
|
|
3169
3295
|
try {
|
|
3170
3296
|
const balance = await agent.getBalances(state, chainId);
|
|
3171
|
-
const target = asset.toLowerCase();
|
|
3172
3297
|
const token = balance.tokens.find(
|
|
3173
|
-
(t) => t.chainId === chainId && t.asset
|
|
3298
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
3174
3299
|
);
|
|
3175
3300
|
return !!token && Number(token.amount) > 0;
|
|
3176
3301
|
} catch {
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AgentChainIncompatibleError,
|
|
3
3
|
AgentNotFoundError,
|
|
4
|
+
ERC20_ALLOWANCE_ABI,
|
|
5
|
+
MAX_UINT256,
|
|
4
6
|
NotConnectedError,
|
|
5
|
-
OwneyError
|
|
6
|
-
|
|
7
|
+
OwneyError,
|
|
8
|
+
PERMIT2_ADDRESS,
|
|
9
|
+
buildPermitTransferFromTypedData,
|
|
10
|
+
randomPermit2Nonce,
|
|
11
|
+
readErc20Balance,
|
|
12
|
+
readPermit2Allowance
|
|
13
|
+
} from "./chunk-PWIKIQOX.js";
|
|
7
14
|
|
|
8
15
|
// src/agents/zyfai/zyfai.agent.ts
|
|
9
16
|
import { ZyfaiSDK } from "@zyfai/sdk";
|
|
@@ -74,6 +81,12 @@ var isValidChainId = (chainId) => {
|
|
|
74
81
|
}
|
|
75
82
|
return chainId;
|
|
76
83
|
};
|
|
84
|
+
function isSameAsset(tokenSymbol, asset) {
|
|
85
|
+
const token = tokenSymbol.toLowerCase();
|
|
86
|
+
const target = asset.toLowerCase();
|
|
87
|
+
if (token === target) return true;
|
|
88
|
+
return target === "weth" && token === "eth" || target === "eth" && token === "weth";
|
|
89
|
+
}
|
|
77
90
|
function hexToDecimal(hex, decimals = 6) {
|
|
78
91
|
const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
|
|
79
92
|
const parsed = BigInt(normalized);
|
|
@@ -1279,6 +1292,37 @@ var ZyfaiAgent = class {
|
|
|
1279
1292
|
);
|
|
1280
1293
|
}
|
|
1281
1294
|
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Records the deposit with Zyfai, retrying transient failures.
|
|
1297
|
+
*
|
|
1298
|
+
* This runs AFTER the transfer has already landed on-chain, so it must never
|
|
1299
|
+
* fail the deposit — the funds moved. But it is also the ONLY source of the
|
|
1300
|
+
* "Top up wallet" entry the history is built from: Zyfai auto-deploys the
|
|
1301
|
+
* balance it detects either way, so when this call is lost the user's deposit
|
|
1302
|
+
* never appears in Activity (an earlier withdrawal stays the newest row) and
|
|
1303
|
+
* nothing ever backfills it. Retry, then log loudly enough to be recoverable.
|
|
1304
|
+
*/
|
|
1305
|
+
async logDepositWithRetry(chainId, txHash, amount, tokenAddress) {
|
|
1306
|
+
const ATTEMPTS = 3;
|
|
1307
|
+
const RETRY_DELAY_MS = 1e3;
|
|
1308
|
+
for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
|
|
1309
|
+
try {
|
|
1310
|
+
await (tokenAddress ? this.sdk.logDeposit(chainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(chainId, txHash, amount));
|
|
1311
|
+
return;
|
|
1312
|
+
} catch (logError) {
|
|
1313
|
+
if (attempt === ATTEMPTS) {
|
|
1314
|
+
console.error(
|
|
1315
|
+
"[owney-sdk] Deposit landed on-chain but logDeposit failed \u2014 it will be missing from Zyfai history:",
|
|
1316
|
+
{ txHash, chainId, amount, tokenAddress, error: logError }
|
|
1317
|
+
);
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
await new Promise(
|
|
1321
|
+
(resolve) => setTimeout(resolve, RETRY_DELAY_MS * attempt)
|
|
1322
|
+
);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1282
1326
|
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
1283
1327
|
try {
|
|
1284
1328
|
await this.connectFull(state, chainId);
|
|
@@ -1294,14 +1338,12 @@ var ZyfaiAgent = class {
|
|
|
1294
1338
|
amount
|
|
1295
1339
|
);
|
|
1296
1340
|
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
);
|
|
1304
|
-
}
|
|
1341
|
+
await this.logDepositWithRetry(
|
|
1342
|
+
validChainId,
|
|
1343
|
+
txHash,
|
|
1344
|
+
amount,
|
|
1345
|
+
tokenAddress
|
|
1346
|
+
);
|
|
1305
1347
|
return { txHash, smartWallet: wallet.address, amount };
|
|
1306
1348
|
}
|
|
1307
1349
|
const raw = await this.sdk.depositFunds(
|
|
@@ -1540,11 +1582,10 @@ async function withFailureReporting(apiKey, agentType, fn) {
|
|
|
1540
1582
|
// src/lib/helpers/withdraw-helper.ts
|
|
1541
1583
|
import { parseUnits } from "viem";
|
|
1542
1584
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
1543
|
-
const target = asset.toUpperCase();
|
|
1544
1585
|
return agents.map((agent) => {
|
|
1545
1586
|
const agentBalance = aggregated[agent.id];
|
|
1546
1587
|
const tokenBalance = agentBalance?.tokens.find(
|
|
1547
|
-
(t) => t.chainId === chainId && t.asset
|
|
1588
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
1548
1589
|
);
|
|
1549
1590
|
if (!tokenBalance) return { agent, balance: 0n };
|
|
1550
1591
|
return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
|
|
@@ -1814,84 +1855,6 @@ async function getSponsorRelayerAddress(input) {
|
|
|
1814
1855
|
return parsed.data.relayer;
|
|
1815
1856
|
}
|
|
1816
1857
|
|
|
1817
|
-
// src/lib/permit2.ts
|
|
1818
|
-
import { bytesToHex as bytesToHex2 } from "viem";
|
|
1819
|
-
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
1820
|
-
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
1821
|
-
var ERC20_ALLOWANCE_ABI = [
|
|
1822
|
-
{
|
|
1823
|
-
type: "function",
|
|
1824
|
-
name: "allowance",
|
|
1825
|
-
stateMutability: "view",
|
|
1826
|
-
inputs: [
|
|
1827
|
-
{ name: "owner", type: "address" },
|
|
1828
|
-
{ name: "spender", type: "address" }
|
|
1829
|
-
],
|
|
1830
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
1831
|
-
},
|
|
1832
|
-
{
|
|
1833
|
-
type: "function",
|
|
1834
|
-
name: "approve",
|
|
1835
|
-
stateMutability: "nonpayable",
|
|
1836
|
-
inputs: [
|
|
1837
|
-
{ name: "spender", type: "address" },
|
|
1838
|
-
{ name: "amount", type: "uint256" }
|
|
1839
|
-
],
|
|
1840
|
-
outputs: [{ name: "", type: "bool" }]
|
|
1841
|
-
},
|
|
1842
|
-
{
|
|
1843
|
-
type: "function",
|
|
1844
|
-
name: "balanceOf",
|
|
1845
|
-
stateMutability: "view",
|
|
1846
|
-
inputs: [{ name: "account", type: "address" }],
|
|
1847
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
1848
|
-
}
|
|
1849
|
-
];
|
|
1850
|
-
function buildPermitTransferFromTypedData(input) {
|
|
1851
|
-
return {
|
|
1852
|
-
domain: {
|
|
1853
|
-
name: "Permit2",
|
|
1854
|
-
chainId: input.chainId,
|
|
1855
|
-
verifyingContract: PERMIT2_ADDRESS
|
|
1856
|
-
},
|
|
1857
|
-
types: {
|
|
1858
|
-
PermitTransferFrom: [
|
|
1859
|
-
{ name: "permitted", type: "TokenPermissions" },
|
|
1860
|
-
{ name: "spender", type: "address" },
|
|
1861
|
-
{ name: "nonce", type: "uint256" },
|
|
1862
|
-
{ name: "deadline", type: "uint256" }
|
|
1863
|
-
],
|
|
1864
|
-
TokenPermissions: [
|
|
1865
|
-
{ name: "token", type: "address" },
|
|
1866
|
-
{ name: "amount", type: "uint256" }
|
|
1867
|
-
]
|
|
1868
|
-
},
|
|
1869
|
-
primaryType: "PermitTransferFrom",
|
|
1870
|
-
message: input.message
|
|
1871
|
-
};
|
|
1872
|
-
}
|
|
1873
|
-
function randomPermit2Nonce() {
|
|
1874
|
-
const bytes = new Uint8Array(32);
|
|
1875
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
1876
|
-
return BigInt(bytesToHex2(bytes));
|
|
1877
|
-
}
|
|
1878
|
-
async function readPermit2Allowance(publicClient, token, owner) {
|
|
1879
|
-
return publicClient.readContract({
|
|
1880
|
-
address: token,
|
|
1881
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
1882
|
-
functionName: "allowance",
|
|
1883
|
-
args: [owner, PERMIT2_ADDRESS]
|
|
1884
|
-
});
|
|
1885
|
-
}
|
|
1886
|
-
async function readErc20Balance(publicClient, token, owner) {
|
|
1887
|
-
return publicClient.readContract({
|
|
1888
|
-
address: token,
|
|
1889
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
1890
|
-
functionName: "balanceOf",
|
|
1891
|
-
args: [owner]
|
|
1892
|
-
});
|
|
1893
|
-
}
|
|
1894
|
-
|
|
1895
1858
|
// src/lib/sponsored-deposit.ts
|
|
1896
1859
|
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
1897
1860
|
function makeSponsoredDepositCallback(deps) {
|
|
@@ -2349,7 +2312,7 @@ var OwneySDK = class {
|
|
|
2349
2312
|
}
|
|
2350
2313
|
if (agentId === "surfliquid") {
|
|
2351
2314
|
if (!projectName) return null;
|
|
2352
|
-
const { SurfLiquidAgent } = await import("./surfliquid.agent-
|
|
2315
|
+
const { SurfLiquidAgent } = await import("./surfliquid.agent-VNGQUSXN.js");
|
|
2353
2316
|
return new SurfLiquidAgent({
|
|
2354
2317
|
projectName,
|
|
2355
2318
|
appId: key2,
|
|
@@ -2652,9 +2615,8 @@ var OwneySDK = class {
|
|
|
2652
2615
|
async hasExistingBalance(agent, state, chainId, asset) {
|
|
2653
2616
|
try {
|
|
2654
2617
|
const balance = await agent.getBalances(state, chainId);
|
|
2655
|
-
const target = asset.toLowerCase();
|
|
2656
2618
|
const token = balance.tokens.find(
|
|
2657
|
-
(t) => t.chainId === chainId && t.asset
|
|
2619
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
2658
2620
|
);
|
|
2659
2621
|
return !!token && Number(token.amount) > 0;
|
|
2660
2622
|
} catch {
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ERC20_ALLOWANCE_ABI,
|
|
2
3
|
OwneyError
|
|
3
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PWIKIQOX.js";
|
|
4
5
|
|
|
5
6
|
// src/agents/surfliquid/surfliquid.agent.ts
|
|
6
7
|
import { SurfClient } from "@surf_liquid/core-sdk";
|
|
7
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
createPublicClient,
|
|
10
|
+
createWalletClient,
|
|
11
|
+
custom,
|
|
12
|
+
formatUnits
|
|
13
|
+
} from "viem";
|
|
14
|
+
import { base } from "viem/chains";
|
|
8
15
|
|
|
9
16
|
// src/agents/surfliquid/surfliquid.constants.ts
|
|
10
17
|
var SURFLIQUID_CHAIN_ID = 8453;
|
|
@@ -12,6 +19,7 @@ var SURFLIQUID_CHAIN_NAME = "BASE";
|
|
|
12
19
|
var SURFLIQUID_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
13
20
|
var SURFLIQUID_USDC_DECIMALS = 6;
|
|
14
21
|
var SURFLIQUID_MIN_DEPOSIT = "0";
|
|
22
|
+
var SURFLIQUID_APPROVAL_TARGET = 100000000000n;
|
|
15
23
|
var SURFLIQUID_SUPPORTED_ASSETS = [
|
|
16
24
|
{
|
|
17
25
|
chainId: SURFLIQUID_CHAIN_ID,
|
|
@@ -166,27 +174,29 @@ var VAULT_STATS_QUERY = `
|
|
|
166
174
|
}
|
|
167
175
|
}
|
|
168
176
|
`;
|
|
169
|
-
|
|
177
|
+
var VAULT_V2_STATS_QUERY = `
|
|
178
|
+
query VaultV2Stats($address: String!, $chainId: Int!) {
|
|
179
|
+
vaultV2ByAddress(address: $address, chainId: $chainId) {
|
|
180
|
+
name
|
|
181
|
+
totalAssetsUsd
|
|
182
|
+
liquidityUsd
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
async function queryMorpho(query, vaultAddress, chainId) {
|
|
170
187
|
try {
|
|
171
188
|
const res = await fetch(MORPHO_API_URL, {
|
|
172
189
|
method: "POST",
|
|
173
190
|
headers: { "Content-Type": "application/json" },
|
|
174
191
|
body: JSON.stringify({
|
|
175
|
-
query
|
|
192
|
+
query,
|
|
176
193
|
variables: { address: vaultAddress, chainId }
|
|
177
194
|
})
|
|
178
195
|
});
|
|
179
196
|
if (!res.ok) return null;
|
|
180
197
|
const json = await res.json();
|
|
181
198
|
if (json.errors) return null;
|
|
182
|
-
|
|
183
|
-
const tvlUsd = vault?.state?.totalAssetsUsd;
|
|
184
|
-
const liquidityUsd = vault?.liquidity?.usd;
|
|
185
|
-
if (typeof tvlUsd !== "number" || typeof liquidityUsd !== "number") {
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
188
|
-
const name = typeof vault?.name === "string" ? vault.name : null;
|
|
189
|
-
return { name, tvlUsd, liquidityUsd };
|
|
199
|
+
return json.data ?? null;
|
|
190
200
|
} catch (error) {
|
|
191
201
|
console.warn("surfliquid: Morpho vault stats fetch failed", {
|
|
192
202
|
vaultAddress,
|
|
@@ -196,6 +206,29 @@ async function fetchMorphoVaultStats(vaultAddress, chainId) {
|
|
|
196
206
|
return null;
|
|
197
207
|
}
|
|
198
208
|
}
|
|
209
|
+
function toStats(name, tvlUsd, liquidityUsd) {
|
|
210
|
+
if (typeof tvlUsd !== "number" || typeof liquidityUsd !== "number") {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
name: typeof name === "string" ? name : null,
|
|
215
|
+
tvlUsd,
|
|
216
|
+
liquidityUsd
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
async function fetchMorphoVaultStats(vaultAddress, chainId) {
|
|
220
|
+
const v1 = await queryMorpho(VAULT_STATS_QUERY, vaultAddress, chainId);
|
|
221
|
+
const vault = v1?.vaultByAddress;
|
|
222
|
+
const v1Stats = toStats(
|
|
223
|
+
vault?.name,
|
|
224
|
+
vault?.state?.totalAssetsUsd,
|
|
225
|
+
vault?.liquidity?.usd
|
|
226
|
+
);
|
|
227
|
+
if (v1Stats) return v1Stats;
|
|
228
|
+
const v2 = await queryMorpho(VAULT_V2_STATS_QUERY, vaultAddress, chainId);
|
|
229
|
+
const vaultV2 = v2?.vaultV2ByAddress;
|
|
230
|
+
return toStats(vaultV2?.name, vaultV2?.totalAssetsUsd, vaultV2?.liquidityUsd);
|
|
231
|
+
}
|
|
199
232
|
async function fetchMorphoStatsByVault(vaultAddresses, chainId) {
|
|
200
233
|
const unique = [...new Set(vaultAddresses.map((a) => a.toLowerCase()))];
|
|
201
234
|
const entries = await Promise.all(
|
|
@@ -283,6 +316,60 @@ var SurfLiquidAgent = class {
|
|
|
283
316
|
const { vaultAddress } = await surf.deployVault();
|
|
284
317
|
return vaultAddress;
|
|
285
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Grants the user's vault a standing USDC allowance so SurfLiquid's
|
|
321
|
+
* `autoApprove` has nothing to do — see {@link SURFLIQUID_APPROVAL_TARGET}
|
|
322
|
+
* for why one prompt per deposit is worth removing.
|
|
323
|
+
*
|
|
324
|
+
* Fails OPEN on a read error: if the allowance can't be checked we simply
|
|
325
|
+
* return and let SurfLiquid's own per-deposit approve run, because a flaky
|
|
326
|
+
* RPC must never block a deposit. A failed or rejected APPROVE does
|
|
327
|
+
* propagate — the user declined a transaction, and letting SurfLiquid
|
|
328
|
+
* immediately ask again for a smaller one is exactly the double prompt this
|
|
329
|
+
* removes.
|
|
330
|
+
*/
|
|
331
|
+
async ensureVaultAllowance(state, vaultAddress, amount) {
|
|
332
|
+
const spender = vaultAddress;
|
|
333
|
+
const transport = custom(state.provider);
|
|
334
|
+
const publicClient = createPublicClient({ chain: base, transport });
|
|
335
|
+
let allowance;
|
|
336
|
+
try {
|
|
337
|
+
allowance = await publicClient.readContract({
|
|
338
|
+
address: SURFLIQUID_USDC_ADDRESS,
|
|
339
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
340
|
+
functionName: "allowance",
|
|
341
|
+
args: [state.walletAddress, spender]
|
|
342
|
+
});
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.warn(
|
|
345
|
+
"[owney-sdk] SurfLiquid allowance pre-check failed; deferring to its per-deposit approve:",
|
|
346
|
+
error instanceof Error ? error.message : String(error)
|
|
347
|
+
);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (allowance >= amount) return;
|
|
351
|
+
const target = amount > SURFLIQUID_APPROVAL_TARGET ? amount : SURFLIQUID_APPROVAL_TARGET;
|
|
352
|
+
const wallet = createWalletClient({
|
|
353
|
+
account: state.walletAddress,
|
|
354
|
+
chain: base,
|
|
355
|
+
transport
|
|
356
|
+
});
|
|
357
|
+
const hash = await wallet.writeContract({
|
|
358
|
+
address: SURFLIQUID_USDC_ADDRESS,
|
|
359
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
360
|
+
functionName: "approve",
|
|
361
|
+
args: [spender, target],
|
|
362
|
+
account: state.walletAddress,
|
|
363
|
+
chain: base
|
|
364
|
+
});
|
|
365
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
366
|
+
hash,
|
|
367
|
+
confirmations: 1
|
|
368
|
+
});
|
|
369
|
+
if (receipt.status !== "success") {
|
|
370
|
+
throw new Error(`SurfLiquid USDC approval reverted (tx ${hash})`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
286
373
|
// --- IAgent: lifecycle ---
|
|
287
374
|
async disconnect() {
|
|
288
375
|
if (this.surf) {
|
|
@@ -301,6 +388,7 @@ var SurfLiquidAgent = class {
|
|
|
301
388
|
surf,
|
|
302
389
|
state.walletAddress
|
|
303
390
|
);
|
|
391
|
+
await this.ensureVaultAllowance(state, smartWallet, BigInt(amount));
|
|
304
392
|
const tx = await surf.deposit({
|
|
305
393
|
asset: SURFLIQUID_USDC_ADDRESS,
|
|
306
394
|
amount: human
|
package/package.json
CHANGED
package/dist/chunk-GMNQBWUB.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// src/errors.ts
|
|
2
|
-
var OwneyError = class extends Error {
|
|
3
|
-
code;
|
|
4
|
-
details;
|
|
5
|
-
agentId;
|
|
6
|
-
constructor(code, message, details, agentId) {
|
|
7
|
-
const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
|
|
8
|
-
super(`${prefix} ${message}`);
|
|
9
|
-
this.name = "OwneyError";
|
|
10
|
-
this.code = code;
|
|
11
|
-
this.details = details;
|
|
12
|
-
this.agentId = agentId;
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
var AgentNotFoundError = class extends OwneyError {
|
|
16
|
-
constructor(agentId, available) {
|
|
17
|
-
super(
|
|
18
|
-
"AGENT_NOT_FOUND",
|
|
19
|
-
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
20
|
-
{ agentId, available },
|
|
21
|
-
agentId
|
|
22
|
-
);
|
|
23
|
-
this.name = "AgentNotFoundError";
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
var NotConnectedError = class extends OwneyError {
|
|
27
|
-
constructor() {
|
|
28
|
-
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
29
|
-
this.name = "NotConnectedError";
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
var AgentChainIncompatibleError = class extends OwneyError {
|
|
33
|
-
incompatibleAgents;
|
|
34
|
-
connectedChainId;
|
|
35
|
-
constructor(incompatibleAgents, connectedChainId) {
|
|
36
|
-
const details = incompatibleAgents.map(
|
|
37
|
-
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
38
|
-
).join("; ");
|
|
39
|
-
super(
|
|
40
|
-
"AGENT_CHAIN_INCOMPATIBLE",
|
|
41
|
-
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
42
|
-
{ incompatibleAgents, connectedChainId }
|
|
43
|
-
);
|
|
44
|
-
this.name = "AgentChainIncompatibleError";
|
|
45
|
-
this.incompatibleAgents = incompatibleAgents;
|
|
46
|
-
this.connectedChainId = connectedChainId;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export {
|
|
51
|
-
OwneyError,
|
|
52
|
-
AgentNotFoundError,
|
|
53
|
-
NotConnectedError,
|
|
54
|
-
AgentChainIncompatibleError
|
|
55
|
-
};
|