@owney/sdk 0.7.25-beta.1 → 0.7.25-beta.10
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/README.md +79 -14
- package/dist/index.cjs +971 -290
- package/dist/index.d.cts +228 -157
- package/dist/index.d.ts +228 -157
- package/dist/index.js +917 -233
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -159,6 +159,34 @@ function rateLimitDelay(error, now = Date.now()) {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// src/lib/agent-reads.ts
|
|
162
|
+
function rateLimitDiagnostics(error) {
|
|
163
|
+
const seen = /* @__PURE__ */ new Set();
|
|
164
|
+
let details = {};
|
|
165
|
+
function visit(value, depth = 0) {
|
|
166
|
+
if (depth > 6 || !value || typeof value !== "object" || seen.has(value))
|
|
167
|
+
return;
|
|
168
|
+
seen.add(value);
|
|
169
|
+
const record = value;
|
|
170
|
+
for (const key2 of [
|
|
171
|
+
"rpcSource",
|
|
172
|
+
"chainId",
|
|
173
|
+
"rpcMethod",
|
|
174
|
+
"providerRequestId",
|
|
175
|
+
"statusCode"
|
|
176
|
+
]) {
|
|
177
|
+
const candidate = record[key2];
|
|
178
|
+
if (candidate !== void 0 && details[key2] === void 0)
|
|
179
|
+
details = { ...details, [key2]: candidate };
|
|
180
|
+
}
|
|
181
|
+
for (const key2 of ["details", "cause", "response", "fields", "error"])
|
|
182
|
+
visit(record[key2], depth + 1);
|
|
183
|
+
}
|
|
184
|
+
visit(error);
|
|
185
|
+
return {
|
|
186
|
+
rpcSource: details.rpcSource ?? "agent-api",
|
|
187
|
+
...details
|
|
188
|
+
};
|
|
189
|
+
}
|
|
162
190
|
var AgentReads = class {
|
|
163
191
|
inFlight = /* @__PURE__ */ new Map();
|
|
164
192
|
cooldowns = /* @__PURE__ */ new Map();
|
|
@@ -166,14 +194,15 @@ var AgentReads = class {
|
|
|
166
194
|
clearInFlight() {
|
|
167
195
|
this.inFlight.clear();
|
|
168
196
|
}
|
|
169
|
-
limited(agentId, until) {
|
|
197
|
+
limited(agentId, until, diagnostics) {
|
|
170
198
|
return new OwneyError(
|
|
171
199
|
"AGENT_RATE_LIMITED",
|
|
172
200
|
"Too many requests. Please wait before trying again.",
|
|
173
201
|
{
|
|
174
202
|
statusCode: 429,
|
|
175
203
|
retryAt: until,
|
|
176
|
-
retryAfterSeconds: Math.max(0, Math.ceil((until - Date.now()) / 1e3))
|
|
204
|
+
retryAfterSeconds: Math.max(0, Math.ceil((until - Date.now()) / 1e3)),
|
|
205
|
+
...diagnostics
|
|
177
206
|
},
|
|
178
207
|
agentId
|
|
179
208
|
);
|
|
@@ -181,7 +210,9 @@ var AgentReads = class {
|
|
|
181
210
|
run(agentId, key2, fetch2) {
|
|
182
211
|
const cooldown = this.cooldowns.get(agentId);
|
|
183
212
|
if (cooldown && cooldown.until > Date.now()) {
|
|
184
|
-
return Promise.reject(
|
|
213
|
+
return Promise.reject(
|
|
214
|
+
this.limited(agentId, cooldown.until, cooldown.diagnostics)
|
|
215
|
+
);
|
|
185
216
|
}
|
|
186
217
|
const requestKey = JSON.stringify([agentId, key2]);
|
|
187
218
|
const existing = this.inFlight.get(requestKey);
|
|
@@ -202,8 +233,9 @@ var AgentReads = class {
|
|
|
202
233
|
Math.min(3e4 * 2 ** (failures - 1), 3e5)
|
|
203
234
|
);
|
|
204
235
|
const until = Math.max(previous?.until ?? 0, Date.now() + delay);
|
|
205
|
-
|
|
206
|
-
|
|
236
|
+
const diagnostics = rateLimitDiagnostics(error);
|
|
237
|
+
this.cooldowns.set(agentId, { until, failures, diagnostics });
|
|
238
|
+
throw this.limited(agentId, until, diagnostics);
|
|
207
239
|
}
|
|
208
240
|
).finally(() => {
|
|
209
241
|
if (this.inFlight.get(requestKey) === promise)
|
|
@@ -216,7 +248,7 @@ var AgentReads = class {
|
|
|
216
248
|
|
|
217
249
|
// src/agents/zyfai/zyfai.agent.ts
|
|
218
250
|
var import_sdk = require("@zyfai/sdk");
|
|
219
|
-
var
|
|
251
|
+
var import_viem2 = require("viem");
|
|
220
252
|
var import_chains = require("viem/chains");
|
|
221
253
|
|
|
222
254
|
// src/types/config.ts
|
|
@@ -1097,15 +1129,145 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
1097
1129
|
return !protocolListsEqual(current.protocols, desiredProtocols);
|
|
1098
1130
|
}
|
|
1099
1131
|
|
|
1132
|
+
// src/lib/paid-rpc.ts
|
|
1133
|
+
var import_viem = require("viem");
|
|
1134
|
+
var PAID_RPC_RETRY_COUNT = 3;
|
|
1135
|
+
var PAID_RPC_RETRY_DELAY_MS = 1e3;
|
|
1136
|
+
function configuredRpcProxyBaseUrl(baseUrl) {
|
|
1137
|
+
const configured = (baseUrl ?? "")?.trim();
|
|
1138
|
+
if (!configured) {
|
|
1139
|
+
throw new Error(
|
|
1140
|
+
"OWNEY_ROUTING_API_BASE_URL is required for RPC proxy requests unless routingApiBaseUrl or all rpcUrls are configured."
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
return configured;
|
|
1144
|
+
}
|
|
1145
|
+
function rpcProxyUrl(chainId, apiKey, baseUrl) {
|
|
1146
|
+
const url = new URL(
|
|
1147
|
+
`${configuredRpcProxyBaseUrl(baseUrl).replace(/\/$/, "")}/api/v1/rpc/${chainId}`
|
|
1148
|
+
);
|
|
1149
|
+
if (apiKey) url.searchParams.set("apiKey", apiKey);
|
|
1150
|
+
return url.toString();
|
|
1151
|
+
}
|
|
1152
|
+
function resolveRpcUrl(rpcUrls, chainId, apiKey = "", baseUrl) {
|
|
1153
|
+
const url = rpcUrls?.[chainId]?.trim();
|
|
1154
|
+
if (url) return url;
|
|
1155
|
+
return rpcProxyUrl(chainId, apiKey, baseUrl);
|
|
1156
|
+
}
|
|
1157
|
+
function resolveRpcUrls(rpcUrls, apiKey = "", baseUrl) {
|
|
1158
|
+
return {
|
|
1159
|
+
1: resolveRpcUrl(rpcUrls, 1, apiKey, baseUrl),
|
|
1160
|
+
8453: resolveRpcUrl(rpcUrls, 8453, apiKey, baseUrl),
|
|
1161
|
+
42161: resolveRpcUrl(rpcUrls, 42161, apiKey, baseUrl)
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
function createPaidRpcClient(chain, rpcUrls) {
|
|
1165
|
+
const url = resolveRpcUrl(
|
|
1166
|
+
rpcUrls,
|
|
1167
|
+
chain.id
|
|
1168
|
+
);
|
|
1169
|
+
return (0, import_viem.createPublicClient)({
|
|
1170
|
+
chain,
|
|
1171
|
+
transport: (0, import_viem.http)(url, {
|
|
1172
|
+
retryCount: PAID_RPC_RETRY_COUNT,
|
|
1173
|
+
retryDelay: PAID_RPC_RETRY_DELAY_MS
|
|
1174
|
+
})
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
function headerValue(error, name) {
|
|
1178
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1179
|
+
let value;
|
|
1180
|
+
function visit(candidate, depth = 0) {
|
|
1181
|
+
if (value || depth > 6 || !candidate || typeof candidate !== "object")
|
|
1182
|
+
return;
|
|
1183
|
+
if (seen.has(candidate)) return;
|
|
1184
|
+
seen.add(candidate);
|
|
1185
|
+
const record = candidate;
|
|
1186
|
+
const headers = record.headers;
|
|
1187
|
+
let found;
|
|
1188
|
+
if (typeof headers?.get === "function") {
|
|
1189
|
+
found = headers.get(name);
|
|
1190
|
+
} else {
|
|
1191
|
+
const headerRecord = headers;
|
|
1192
|
+
found = headerRecord?.[name] ?? headerRecord?.[name.toLowerCase()];
|
|
1193
|
+
}
|
|
1194
|
+
if (typeof found === "string" && found) {
|
|
1195
|
+
value = found;
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
for (const key2 of ["cause", "details", "response", "error"])
|
|
1199
|
+
visit(record[key2], depth + 1);
|
|
1200
|
+
}
|
|
1201
|
+
visit(error);
|
|
1202
|
+
return value;
|
|
1203
|
+
}
|
|
1204
|
+
function statusCode(error) {
|
|
1205
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1206
|
+
let status;
|
|
1207
|
+
function visit(candidate, depth = 0) {
|
|
1208
|
+
if (status || depth > 6 || !candidate || typeof candidate !== "object")
|
|
1209
|
+
return;
|
|
1210
|
+
if (seen.has(candidate)) return;
|
|
1211
|
+
seen.add(candidate);
|
|
1212
|
+
const record = candidate;
|
|
1213
|
+
for (const key2 of ["status", "statusCode"]) {
|
|
1214
|
+
const parsed = Number(record[key2]);
|
|
1215
|
+
if (Number.isInteger(parsed) && parsed >= 100 && parsed <= 599) {
|
|
1216
|
+
status = parsed;
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
for (const key2 of ["cause", "details", "response", "error"])
|
|
1221
|
+
visit(record[key2], depth + 1);
|
|
1222
|
+
}
|
|
1223
|
+
visit(error);
|
|
1224
|
+
return status;
|
|
1225
|
+
}
|
|
1226
|
+
function paidRpcError(error, chainId, rpcMethod, agentId) {
|
|
1227
|
+
const delay = rateLimitDelay(error);
|
|
1228
|
+
const providerRequestId = headerValue(error, "x-alchemy-request-id") ?? headerValue(error, "x-request-id");
|
|
1229
|
+
if (delay === void 0) {
|
|
1230
|
+
return new OwneyError(
|
|
1231
|
+
"AGENT_API_ERROR",
|
|
1232
|
+
"The blockchain RPC request failed.",
|
|
1233
|
+
{
|
|
1234
|
+
rpcSource: "paid-rpc",
|
|
1235
|
+
chainId,
|
|
1236
|
+
rpcMethod,
|
|
1237
|
+
...statusCode(error) ? { statusCode: statusCode(error) } : {},
|
|
1238
|
+
...providerRequestId ? { providerRequestId } : {}
|
|
1239
|
+
},
|
|
1240
|
+
agentId
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
const retryAt = Date.now() + delay;
|
|
1244
|
+
return new OwneyError(
|
|
1245
|
+
"AGENT_RATE_LIMITED",
|
|
1246
|
+
"The blockchain RPC is rate limited. Please wait before trying again.",
|
|
1247
|
+
{
|
|
1248
|
+
rpcSource: "paid-rpc",
|
|
1249
|
+
chainId,
|
|
1250
|
+
rpcMethod,
|
|
1251
|
+
statusCode: 429,
|
|
1252
|
+
retryAt,
|
|
1253
|
+
retryAfterSeconds: Math.max(0, Math.ceil(delay / 1e3)),
|
|
1254
|
+
...providerRequestId ? { providerRequestId } : {}
|
|
1255
|
+
},
|
|
1256
|
+
agentId
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
|
|
1260
|
+
try {
|
|
1261
|
+
return await operation();
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
throw paidRpcError(error, chainId, rpcMethod, agentId);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1100
1267
|
// src/agents/zyfai/zyfai.agent.ts
|
|
1101
|
-
var ERC7579_IS_MODULE_INSTALLED_ABI = (0,
|
|
1268
|
+
var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem2.parseAbi)([
|
|
1102
1269
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
1103
1270
|
]);
|
|
1104
|
-
var DEFAULT_ZYFAI_RPC_URLS = {
|
|
1105
|
-
8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
1106
|
-
42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
1107
|
-
1: "https://eth-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
|
|
1108
|
-
};
|
|
1109
1271
|
var WETH_ADDRESS_BY_CHAIN = {
|
|
1110
1272
|
8453: "0x4200000000000000000000000000000000000006",
|
|
1111
1273
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -1171,7 +1333,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1171
1333
|
earningsSnapshot = null;
|
|
1172
1334
|
earningsGeneration = 0;
|
|
1173
1335
|
constructor(apiKey, rpcUrls, referralSource) {
|
|
1174
|
-
this.rpcUrls = rpcUrls
|
|
1336
|
+
this.rpcUrls = resolveRpcUrls(rpcUrls);
|
|
1175
1337
|
this.sdk = new import_sdk.ZyfaiSDK({
|
|
1176
1338
|
apiKey,
|
|
1177
1339
|
rpcUrls: this.rpcUrls,
|
|
@@ -1185,10 +1347,10 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1185
1347
|
getPublicClient(chainId) {
|
|
1186
1348
|
const cached = this.publicClients.get(chainId);
|
|
1187
1349
|
if (cached) return cached;
|
|
1188
|
-
const client = (
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1350
|
+
const client = createPaidRpcClient(
|
|
1351
|
+
VIEM_CHAIN[chainId],
|
|
1352
|
+
this.rpcUrls
|
|
1353
|
+
);
|
|
1192
1354
|
this.publicClients.set(chainId, client);
|
|
1193
1355
|
return client;
|
|
1194
1356
|
}
|
|
@@ -2156,7 +2318,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2156
2318
|
};
|
|
2157
2319
|
|
|
2158
2320
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2159
|
-
var
|
|
2321
|
+
var import_viem7 = require("viem");
|
|
2160
2322
|
var import_chains3 = require("viem/chains");
|
|
2161
2323
|
|
|
2162
2324
|
// src/lib/chain-guard.ts
|
|
@@ -2195,7 +2357,7 @@ async function ensureWalletOnChain(pub, wallet, expected) {
|
|
|
2195
2357
|
}
|
|
2196
2358
|
|
|
2197
2359
|
// src/lib/transfer-auth.ts
|
|
2198
|
-
var
|
|
2360
|
+
var import_viem3 = require("viem");
|
|
2199
2361
|
|
|
2200
2362
|
// src/lib/sponsor-client.ts
|
|
2201
2363
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
@@ -2311,13 +2473,14 @@ async function postSponsorBatchTransfer(input) {
|
|
|
2311
2473
|
}
|
|
2312
2474
|
|
|
2313
2475
|
// src/lib/permit2.ts
|
|
2314
|
-
var
|
|
2476
|
+
var import_viem4 = require("viem");
|
|
2315
2477
|
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2478
|
+
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2316
2479
|
function permit2ApprovalAmount(requiredAmount) {
|
|
2317
2480
|
if (requiredAmount <= 0n) {
|
|
2318
2481
|
throw new Error("Permit2 approval requires a positive deposit amount");
|
|
2319
2482
|
}
|
|
2320
|
-
return
|
|
2483
|
+
return MAX_UINT256;
|
|
2321
2484
|
}
|
|
2322
2485
|
var ERC20_ALLOWANCE_ABI = [
|
|
2323
2486
|
{
|
|
@@ -2351,14 +2514,15 @@ var ERC20_ALLOWANCE_ABI = [
|
|
|
2351
2514
|
function randomPermit2Nonce() {
|
|
2352
2515
|
const bytes = new Uint8Array(32);
|
|
2353
2516
|
globalThis.crypto.getRandomValues(bytes);
|
|
2354
|
-
return BigInt((0,
|
|
2517
|
+
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
2355
2518
|
}
|
|
2356
|
-
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2519
|
+
async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
|
|
2357
2520
|
return publicClient.readContract({
|
|
2358
2521
|
address: token,
|
|
2359
2522
|
abi: ERC20_ALLOWANCE_ABI,
|
|
2360
2523
|
functionName: "allowance",
|
|
2361
|
-
args: [owner, PERMIT2_ADDRESS]
|
|
2524
|
+
args: [owner, PERMIT2_ADDRESS],
|
|
2525
|
+
...blockNumber === void 0 ? {} : { blockNumber }
|
|
2362
2526
|
});
|
|
2363
2527
|
}
|
|
2364
2528
|
async function readErc20Balance(publicClient, token, owner) {
|
|
@@ -2393,7 +2557,7 @@ function makeVerificationAwareDepositCallback(implementation) {
|
|
|
2393
2557
|
|
|
2394
2558
|
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2395
2559
|
var import_siwe = require("siwe");
|
|
2396
|
-
var
|
|
2560
|
+
var import_viem5 = require("viem");
|
|
2397
2561
|
var import_chains2 = require("viem/chains");
|
|
2398
2562
|
|
|
2399
2563
|
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
@@ -2506,7 +2670,7 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
|
2506
2670
|
return new import_siwe.SiweMessage({
|
|
2507
2671
|
scheme: url.protocol.slice(0, -1),
|
|
2508
2672
|
domain: url.host,
|
|
2509
|
-
address: (0,
|
|
2673
|
+
address: (0, import_viem5.getAddress)(address),
|
|
2510
2674
|
uri: url.origin,
|
|
2511
2675
|
version: "1",
|
|
2512
2676
|
chainId,
|
|
@@ -2592,15 +2756,15 @@ var YieldseekerAuth = class {
|
|
|
2592
2756
|
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2593
2757
|
}
|
|
2594
2758
|
async sign(state, chainId) {
|
|
2595
|
-
const account = (0,
|
|
2596
|
-
const publicClient = (0,
|
|
2759
|
+
const account = (0, import_viem5.getAddress)(state.walletAddress);
|
|
2760
|
+
const publicClient = (0, import_viem5.createPublicClient)({
|
|
2597
2761
|
chain: import_chains2.base,
|
|
2598
|
-
transport: (0,
|
|
2762
|
+
transport: (0, import_viem5.custom)(state.provider)
|
|
2599
2763
|
});
|
|
2600
|
-
const walletClient = (0,
|
|
2764
|
+
const walletClient = (0, import_viem5.createWalletClient)({
|
|
2601
2765
|
account,
|
|
2602
2766
|
chain: import_chains2.base,
|
|
2603
|
-
transport: (0,
|
|
2767
|
+
transport: (0, import_viem5.custom)(state.provider)
|
|
2604
2768
|
});
|
|
2605
2769
|
await ensureWalletOnChain(
|
|
2606
2770
|
publicClient,
|
|
@@ -2741,10 +2905,11 @@ var YieldseekerApiClient = class {
|
|
|
2741
2905
|
const payload = await response.json().catch(() => null);
|
|
2742
2906
|
if (!response.ok) {
|
|
2743
2907
|
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2908
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
2744
2909
|
throw new YieldseekerApiError(
|
|
2745
2910
|
response.status,
|
|
2746
2911
|
error.code,
|
|
2747
|
-
error.fields
|
|
2912
|
+
retryAfter ? { ...error.fields, headers: { "Retry-After": retryAfter } } : error.fields
|
|
2748
2913
|
);
|
|
2749
2914
|
}
|
|
2750
2915
|
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
@@ -2766,7 +2931,7 @@ var YieldseekerApiClient = class {
|
|
|
2766
2931
|
};
|
|
2767
2932
|
|
|
2768
2933
|
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2769
|
-
var
|
|
2934
|
+
var import_viem6 = require("viem");
|
|
2770
2935
|
|
|
2771
2936
|
// src/lib/helpers/snapshot-apy.ts
|
|
2772
2937
|
var DAY_MS = 864e5;
|
|
@@ -2833,10 +2998,10 @@ function raw(value, endpoint) {
|
|
|
2833
2998
|
return BigInt(value);
|
|
2834
2999
|
}
|
|
2835
3000
|
function decimal(value, decimals, endpoint) {
|
|
2836
|
-
return (0,
|
|
3001
|
+
return (0, import_viem6.formatUnits)(raw(value, endpoint), decimals);
|
|
2837
3002
|
}
|
|
2838
3003
|
function usd(rawAmount, decimals, price) {
|
|
2839
|
-
return Number((0,
|
|
3004
|
+
return Number((0, import_viem6.formatUnits)(rawAmount, decimals)) * price;
|
|
2840
3005
|
}
|
|
2841
3006
|
function percent(value) {
|
|
2842
3007
|
const result = Number(value);
|
|
@@ -2862,7 +3027,7 @@ function assetAddressValue(record, address) {
|
|
|
2862
3027
|
}
|
|
2863
3028
|
function position(value, asset, baseAssetDecimals) {
|
|
2864
3029
|
const option = value?.yieldOption;
|
|
2865
|
-
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0,
|
|
3030
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem6.isAddress)(option.address)) {
|
|
2866
3031
|
return invalid("yield positions", "missing vault metadata");
|
|
2867
3032
|
}
|
|
2868
3033
|
return {
|
|
@@ -2875,13 +3040,14 @@ function position(value, asset, baseAssetDecimals) {
|
|
|
2875
3040
|
// differ from the underlying asset. Yieldseeker already converts it to
|
|
2876
3041
|
// underlying base-asset units in `assetsBase`; pair that value with the
|
|
2877
3042
|
// snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
|
|
2878
|
-
// share quantity
|
|
3043
|
+
// share quantity separate from the withdrawable underlying asset amount.
|
|
2879
3044
|
amount: decimal(
|
|
2880
3045
|
value.assetsBase,
|
|
2881
3046
|
baseAssetDecimals,
|
|
2882
3047
|
"yield positions"
|
|
2883
3048
|
),
|
|
2884
3049
|
amountRaw: String(value.assetsRaw),
|
|
3050
|
+
withdrawableAmountRaw: String(value.withdrawableAssetsRaw),
|
|
2885
3051
|
apy: percent(option.riskAdjustedApy),
|
|
2886
3052
|
tvl: Number(option.totalDepositsUsd),
|
|
2887
3053
|
liquidity: Number(option.withdrawableDepositsUsd)
|
|
@@ -2943,7 +3109,7 @@ function mapYieldseekerEarnings(contexts) {
|
|
|
2943
3109
|
chain: "BASE",
|
|
2944
3110
|
chainId: 8453,
|
|
2945
3111
|
asset: context.asset,
|
|
2946
|
-
amount: (0,
|
|
3112
|
+
amount: (0, import_viem6.formatUnits)(amount, context.snapshot.baseAssetDecimals)
|
|
2947
3113
|
});
|
|
2948
3114
|
lifetimeEarnings += usd(
|
|
2949
3115
|
amount,
|
|
@@ -3206,10 +3372,18 @@ function mapYieldseekerAgentApy(options, days) {
|
|
|
3206
3372
|
|
|
3207
3373
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
3208
3374
|
var OWNEY_AGENT_NAME = "owney";
|
|
3375
|
+
var OWNEY_AGENT_RULE_PRESET = "explorative";
|
|
3209
3376
|
var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
3210
3377
|
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
3211
3378
|
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
3212
3379
|
var YIELDSEEKER_YIELD_OPTIONS_CACHE_MS = 6e4;
|
|
3380
|
+
var YIELDSEEKER_AGENT_LIST_CACHE_MS = 12e4;
|
|
3381
|
+
var YIELDSEEKER_PORTFOLIO_CACHE_MS = 12e4;
|
|
3382
|
+
var YIELDSEEKER_SETTLEMENT_CACHE_MS = 5e3;
|
|
3383
|
+
var YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS = 15e3;
|
|
3384
|
+
var YIELDSEEKER_READ_FAILURE_COOLDOWN_MS = 6e4;
|
|
3385
|
+
var YIELDSEEKER_SETTLEMENT_WINDOW_MS = 5 * 6e4;
|
|
3386
|
+
var YIELDSEEKER_ACTIVITY_CACHE_MS = 6e4;
|
|
3213
3387
|
function generateYieldseekerUsername() {
|
|
3214
3388
|
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
3215
3389
|
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
@@ -3253,6 +3427,7 @@ function query(params) {
|
|
|
3253
3427
|
var YieldseekerAgent = class {
|
|
3254
3428
|
id = "yieldseeker";
|
|
3255
3429
|
balanceComposition = "tokens-plus-positions";
|
|
3430
|
+
withdrawalRequiresWalletApproval = true;
|
|
3256
3431
|
supportedChainIds = [8453];
|
|
3257
3432
|
supportedAssets = [
|
|
3258
3433
|
{
|
|
@@ -3266,11 +3441,26 @@ var YieldseekerAgent = class {
|
|
|
3266
3441
|
];
|
|
3267
3442
|
api;
|
|
3268
3443
|
auth;
|
|
3444
|
+
rpcUrls;
|
|
3445
|
+
receiptClient;
|
|
3269
3446
|
transactionExecutor;
|
|
3270
3447
|
unwindReceiptWaiter;
|
|
3271
3448
|
agentContexts = /* @__PURE__ */ new Map();
|
|
3272
3449
|
users = /* @__PURE__ */ new Map();
|
|
3273
3450
|
pendingAgents = /* @__PURE__ */ new Map();
|
|
3451
|
+
pendingWalletContexts = /* @__PURE__ */ new Map();
|
|
3452
|
+
readCache = /* @__PURE__ */ new Map();
|
|
3453
|
+
pendingReads = /* @__PURE__ */ new Map();
|
|
3454
|
+
readGeneration = 0;
|
|
3455
|
+
portfolioVersions = /* @__PURE__ */ new Map();
|
|
3456
|
+
snapshotFailures = /* @__PURE__ */ new Map();
|
|
3457
|
+
standardReadFailures = /* @__PURE__ */ new Map();
|
|
3458
|
+
reconcileUntil = /* @__PURE__ */ new Map();
|
|
3459
|
+
activityRefreshUntil = /* @__PURE__ */ new Map();
|
|
3460
|
+
// null means a confirmed movement has no reliable baseline. Do not guess
|
|
3461
|
+
// settlement; keep slow reconciliation after the fast window expires.
|
|
3462
|
+
snapshotMovements = /* @__PURE__ */ new Map();
|
|
3463
|
+
movementEpochs = /* @__PURE__ */ new Map();
|
|
3274
3464
|
yieldOptions = /* @__PURE__ */ new Map();
|
|
3275
3465
|
pendingYieldOptions = /* @__PURE__ */ new Map();
|
|
3276
3466
|
constructor(owneyApiKey, options = {}) {
|
|
@@ -3280,10 +3470,16 @@ var YieldseekerAgent = class {
|
|
|
3280
3470
|
options.fetchFn
|
|
3281
3471
|
);
|
|
3282
3472
|
this.auth = new YieldseekerAuth(options.auth);
|
|
3473
|
+
this.rpcUrls = options.rpcUrls;
|
|
3283
3474
|
this.transactionExecutor = options.transactionExecutor;
|
|
3284
3475
|
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
3285
3476
|
}
|
|
3477
|
+
getReceiptClient() {
|
|
3478
|
+
this.receiptClient ??= createPaidRpcClient(import_chains3.base, this.rpcUrls);
|
|
3479
|
+
return this.receiptClient;
|
|
3480
|
+
}
|
|
3286
3481
|
async disconnect() {
|
|
3482
|
+
this.readGeneration += 1;
|
|
3287
3483
|
this.auth.clear();
|
|
3288
3484
|
for (const key2 of this.users.keys()) {
|
|
3289
3485
|
const [walletAddress, chainId] = key2.split(":");
|
|
@@ -3292,6 +3488,16 @@ var YieldseekerAgent = class {
|
|
|
3292
3488
|
this.users.clear();
|
|
3293
3489
|
this.agentContexts.clear();
|
|
3294
3490
|
this.pendingAgents.clear();
|
|
3491
|
+
this.pendingWalletContexts.clear();
|
|
3492
|
+
this.readCache.clear();
|
|
3493
|
+
this.pendingReads.clear();
|
|
3494
|
+
this.portfolioVersions.clear();
|
|
3495
|
+
this.snapshotFailures.clear();
|
|
3496
|
+
this.standardReadFailures.clear();
|
|
3497
|
+
this.reconcileUntil.clear();
|
|
3498
|
+
this.activityRefreshUntil.clear();
|
|
3499
|
+
this.snapshotMovements.clear();
|
|
3500
|
+
this.movementEpochs.clear();
|
|
3295
3501
|
}
|
|
3296
3502
|
async activateAgent(state, chainId, asset) {
|
|
3297
3503
|
this.assertChain(chainId);
|
|
@@ -3311,41 +3517,37 @@ var YieldseekerAgent = class {
|
|
|
3311
3517
|
);
|
|
3312
3518
|
}
|
|
3313
3519
|
const context = await this.ensureAgent(state, chainId, asset);
|
|
3520
|
+
const generation = this.readGeneration;
|
|
3521
|
+
const movement = this.snapshotMovement(state, chainId, context, BigInt(amount));
|
|
3314
3522
|
let txHash;
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
context.wallet.walletAddress,
|
|
3325
|
-
chainId,
|
|
3326
|
-
amount
|
|
3327
|
-
);
|
|
3328
|
-
await this.waitForReceipt(state, chainId, txHash);
|
|
3329
|
-
} else {
|
|
3330
|
-
txHash = await this.submitTransaction(state, chainId, {
|
|
3331
|
-
from: (0, import_viem6.getAddress)(state.walletAddress),
|
|
3332
|
-
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3333
|
-
data: (0, import_viem6.encodeFunctionData)({
|
|
3334
|
-
abi: import_viem6.erc20Abi,
|
|
3335
|
-
functionName: "transfer",
|
|
3336
|
-
args: [(0, import_viem6.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
3337
|
-
}),
|
|
3338
|
-
value: "0",
|
|
3339
|
-
chainId
|
|
3340
|
-
});
|
|
3341
|
-
}
|
|
3342
|
-
} finally {
|
|
3343
|
-
await this.refreshSnapshotAfterMovement(
|
|
3344
|
-
state,
|
|
3523
|
+
if (depositCallback) {
|
|
3524
|
+
provideDepositVerificationContext(depositCallback, {
|
|
3525
|
+
agentId: "yieldseeker",
|
|
3526
|
+
signature: await this.auth.getToken(state, chainId),
|
|
3527
|
+
userId: context.user.userId,
|
|
3528
|
+
yieldseekerAgentId: context.agent.agentId
|
|
3529
|
+
});
|
|
3530
|
+
txHash = await depositCallback(
|
|
3531
|
+
context.wallet.walletAddress,
|
|
3345
3532
|
chainId,
|
|
3346
|
-
|
|
3347
|
-
"deposit"
|
|
3533
|
+
amount
|
|
3348
3534
|
);
|
|
3535
|
+
await this.waitForReceipt(state, chainId, txHash);
|
|
3536
|
+
} else {
|
|
3537
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
3538
|
+
from: (0, import_viem7.getAddress)(state.walletAddress),
|
|
3539
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3540
|
+
data: (0, import_viem7.encodeFunctionData)({
|
|
3541
|
+
abi: import_viem7.erc20Abi,
|
|
3542
|
+
functionName: "transfer",
|
|
3543
|
+
args: [(0, import_viem7.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
3544
|
+
}),
|
|
3545
|
+
value: "0",
|
|
3546
|
+
chainId
|
|
3547
|
+
});
|
|
3548
|
+
}
|
|
3549
|
+
if (this.readGeneration === generation) {
|
|
3550
|
+
void this.refreshSnapshotAfterMovement(state, chainId, context, "deposit", movement);
|
|
3349
3551
|
}
|
|
3350
3552
|
return {
|
|
3351
3553
|
txHash,
|
|
@@ -3373,6 +3575,9 @@ var YieldseekerAgent = class {
|
|
|
3373
3575
|
this.id
|
|
3374
3576
|
);
|
|
3375
3577
|
}
|
|
3578
|
+
const generation = this.readGeneration;
|
|
3579
|
+
let confirmedMovement = false;
|
|
3580
|
+
let settlement = null;
|
|
3376
3581
|
try {
|
|
3377
3582
|
const portfolio = await this.loadPortfolioContext(
|
|
3378
3583
|
state,
|
|
@@ -3390,6 +3595,7 @@ var YieldseekerAgent = class {
|
|
|
3390
3595
|
);
|
|
3391
3596
|
const totalAvailable = idle + deployed;
|
|
3392
3597
|
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3598
|
+
const plannedMovement = this.snapshotMovement(state, chainId, context, -requested);
|
|
3393
3599
|
if (requested > totalAvailable) {
|
|
3394
3600
|
throw new OwneyError(
|
|
3395
3601
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
@@ -3425,6 +3631,7 @@ var YieldseekerAgent = class {
|
|
|
3425
3631
|
throw this.invalidResponse("position withdrawal");
|
|
3426
3632
|
}
|
|
3427
3633
|
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3634
|
+
confirmedMovement = true;
|
|
3428
3635
|
remaining -= assetsRaw;
|
|
3429
3636
|
}
|
|
3430
3637
|
if (remaining > 0n) {
|
|
@@ -3433,15 +3640,15 @@ var YieldseekerAgent = class {
|
|
|
3433
3640
|
remaining: remaining.toString()
|
|
3434
3641
|
});
|
|
3435
3642
|
}
|
|
3436
|
-
const account = (0,
|
|
3643
|
+
const account = (0, import_viem7.getAddress)(state.walletAddress);
|
|
3437
3644
|
const txHash = await this.submitTransaction(state, chainId, {
|
|
3438
3645
|
from: account,
|
|
3439
|
-
to: (0,
|
|
3440
|
-
data: amount === void 0 ? (0,
|
|
3646
|
+
to: (0, import_viem7.getAddress)(context.wallet.walletAddress),
|
|
3647
|
+
data: amount === void 0 ? (0, import_viem7.encodeFunctionData)({
|
|
3441
3648
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3442
3649
|
functionName: "withdrawAllAssetToUser",
|
|
3443
3650
|
args: [account, metadata.address]
|
|
3444
|
-
}) : (0,
|
|
3651
|
+
}) : (0, import_viem7.encodeFunctionData)({
|
|
3445
3652
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3446
3653
|
functionName: "withdrawAssetToUser",
|
|
3447
3654
|
args: [account, metadata.address, requested]
|
|
@@ -3449,18 +3656,23 @@ var YieldseekerAgent = class {
|
|
|
3449
3656
|
value: "0",
|
|
3450
3657
|
chainId
|
|
3451
3658
|
});
|
|
3659
|
+
confirmedMovement = true;
|
|
3660
|
+
settlement = plannedMovement;
|
|
3452
3661
|
return {
|
|
3453
3662
|
txHash,
|
|
3454
3663
|
type: amount === void 0 ? "full" : "partial",
|
|
3455
3664
|
amount: requested.toString()
|
|
3456
3665
|
};
|
|
3457
3666
|
} finally {
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3667
|
+
if (confirmedMovement && this.readGeneration === generation) {
|
|
3668
|
+
void this.refreshSnapshotAfterMovement(
|
|
3669
|
+
state,
|
|
3670
|
+
chainId,
|
|
3671
|
+
context,
|
|
3672
|
+
"withdrawal",
|
|
3673
|
+
settlement
|
|
3674
|
+
);
|
|
3675
|
+
}
|
|
3464
3676
|
}
|
|
3465
3677
|
}
|
|
3466
3678
|
async getBalances(state, chainId) {
|
|
@@ -3485,7 +3697,7 @@ var YieldseekerAgent = class {
|
|
|
3485
3697
|
this.assertChain(chainId);
|
|
3486
3698
|
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3487
3699
|
if (asset !== void 0) this.assertAsset(asset);
|
|
3488
|
-
const contexts = await this.
|
|
3700
|
+
const contexts = await this.loadActivityPortfolio(state, chainId, {
|
|
3489
3701
|
...asset ? { asset } : {},
|
|
3490
3702
|
historic: true,
|
|
3491
3703
|
actions: true
|
|
@@ -3497,7 +3709,7 @@ var YieldseekerAgent = class {
|
|
|
3497
3709
|
);
|
|
3498
3710
|
const vaultAddresses = new Set(
|
|
3499
3711
|
catalog.flat().filter(
|
|
3500
|
-
(yieldOption) => yieldOption.chainId === chainId && (0,
|
|
3712
|
+
(yieldOption) => yieldOption.chainId === chainId && (0, import_viem7.isAddress)(yieldOption.address)
|
|
3501
3713
|
).map((yieldOption) => yieldOption.address.toLowerCase())
|
|
3502
3714
|
);
|
|
3503
3715
|
return mapYieldseekerHistory(contexts, {
|
|
@@ -3512,7 +3724,7 @@ var YieldseekerAgent = class {
|
|
|
3512
3724
|
this.assertChain(chainId);
|
|
3513
3725
|
return mapYieldseekerProfile(
|
|
3514
3726
|
state.walletAddress,
|
|
3515
|
-
await this.
|
|
3727
|
+
await this.loadActivityPortfolio(state, chainId, {})
|
|
3516
3728
|
);
|
|
3517
3729
|
}
|
|
3518
3730
|
async getAgentApy(days, options) {
|
|
@@ -3554,6 +3766,95 @@ var YieldseekerAgent = class {
|
|
|
3554
3766
|
contextKey(state, chainId, asset) {
|
|
3555
3767
|
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3556
3768
|
}
|
|
3769
|
+
assertSession(generation, chainId, asset) {
|
|
3770
|
+
if (this.readGeneration !== generation) {
|
|
3771
|
+
throw new OwneyError(
|
|
3772
|
+
"NOT_CONNECTED",
|
|
3773
|
+
"Wallet session changed during the Yieldseeker request.",
|
|
3774
|
+
{ rpcSource: "agent-api", chainId, ...asset ? { asset } : {} },
|
|
3775
|
+
this.id
|
|
3776
|
+
);
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
cachedRead(key2, ttlMs, read2) {
|
|
3780
|
+
const cached = this.readCache.get(key2);
|
|
3781
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
3782
|
+
return Promise.resolve(cached.value);
|
|
3783
|
+
}
|
|
3784
|
+
const pending = this.pendingReads.get(key2);
|
|
3785
|
+
if (pending) return pending;
|
|
3786
|
+
const generation = this.readGeneration;
|
|
3787
|
+
const request = Promise.resolve().then(read2).then((value) => {
|
|
3788
|
+
if (this.readGeneration === generation && this.pendingReads.get(key2) === request) {
|
|
3789
|
+
this.readCache.set(key2, {
|
|
3790
|
+
expiresAt: Date.now() + ttlMs,
|
|
3791
|
+
value
|
|
3792
|
+
});
|
|
3793
|
+
}
|
|
3794
|
+
return value;
|
|
3795
|
+
}).finally(() => {
|
|
3796
|
+
if (this.pendingReads.get(key2) === request) {
|
|
3797
|
+
this.pendingReads.delete(key2);
|
|
3798
|
+
}
|
|
3799
|
+
});
|
|
3800
|
+
this.pendingReads.set(key2, request);
|
|
3801
|
+
return request;
|
|
3802
|
+
}
|
|
3803
|
+
agentListKey(state, chainId) {
|
|
3804
|
+
return `agents:${this.userKey(state, chainId)}`;
|
|
3805
|
+
}
|
|
3806
|
+
async listAgents(state, chainId, user) {
|
|
3807
|
+
const response = await this.cachedRead(
|
|
3808
|
+
this.agentListKey(state, chainId),
|
|
3809
|
+
YIELDSEEKER_AGENT_LIST_CACHE_MS,
|
|
3810
|
+
async () => {
|
|
3811
|
+
const response2 = await this.walletRequest(
|
|
3812
|
+
state,
|
|
3813
|
+
chainId,
|
|
3814
|
+
`/users/${user.userId}/agents`
|
|
3815
|
+
);
|
|
3816
|
+
if (!Array.isArray(response2?.agents)) {
|
|
3817
|
+
throw this.invalidResponse("agent list");
|
|
3818
|
+
}
|
|
3819
|
+
return response2;
|
|
3820
|
+
}
|
|
3821
|
+
);
|
|
3822
|
+
return response.agents;
|
|
3823
|
+
}
|
|
3824
|
+
async contextForAgent(state, chainId, user, agent, asset) {
|
|
3825
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3826
|
+
const cached = this.agentContexts.get(key2);
|
|
3827
|
+
if (cached?.agent.agentId === agent.agentId) return cached;
|
|
3828
|
+
const pending = this.pendingWalletContexts.get(key2);
|
|
3829
|
+
if (pending) return pending;
|
|
3830
|
+
const generation = this.readGeneration;
|
|
3831
|
+
const request = this.walletRequest(
|
|
3832
|
+
state,
|
|
3833
|
+
chainId,
|
|
3834
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3835
|
+
).then((walletResponse) => {
|
|
3836
|
+
this.assertSession(generation, chainId, asset);
|
|
3837
|
+
if (!walletResponse?.agentWallet || !(0, import_viem7.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3838
|
+
throw this.invalidResponse("agent wallet");
|
|
3839
|
+
}
|
|
3840
|
+
const context = {
|
|
3841
|
+
user,
|
|
3842
|
+
agent,
|
|
3843
|
+
wallet: walletResponse.agentWallet,
|
|
3844
|
+
asset
|
|
3845
|
+
};
|
|
3846
|
+
this.agentContexts.set(key2, context);
|
|
3847
|
+
return context;
|
|
3848
|
+
});
|
|
3849
|
+
this.pendingWalletContexts.set(key2, request);
|
|
3850
|
+
try {
|
|
3851
|
+
return await request;
|
|
3852
|
+
} finally {
|
|
3853
|
+
if (this.pendingWalletContexts.get(key2) === request) {
|
|
3854
|
+
this.pendingWalletContexts.delete(key2);
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3557
3858
|
async resolveUser(state, chainId) {
|
|
3558
3859
|
const key2 = this.userKey(state, chainId);
|
|
3559
3860
|
const inMemory = this.users.get(key2);
|
|
@@ -3563,7 +3864,8 @@ var YieldseekerAgent = class {
|
|
|
3563
3864
|
this.users.set(key2, persisted);
|
|
3564
3865
|
return persisted;
|
|
3565
3866
|
}
|
|
3566
|
-
const walletAddress = (0,
|
|
3867
|
+
const walletAddress = (0, import_viem7.getAddress)(state.walletAddress);
|
|
3868
|
+
const generation = this.readGeneration;
|
|
3567
3869
|
let user = null;
|
|
3568
3870
|
try {
|
|
3569
3871
|
const login = await this.providerRequest(
|
|
@@ -3610,6 +3912,7 @@ var YieldseekerAgent = class {
|
|
|
3610
3912
|
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3611
3913
|
throw this.invalidResponse("wallet identity");
|
|
3612
3914
|
}
|
|
3915
|
+
this.assertSession(generation, chainId);
|
|
3613
3916
|
const resolved = { userId: user.userId };
|
|
3614
3917
|
this.users.set(key2, resolved);
|
|
3615
3918
|
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
@@ -3625,10 +3928,13 @@ var YieldseekerAgent = class {
|
|
|
3625
3928
|
if (cached) return cached;
|
|
3626
3929
|
const pending = this.pendingAgents.get(key2);
|
|
3627
3930
|
if (pending) return pending;
|
|
3931
|
+
const generation = this.readGeneration;
|
|
3628
3932
|
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3629
3933
|
async (context) => {
|
|
3934
|
+
this.assertSession(generation, chainId, asset);
|
|
3630
3935
|
if (!context) throw this.invalidResponse("agent creation");
|
|
3631
3936
|
await this.deployAgent(state, chainId, context);
|
|
3937
|
+
this.assertSession(generation, chainId, asset);
|
|
3632
3938
|
this.agentContexts.set(key2, context);
|
|
3633
3939
|
return context;
|
|
3634
3940
|
}
|
|
@@ -3637,29 +3943,27 @@ var YieldseekerAgent = class {
|
|
|
3637
3943
|
try {
|
|
3638
3944
|
return await request;
|
|
3639
3945
|
} finally {
|
|
3640
|
-
this.pendingAgents.delete(key2);
|
|
3946
|
+
if (this.pendingAgents.get(key2) === request) this.pendingAgents.delete(key2);
|
|
3641
3947
|
}
|
|
3642
3948
|
}
|
|
3643
3949
|
async findAgent(state, chainId, asset) {
|
|
3644
3950
|
const key2 = this.contextKey(state, chainId, asset);
|
|
3645
3951
|
const cached = this.agentContexts.get(key2);
|
|
3646
3952
|
if (cached) return cached;
|
|
3953
|
+
const generation = this.readGeneration;
|
|
3647
3954
|
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3955
|
+
this.assertSession(generation, chainId, asset);
|
|
3648
3956
|
if (context) this.agentContexts.set(key2, context);
|
|
3649
3957
|
return context;
|
|
3650
3958
|
}
|
|
3651
3959
|
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3960
|
+
const generation = this.readGeneration;
|
|
3652
3961
|
const user = await this.resolveUser(state, chainId);
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
`/users/${user.userId}/agents`
|
|
3657
|
-
);
|
|
3658
|
-
if (!Array.isArray(response?.agents)) {
|
|
3659
|
-
throw this.invalidResponse("agent list");
|
|
3660
|
-
}
|
|
3962
|
+
this.assertSession(generation, chainId, asset);
|
|
3963
|
+
const agents = await this.listAgents(state, chainId, user);
|
|
3964
|
+
this.assertSession(generation, chainId, asset);
|
|
3661
3965
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3662
|
-
let agent =
|
|
3966
|
+
let agent = agents.find(
|
|
3663
3967
|
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3664
3968
|
);
|
|
3665
3969
|
if (!agent && createIfMissing) {
|
|
@@ -3675,99 +3979,254 @@ var YieldseekerAgent = class {
|
|
|
3675
3979
|
chainId,
|
|
3676
3980
|
assetAddress: metadata.address,
|
|
3677
3981
|
type: "vault",
|
|
3678
|
-
rulePreset:
|
|
3982
|
+
rulePreset: OWNEY_AGENT_RULE_PRESET
|
|
3679
3983
|
}
|
|
3680
3984
|
}
|
|
3681
3985
|
);
|
|
3986
|
+
this.assertSession(generation, chainId, asset);
|
|
3682
3987
|
agent = created?.agent;
|
|
3988
|
+
if (agent) {
|
|
3989
|
+
this.readCache.set(this.agentListKey(state, chainId), {
|
|
3990
|
+
expiresAt: Date.now() + YIELDSEEKER_AGENT_LIST_CACHE_MS,
|
|
3991
|
+
value: {
|
|
3992
|
+
agents: [...agents, agent]
|
|
3993
|
+
}
|
|
3994
|
+
});
|
|
3995
|
+
}
|
|
3683
3996
|
}
|
|
3684
3997
|
if (!agent) return null;
|
|
3685
3998
|
this.assertAgent(agent);
|
|
3686
|
-
|
|
3687
|
-
state,
|
|
3688
|
-
chainId,
|
|
3689
|
-
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3690
|
-
);
|
|
3691
|
-
if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3692
|
-
throw this.invalidResponse("agent wallet");
|
|
3693
|
-
}
|
|
3694
|
-
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3999
|
+
return this.contextForAgent(state, chainId, user, agent, asset);
|
|
3695
4000
|
}
|
|
3696
4001
|
async loadPortfolio(state, chainId, options) {
|
|
3697
|
-
const
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
4002
|
+
const contexts = await this.resolvePortfolioContexts(state, chainId, options.asset);
|
|
4003
|
+
return Promise.all(
|
|
4004
|
+
contexts.map(
|
|
4005
|
+
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
4006
|
+
)
|
|
3702
4007
|
);
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
4008
|
+
}
|
|
4009
|
+
async loadActivityPortfolio(state, chainId, options) {
|
|
4010
|
+
const contexts = await this.resolvePortfolioContexts(state, chainId, options.asset);
|
|
4011
|
+
return Promise.all(
|
|
4012
|
+
contexts.map(
|
|
4013
|
+
(context) => this.loadActivityContext(state, chainId, context, options)
|
|
4014
|
+
)
|
|
4015
|
+
);
|
|
4016
|
+
}
|
|
4017
|
+
async resolvePortfolioContexts(state, chainId, assetFilter) {
|
|
4018
|
+
const generation = this.readGeneration;
|
|
4019
|
+
const user = await this.resolveUser(state, chainId);
|
|
4020
|
+
this.assertSession(generation, chainId);
|
|
4021
|
+
const agents = await this.listAgents(state, chainId, user);
|
|
4022
|
+
this.assertSession(generation, chainId);
|
|
3706
4023
|
const contexts = [];
|
|
3707
|
-
for (const agent of
|
|
4024
|
+
for (const agent of agents) {
|
|
3708
4025
|
const asset = this.assetForAgent(agent);
|
|
3709
|
-
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" ||
|
|
4026
|
+
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || assetFilter && assetFilter !== asset) {
|
|
3710
4027
|
continue;
|
|
3711
4028
|
}
|
|
3712
4029
|
this.assertAgent(agent);
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
4030
|
+
contexts.push(this.contextForAgent(state, chainId, user, agent, asset));
|
|
4031
|
+
}
|
|
4032
|
+
const resolvedContexts = await Promise.all(contexts);
|
|
4033
|
+
this.assertSession(generation, chainId);
|
|
4034
|
+
return resolvedContexts;
|
|
4035
|
+
}
|
|
4036
|
+
portfolioVersion(contextKey) {
|
|
4037
|
+
return this.portfolioVersions.get(contextKey) ?? 0;
|
|
4038
|
+
}
|
|
4039
|
+
portfolioCacheMs(contextKey) {
|
|
4040
|
+
return (this.reconcileUntil.get(contextKey) ?? 0) > Date.now() ? YIELDSEEKER_SETTLEMENT_CACHE_MS : YIELDSEEKER_PORTFOLIO_CACHE_MS;
|
|
4041
|
+
}
|
|
4042
|
+
snapshotMovement(state, chainId, context, delta) {
|
|
4043
|
+
const key2 = this.contextKey(state, chainId, context.asset);
|
|
4044
|
+
const previous = this.snapshotMovements.get(key2);
|
|
4045
|
+
if (this.snapshotMovements.has(key2)) {
|
|
4046
|
+
if (!previous || previous.delta > 0n !== delta > 0n) return null;
|
|
4047
|
+
return {
|
|
4048
|
+
...previous,
|
|
4049
|
+
epoch: this.movementEpochs.get(key2) ?? 0,
|
|
4050
|
+
delta: previous.delta + delta,
|
|
4051
|
+
expectedBalance: previous.expectedBalance + delta,
|
|
4052
|
+
expectedNetDeposits: previous.expectedNetDeposits + delta
|
|
3726
4053
|
};
|
|
3727
|
-
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3728
|
-
contexts.push(context);
|
|
3729
4054
|
}
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
);
|
|
4055
|
+
const cached = this.readCache.get(`snapshot:${key2}`);
|
|
4056
|
+
if (!cached || cached.expiresAt <= Date.now()) return null;
|
|
4057
|
+
const snapshot = cached.value.agentSnapshot;
|
|
4058
|
+
if (snapshot.baseAssetDecimals !== YIELDSEEKER_ASSET_METADATA[context.asset].decimals) return null;
|
|
4059
|
+
if (!/^-?\d+$/.test(snapshot.netDepositsBase) || !/^\d+$/.test(snapshot.totalValueBase)) return null;
|
|
4060
|
+
return {
|
|
4061
|
+
epoch: this.movementEpochs.get(key2) ?? 0,
|
|
4062
|
+
delta,
|
|
4063
|
+
decimals: snapshot.baseAssetDecimals,
|
|
4064
|
+
expectedBalance: BigInt(snapshot.totalValueBase) + delta,
|
|
4065
|
+
expectedNetDeposits: BigInt(snapshot.netDepositsBase) + delta
|
|
4066
|
+
};
|
|
4067
|
+
}
|
|
4068
|
+
advancePortfolioVersion(contextKey) {
|
|
4069
|
+
const version = this.portfolioVersion(contextKey) + 1;
|
|
4070
|
+
this.portfolioVersions.set(contextKey, version);
|
|
4071
|
+
this.snapshotFailures.delete(contextKey);
|
|
4072
|
+
for (const kind of ["snapshot", "positions", "historic", "actions"]) {
|
|
4073
|
+
const key2 = `${kind}:${contextKey}`;
|
|
4074
|
+
this.readCache.delete(key2);
|
|
4075
|
+
this.pendingReads.delete(key2);
|
|
4076
|
+
}
|
|
4077
|
+
return version;
|
|
4078
|
+
}
|
|
4079
|
+
requestPortfolioSnapshot(state, chainId, context, contextKey, version, generation, forceRefresh = false) {
|
|
4080
|
+
return this.walletRequest(
|
|
4081
|
+
state,
|
|
4082
|
+
chainId,
|
|
4083
|
+
`${this.agentPath(context, "snapshot")}${query(forceRefresh ? { shouldForceRefresh: true } : { shouldOnlyUseRecentValue: true, shouldAllowStaleOnError: true })}`
|
|
4084
|
+
).then((response) => {
|
|
4085
|
+
if (!response?.agentSnapshot) {
|
|
4086
|
+
throw this.invalidResponse("agent snapshot");
|
|
4087
|
+
}
|
|
4088
|
+
if (this.readGeneration === generation && this.portfolioVersion(contextKey) === version) {
|
|
4089
|
+
this.snapshotFailures.delete(contextKey);
|
|
4090
|
+
const movement = this.snapshotMovements.get(contextKey);
|
|
4091
|
+
const snapshot = response.agentSnapshot;
|
|
4092
|
+
if (movement && snapshot.baseAssetDecimals === movement.decimals && /^-?\d+$/.test(snapshot.netDepositsBase) && /^\d+$/.test(snapshot.totalValueBase)) {
|
|
4093
|
+
const balance = BigInt(snapshot.totalValueBase);
|
|
4094
|
+
const netDeposits = BigInt(snapshot.netDepositsBase);
|
|
4095
|
+
const reconciled = movement.delta > 0n ? balance >= movement.expectedBalance && netDeposits >= movement.expectedNetDeposits : balance <= movement.expectedBalance && netDeposits <= movement.expectedNetDeposits;
|
|
4096
|
+
if (reconciled) {
|
|
4097
|
+
this.snapshotMovements.delete(contextKey);
|
|
4098
|
+
this.reconcileUntil.delete(contextKey);
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
return response;
|
|
4103
|
+
}).catch((error) => {
|
|
4104
|
+
if (this.readGeneration === generation && this.portfolioVersion(contextKey) === version) {
|
|
4105
|
+
this.snapshotFailures.set(contextKey, {
|
|
4106
|
+
retryAt: Date.now() + Math.max(YIELDSEEKER_READ_FAILURE_COOLDOWN_MS, rateLimitDelay(error) ?? 0),
|
|
4107
|
+
error
|
|
4108
|
+
});
|
|
4109
|
+
}
|
|
4110
|
+
throw error;
|
|
4111
|
+
});
|
|
4112
|
+
}
|
|
4113
|
+
portfolioSnapshot(state, chainId, context, contextKey) {
|
|
4114
|
+
const key2 = `snapshot:${contextKey}`;
|
|
4115
|
+
const cached = this.readCache.get(key2);
|
|
4116
|
+
if ((!cached || cached.expiresAt <= Date.now()) && !this.pendingReads.has(key2)) {
|
|
4117
|
+
const failure = this.snapshotFailures.get(contextKey);
|
|
4118
|
+
if (failure && failure.retryAt > Date.now()) throw failure.error;
|
|
4119
|
+
this.advancePortfolioVersion(contextKey);
|
|
4120
|
+
}
|
|
4121
|
+
const version = this.portfolioVersion(contextKey);
|
|
4122
|
+
const generation = this.readGeneration;
|
|
4123
|
+
return {
|
|
4124
|
+
version,
|
|
4125
|
+
read: this.cachedRead(key2, this.portfolioCacheMs(contextKey), () => (
|
|
4126
|
+
// Force only during the bounded post-transaction window. Keep unknown
|
|
4127
|
+
// or unreconciled movements for verification, but do not let them turn
|
|
4128
|
+
// routine polling into forced refreshes indefinitely.
|
|
4129
|
+
this.requestPortfolioSnapshot(
|
|
4130
|
+
state,
|
|
4131
|
+
chainId,
|
|
4132
|
+
context,
|
|
4133
|
+
contextKey,
|
|
4134
|
+
version,
|
|
4135
|
+
generation,
|
|
4136
|
+
this.snapshotMovements.has(contextKey) && (this.reconcileUntil.get(contextKey) ?? 0) > Date.now()
|
|
4137
|
+
)
|
|
4138
|
+
))
|
|
4139
|
+
};
|
|
3735
4140
|
}
|
|
3736
4141
|
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3737
|
-
const
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
4142
|
+
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
4143
|
+
const generation = this.readGeneration;
|
|
4144
|
+
const assertSession = () => this.assertSession(generation, chainId, context.asset);
|
|
4145
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
4146
|
+
const { version, read: read2 } = this.portfolioSnapshot(state, chainId, context, contextKey);
|
|
4147
|
+
const parts = this.loadPortfolioParts(state, chainId, context, options, contextKey);
|
|
4148
|
+
try {
|
|
4149
|
+
const [snapshot, portfolioParts] = await Promise.all([read2, parts]);
|
|
4150
|
+
assertSession();
|
|
4151
|
+
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4152
|
+
return {
|
|
4153
|
+
...context,
|
|
4154
|
+
snapshot: snapshot.agentSnapshot,
|
|
4155
|
+
...portfolioParts
|
|
4156
|
+
};
|
|
4157
|
+
} catch (error) {
|
|
4158
|
+
assertSession();
|
|
4159
|
+
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4160
|
+
throw error;
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
throw new OwneyError(
|
|
4164
|
+
"AGENT_API_ERROR",
|
|
4165
|
+
"Yieldseeker portfolio changed during the read. Please retry.",
|
|
4166
|
+
{ rpcSource: "agent-api", chainId, asset: context.asset },
|
|
4167
|
+
this.id
|
|
4168
|
+
);
|
|
4169
|
+
}
|
|
4170
|
+
async loadActivityContext(state, chainId, context, options) {
|
|
4171
|
+
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
4172
|
+
const generation = this.readGeneration;
|
|
4173
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
4174
|
+
const version = this.portfolioVersion(contextKey);
|
|
4175
|
+
try {
|
|
4176
|
+
const parts = await this.loadPortfolioParts(state, chainId, context, options, contextKey);
|
|
4177
|
+
this.assertSession(generation, chainId, context.asset);
|
|
4178
|
+
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4179
|
+
return { ...context, ...parts };
|
|
4180
|
+
} catch (error) {
|
|
4181
|
+
this.assertSession(generation, chainId, context.asset);
|
|
4182
|
+
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4183
|
+
throw error;
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
throw new OwneyError(
|
|
4187
|
+
"AGENT_API_ERROR",
|
|
4188
|
+
"Yieldseeker activity changed during the read. Please retry.",
|
|
4189
|
+
{ rpcSource: "agent-api", chainId, asset: context.asset },
|
|
4190
|
+
this.id
|
|
4191
|
+
);
|
|
4192
|
+
}
|
|
4193
|
+
async loadPortfolioParts(state, chainId, context, options, contextKey) {
|
|
4194
|
+
const [positions, historic, actions] = await Promise.all([
|
|
4195
|
+
this.cachedRead(
|
|
4196
|
+
`positions:${contextKey}`,
|
|
4197
|
+
this.portfolioCacheMs(contextKey),
|
|
4198
|
+
async () => {
|
|
4199
|
+
const response = await this.walletRequest(
|
|
4200
|
+
state,
|
|
4201
|
+
chainId,
|
|
4202
|
+
this.agentPath(context, "yield-positions")
|
|
4203
|
+
);
|
|
4204
|
+
if (!Array.isArray(response?.yieldPositions)) {
|
|
4205
|
+
throw this.invalidResponse("yield positions");
|
|
4206
|
+
}
|
|
4207
|
+
return response;
|
|
4208
|
+
}
|
|
3750
4209
|
),
|
|
3751
|
-
options.historic ? this.
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
this.
|
|
4210
|
+
options.historic ? this.cachedRead(
|
|
4211
|
+
`historic:${contextKey}`,
|
|
4212
|
+
(this.activityRefreshUntil.get(contextKey) ?? 0) > Date.now() ? YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS : YIELDSEEKER_ACTIVITY_CACHE_MS,
|
|
4213
|
+
() => this.walletRequest(
|
|
4214
|
+
state,
|
|
4215
|
+
chainId,
|
|
4216
|
+
this.agentPath(context, "wallet/historic-position")
|
|
4217
|
+
)
|
|
3755
4218
|
) : Promise.resolve(void 0),
|
|
3756
|
-
options.actions ? this.
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
this.
|
|
4219
|
+
options.actions ? this.cachedRead(
|
|
4220
|
+
`actions:${contextKey}`,
|
|
4221
|
+
(this.activityRefreshUntil.get(contextKey) ?? 0) > Date.now() ? YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS : YIELDSEEKER_ACTIVITY_CACHE_MS,
|
|
4222
|
+
() => this.walletRequest(
|
|
4223
|
+
state,
|
|
4224
|
+
chainId,
|
|
4225
|
+
this.agentPath(context, "actions")
|
|
4226
|
+
)
|
|
3760
4227
|
) : Promise.resolve(void 0)
|
|
3761
4228
|
]);
|
|
3762
|
-
if (!snapshot?.agentSnapshot) {
|
|
3763
|
-
throw this.invalidResponse("agent snapshot");
|
|
3764
|
-
}
|
|
3765
|
-
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3766
|
-
throw this.invalidResponse("yield positions");
|
|
3767
|
-
}
|
|
3768
4229
|
return {
|
|
3769
|
-
...context,
|
|
3770
|
-
snapshot: snapshot.agentSnapshot,
|
|
3771
4230
|
positions: positions.yieldPositions,
|
|
3772
4231
|
...historic?.position ? { historic: historic.position } : {},
|
|
3773
4232
|
...actions?.actions ? { actions: actions.actions } : {}
|
|
@@ -3782,25 +4241,28 @@ var YieldseekerAgent = class {
|
|
|
3782
4241
|
this.agentPath(context, "deploy"),
|
|
3783
4242
|
{ method: "POST", body: {} }
|
|
3784
4243
|
);
|
|
3785
|
-
if (!deployed?.agentWallet || !(0,
|
|
4244
|
+
if (!deployed?.agentWallet || !(0, import_viem7.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
|
|
3786
4245
|
throw this.invalidResponse("agent deployment", {
|
|
3787
4246
|
reason: "Deploy did not return the expected Agent Wallet."
|
|
3788
4247
|
});
|
|
3789
4248
|
}
|
|
3790
4249
|
context.wallet = deployed.agentWallet;
|
|
3791
4250
|
}
|
|
3792
|
-
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
4251
|
+
async refreshSnapshotAfterMovement(state, chainId, context, movement, settlement) {
|
|
4252
|
+
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
4253
|
+
this.reconcileUntil.set(contextKey, Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS);
|
|
4254
|
+
this.activityRefreshUntil.set(contextKey, Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS);
|
|
4255
|
+
const epoch = this.movementEpochs.get(contextKey) ?? 0;
|
|
4256
|
+
this.snapshotMovements.set(contextKey, settlement?.epoch === epoch ? settlement : null);
|
|
4257
|
+
this.movementEpochs.set(contextKey, epoch + 1);
|
|
4258
|
+
const version = this.advancePortfolioVersion(contextKey);
|
|
4259
|
+
const generation = this.readGeneration;
|
|
3793
4260
|
try {
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
shouldForceRefresh: true
|
|
3799
|
-
})}`
|
|
4261
|
+
await this.cachedRead(
|
|
4262
|
+
`snapshot:${contextKey}`,
|
|
4263
|
+
YIELDSEEKER_SETTLEMENT_CACHE_MS,
|
|
4264
|
+
() => this.requestPortfolioSnapshot(state, chainId, context, contextKey, version, generation, true)
|
|
3800
4265
|
);
|
|
3801
|
-
if (!response?.agentSnapshot) {
|
|
3802
|
-
throw this.invalidResponse("agent snapshot refresh");
|
|
3803
|
-
}
|
|
3804
4266
|
} catch (error) {
|
|
3805
4267
|
console.warn(
|
|
3806
4268
|
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
@@ -3812,10 +4274,26 @@ var YieldseekerAgent = class {
|
|
|
3812
4274
|
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
3813
4275
|
}
|
|
3814
4276
|
async walletRequest(state, chainId, path, options = {}) {
|
|
4277
|
+
const read2 = (options.method ?? "GET") === "GET";
|
|
4278
|
+
const userKey = this.userKey(state, chainId);
|
|
4279
|
+
if (read2) {
|
|
4280
|
+
const failure = this.standardReadFailures.get(userKey);
|
|
4281
|
+
if (failure && failure.retryAt > Date.now()) throw failure.error;
|
|
4282
|
+
if (failure) this.standardReadFailures.delete(userKey);
|
|
4283
|
+
}
|
|
4284
|
+
const generation = this.readGeneration;
|
|
3815
4285
|
try {
|
|
3816
4286
|
return await this.providerRequest(state, chainId, path, options);
|
|
3817
4287
|
} catch (error) {
|
|
3818
|
-
|
|
4288
|
+
const mapped = this.mapApiError(error);
|
|
4289
|
+
const delay = read2 ? rateLimitDelay(mapped) : void 0;
|
|
4290
|
+
if (delay !== void 0 && this.readGeneration === generation) {
|
|
4291
|
+
this.standardReadFailures.set(userKey, {
|
|
4292
|
+
retryAt: Date.now() + Math.max(YIELDSEEKER_READ_FAILURE_COOLDOWN_MS, delay),
|
|
4293
|
+
error: mapped
|
|
4294
|
+
});
|
|
4295
|
+
}
|
|
4296
|
+
throw mapped;
|
|
3819
4297
|
}
|
|
3820
4298
|
}
|
|
3821
4299
|
async providerRequest(state, chainId, path, options = {}) {
|
|
@@ -3856,6 +4334,7 @@ var YieldseekerAgent = class {
|
|
|
3856
4334
|
code,
|
|
3857
4335
|
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3858
4336
|
{
|
|
4337
|
+
rpcSource: "agent-api",
|
|
3859
4338
|
statusCode: error.status,
|
|
3860
4339
|
providerCode: error.providerCode,
|
|
3861
4340
|
...error.responseFields ? { fields: error.responseFields } : {}
|
|
@@ -3868,32 +4347,37 @@ var YieldseekerAgent = class {
|
|
|
3868
4347
|
return this.transactionExecutor(state, chainId, transaction);
|
|
3869
4348
|
}
|
|
3870
4349
|
this.assertTransaction(transaction, state, chainId);
|
|
3871
|
-
const account = (0,
|
|
3872
|
-
const walletClient = (0,
|
|
4350
|
+
const account = (0, import_viem7.getAddress)(state.walletAddress);
|
|
4351
|
+
const walletClient = (0, import_viem7.createWalletClient)({
|
|
3873
4352
|
account,
|
|
3874
4353
|
chain: import_chains3.base,
|
|
3875
|
-
transport: (0,
|
|
4354
|
+
transport: (0, import_viem7.custom)(state.provider)
|
|
3876
4355
|
});
|
|
3877
|
-
const
|
|
4356
|
+
const walletChainClient = (0, import_viem7.createPublicClient)({
|
|
3878
4357
|
chain: import_chains3.base,
|
|
3879
|
-
transport: (0,
|
|
4358
|
+
transport: (0, import_viem7.custom)(state.provider)
|
|
3880
4359
|
});
|
|
3881
4360
|
await ensureWalletOnChain(
|
|
3882
|
-
|
|
4361
|
+
walletChainClient,
|
|
3883
4362
|
walletClient,
|
|
3884
4363
|
8453
|
|
3885
4364
|
);
|
|
3886
4365
|
const hash = await walletClient.sendTransaction({
|
|
3887
4366
|
account,
|
|
3888
4367
|
chain: import_chains3.base,
|
|
3889
|
-
to: (0,
|
|
4368
|
+
to: (0, import_viem7.getAddress)(transaction.to),
|
|
3890
4369
|
data: transaction.data,
|
|
3891
4370
|
value: BigInt(transaction.value)
|
|
3892
4371
|
});
|
|
3893
|
-
const receipt = await
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
4372
|
+
const receipt = await withPaidRpcDiagnostics(
|
|
4373
|
+
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
4374
|
+
hash,
|
|
4375
|
+
confirmations: 1
|
|
4376
|
+
}),
|
|
4377
|
+
8453,
|
|
4378
|
+
"eth_getTransactionReceipt",
|
|
4379
|
+
this.id
|
|
4380
|
+
);
|
|
3897
4381
|
if (receipt.status !== "success") {
|
|
3898
4382
|
throw new OwneyError(
|
|
3899
4383
|
"AGENT_TRANSACTION_REVERTED",
|
|
@@ -3909,14 +4393,15 @@ var YieldseekerAgent = class {
|
|
|
3909
4393
|
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3910
4394
|
return;
|
|
3911
4395
|
}
|
|
3912
|
-
const
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
4396
|
+
const receipt = await withPaidRpcDiagnostics(
|
|
4397
|
+
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
4398
|
+
hash: transactionHash,
|
|
4399
|
+
confirmations: 1
|
|
4400
|
+
}),
|
|
4401
|
+
8453,
|
|
4402
|
+
"eth_getTransactionReceipt",
|
|
4403
|
+
this.id
|
|
4404
|
+
);
|
|
3920
4405
|
if (receipt.status !== "success") {
|
|
3921
4406
|
throw new OwneyError(
|
|
3922
4407
|
"AGENT_TRANSACTION_REVERTED",
|
|
@@ -3927,12 +4412,12 @@ var YieldseekerAgent = class {
|
|
|
3927
4412
|
}
|
|
3928
4413
|
}
|
|
3929
4414
|
assertTransaction(transaction, state, chainId) {
|
|
3930
|
-
if (!transaction || typeof transaction.from !== "string" || !(0,
|
|
4415
|
+
if (!transaction || typeof transaction.from !== "string" || !(0, import_viem7.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem7.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem7.getAddress)(transaction.from) !== (0, import_viem7.getAddress)(state.walletAddress)) {
|
|
3931
4416
|
throw this.invalidResponse("transaction");
|
|
3932
4417
|
}
|
|
3933
4418
|
}
|
|
3934
4419
|
assertAgent(agent) {
|
|
3935
|
-
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0,
|
|
4420
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem7.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
|
|
3936
4421
|
throw this.invalidResponse("agent");
|
|
3937
4422
|
}
|
|
3938
4423
|
}
|
|
@@ -4085,7 +4570,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
|
4085
4570
|
}
|
|
4086
4571
|
|
|
4087
4572
|
// src/lib/helpers/withdraw-helper.ts
|
|
4088
|
-
var
|
|
4573
|
+
var import_viem8 = require("viem");
|
|
4089
4574
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
4090
4575
|
const target = asset.toUpperCase();
|
|
4091
4576
|
return agents.map((agent) => {
|
|
@@ -4093,7 +4578,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
4093
4578
|
const tokenBalance = agentBalance?.tokens.find(
|
|
4094
4579
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
4095
4580
|
);
|
|
4096
|
-
let balance = tokenBalance ? (0,
|
|
4581
|
+
let balance = tokenBalance ? (0, import_viem8.parseUnits)(tokenBalance.amount, decimals) : 0n;
|
|
4097
4582
|
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
4098
4583
|
const chainNameById = {
|
|
4099
4584
|
1: "ETHEREUM",
|
|
@@ -4105,16 +4590,16 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
4105
4590
|
const positionChain = position2.chain.trim().toUpperCase();
|
|
4106
4591
|
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
4107
4592
|
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
4108
|
-
|
|
4109
|
-
try {
|
|
4110
|
-
balance += BigInt(position2.amountRaw);
|
|
4111
|
-
continue;
|
|
4112
|
-
} catch {
|
|
4113
|
-
}
|
|
4114
|
-
}
|
|
4115
|
-
balance += (0, import_viem7.parseUnits)(position2.amount, decimals);
|
|
4593
|
+
balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0, import_viem8.parseUnits)(position2.amount, decimals);
|
|
4116
4594
|
}
|
|
4117
4595
|
}
|
|
4596
|
+
const assetTotal = agentBalance?.assetBalances?.find(
|
|
4597
|
+
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
4598
|
+
);
|
|
4599
|
+
if (assetTotal) {
|
|
4600
|
+
const total = (0, import_viem8.parseUnits)(assetTotal.amount, decimals);
|
|
4601
|
+
if (total < balance) balance = total > 0n ? total : 0n;
|
|
4602
|
+
}
|
|
4118
4603
|
return { agent, balance };
|
|
4119
4604
|
});
|
|
4120
4605
|
}
|
|
@@ -4271,15 +4756,50 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
4271
4756
|
}
|
|
4272
4757
|
|
|
4273
4758
|
// src/client.ts
|
|
4274
|
-
var
|
|
4759
|
+
var import_viem12 = require("viem");
|
|
4275
4760
|
var import_chains4 = require("viem/chains");
|
|
4276
4761
|
|
|
4277
4762
|
// src/lib/sponsored-token-batch.ts
|
|
4278
|
-
var
|
|
4763
|
+
var import_viem10 = require("viem");
|
|
4279
4764
|
|
|
4280
4765
|
// src/lib/permit2-batch.ts
|
|
4281
|
-
var
|
|
4766
|
+
var import_viem9 = require("viem");
|
|
4282
4767
|
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
4768
|
+
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
4769
|
+
var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
4770
|
+
var ERC2612_PERMIT_TYPES = {
|
|
4771
|
+
Permit: [
|
|
4772
|
+
{ name: "owner", type: "address" },
|
|
4773
|
+
{ name: "spender", type: "address" },
|
|
4774
|
+
{ name: "value", type: "uint256" },
|
|
4775
|
+
{ name: "nonce", type: "uint256" },
|
|
4776
|
+
{ name: "deadline", type: "uint256" }
|
|
4777
|
+
]
|
|
4778
|
+
};
|
|
4779
|
+
var ERC2612_READ_ABI = (0, import_viem9.parseAbi)([
|
|
4780
|
+
"function name() view returns (string)",
|
|
4781
|
+
"function version() view returns (string)",
|
|
4782
|
+
"function nonces(address owner) view returns (uint256)"
|
|
4783
|
+
]);
|
|
4784
|
+
function erc2612TypedData(input) {
|
|
4785
|
+
return {
|
|
4786
|
+
domain: {
|
|
4787
|
+
name: input.permit.tokenName,
|
|
4788
|
+
version: input.permit.tokenVersion,
|
|
4789
|
+
chainId: input.chainId,
|
|
4790
|
+
verifyingContract: input.token
|
|
4791
|
+
},
|
|
4792
|
+
types: ERC2612_PERMIT_TYPES,
|
|
4793
|
+
primaryType: "Permit",
|
|
4794
|
+
message: {
|
|
4795
|
+
owner: input.owner,
|
|
4796
|
+
spender: BATCH_PERMIT2_ADDRESS,
|
|
4797
|
+
value: BigInt(input.permit.value),
|
|
4798
|
+
nonce: BigInt(input.permit.nonce),
|
|
4799
|
+
deadline: BigInt(input.permit.deadline)
|
|
4800
|
+
}
|
|
4801
|
+
};
|
|
4802
|
+
}
|
|
4283
4803
|
var PERMIT_BATCH_TYPES = {
|
|
4284
4804
|
PermitBatchWitnessTransferFrom: [
|
|
4285
4805
|
{ name: "permitted", type: "TokenPermissions[]" },
|
|
@@ -4294,7 +4814,7 @@ var PERMIT_BATCH_TYPES = {
|
|
|
4294
4814
|
{ name: "amount", type: "uint256" }
|
|
4295
4815
|
]
|
|
4296
4816
|
};
|
|
4297
|
-
var PERMIT2_BATCH_ABI = (0,
|
|
4817
|
+
var PERMIT2_BATCH_ABI = (0, import_viem9.parseAbi)([
|
|
4298
4818
|
"struct TokenPermissions { address token; uint256 amount; }",
|
|
4299
4819
|
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
4300
4820
|
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
@@ -4353,7 +4873,7 @@ function clear(key2) {
|
|
|
4353
4873
|
else window.localStorage.removeItem(key2);
|
|
4354
4874
|
}
|
|
4355
4875
|
function sponsorTokenBatch(i) {
|
|
4356
|
-
const key2 = `owney.token-batch.v1:${(0,
|
|
4876
|
+
const key2 = `owney.token-batch.v1:${(0, import_viem10.keccak256)((0, import_viem10.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
|
|
4357
4877
|
const plan = planOf(i.transfers);
|
|
4358
4878
|
const active = inflight.get(key2);
|
|
4359
4879
|
if (active) {
|
|
@@ -4384,7 +4904,7 @@ async function execute(i, key2, plan) {
|
|
|
4384
4904
|
baseUrl: i.baseUrl,
|
|
4385
4905
|
body
|
|
4386
4906
|
});
|
|
4387
|
-
if (!prepared.serializedTransaction || (0,
|
|
4907
|
+
if (!prepared.serializedTransaction || (0, import_viem10.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
|
|
4388
4908
|
throw new Error(
|
|
4389
4909
|
"Sponsorship API did not return a valid prepared transaction."
|
|
4390
4910
|
);
|
|
@@ -4399,7 +4919,7 @@ async function execute(i, key2, plan) {
|
|
|
4399
4919
|
baseUrl: i.baseUrl,
|
|
4400
4920
|
body
|
|
4401
4921
|
});
|
|
4402
|
-
if (result.txHash !== (0,
|
|
4922
|
+
if (result.txHash !== (0, import_viem10.keccak256)(body.serializedTransaction))
|
|
4403
4923
|
throw new Error(
|
|
4404
4924
|
"Sponsorship receipt does not match the pending transaction."
|
|
4405
4925
|
);
|
|
@@ -4414,7 +4934,7 @@ async function execute(i, key2, plan) {
|
|
|
4414
4934
|
const saved = read(key2);
|
|
4415
4935
|
if (saved) {
|
|
4416
4936
|
const previous = JSON.parse(saved);
|
|
4417
|
-
if (previous.chainId !== i.chainId || !(0,
|
|
4937
|
+
if (previous.chainId !== i.chainId || !(0, import_viem10.isAddressEqual)(previous.from, i.owner) || !(0, import_viem10.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
4418
4938
|
throw new Error(
|
|
4419
4939
|
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
4420
4940
|
);
|
|
@@ -4431,28 +4951,67 @@ async function execute(i, key2, plan) {
|
|
|
4431
4951
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
4432
4952
|
"Insufficient token balance for this deposit."
|
|
4433
4953
|
);
|
|
4434
|
-
if (allowance < total)
|
|
4954
|
+
if (allowance < total && (i.chainId !== 8453 || !(0, import_viem10.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
|
|
4435
4955
|
throw new OwneyError(
|
|
4436
4956
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
4437
4957
|
"token deposits need a one-time Permit2 approval."
|
|
4438
4958
|
);
|
|
4439
|
-
const
|
|
4959
|
+
const now = (await i.pub.getBlock()).timestamp;
|
|
4960
|
+
let erc2612Permit;
|
|
4961
|
+
if (allowance < total) {
|
|
4962
|
+
const [tokenName, tokenVersion, permitNonce] = await Promise.all([
|
|
4963
|
+
i.pub.readContract({
|
|
4964
|
+
address: i.token,
|
|
4965
|
+
abi: ERC2612_READ_ABI,
|
|
4966
|
+
functionName: "name"
|
|
4967
|
+
}),
|
|
4968
|
+
i.pub.readContract({
|
|
4969
|
+
address: i.token,
|
|
4970
|
+
abi: ERC2612_READ_ABI,
|
|
4971
|
+
functionName: "version"
|
|
4972
|
+
}),
|
|
4973
|
+
i.pub.readContract({
|
|
4974
|
+
address: i.token,
|
|
4975
|
+
abi: ERC2612_READ_ABI,
|
|
4976
|
+
functionName: "nonces",
|
|
4977
|
+
args: [i.owner]
|
|
4978
|
+
})
|
|
4979
|
+
]);
|
|
4980
|
+
const unsignedPermit = {
|
|
4981
|
+
value: MAX_UINT256.toString(),
|
|
4982
|
+
nonce: permitNonce.toString(),
|
|
4983
|
+
deadline: (now + 900n).toString(),
|
|
4984
|
+
tokenName,
|
|
4985
|
+
tokenVersion
|
|
4986
|
+
};
|
|
4987
|
+
const signature2 = await i.wallet.signTypedData({
|
|
4988
|
+
account: i.owner,
|
|
4989
|
+
...erc2612TypedData({
|
|
4990
|
+
chainId: 8453,
|
|
4991
|
+
token: i.token,
|
|
4992
|
+
owner: i.owner,
|
|
4993
|
+
permit: unsignedPermit
|
|
4994
|
+
})
|
|
4995
|
+
});
|
|
4996
|
+
erc2612Permit = { ...unsignedPermit, signature: signature2 };
|
|
4997
|
+
}
|
|
4998
|
+
const spender = erc2612Permit ? MULTICALL3_ADDRESS : await getSponsorRelayerAddress({
|
|
4440
4999
|
apiKey: i.apiKey,
|
|
4441
5000
|
baseUrl: i.baseUrl,
|
|
4442
5001
|
chainId: i.chainId
|
|
4443
5002
|
});
|
|
4444
|
-
const now = (await i.pub.getBlock()).timestamp;
|
|
4445
5003
|
const unsigned = {
|
|
4446
5004
|
chainId: i.chainId,
|
|
4447
5005
|
token: i.token,
|
|
4448
5006
|
from: i.owner,
|
|
4449
5007
|
transfers: i.transfers,
|
|
4450
5008
|
nonce: randomPermit2Nonce().toString(),
|
|
4451
|
-
deadline: (now + 900n).toString()
|
|
5009
|
+
deadline: (now + 900n).toString(),
|
|
5010
|
+
...erc2612Permit ? { erc2612Permit } : {}
|
|
4452
5011
|
};
|
|
4453
5012
|
const signature = await i.wallet.signTypedData({
|
|
4454
5013
|
account: i.owner,
|
|
4455
|
-
...batchTypedData(unsigned,
|
|
5014
|
+
...batchTypedData(unsigned, spender)
|
|
4456
5015
|
});
|
|
4457
5016
|
i.onApproved?.();
|
|
4458
5017
|
return send({ ...unsigned, signature });
|
|
@@ -4472,8 +5031,8 @@ function makeSponsoredTokenCallback(deps) {
|
|
|
4472
5031
|
"CHAIN_UNSUPPORTED",
|
|
4473
5032
|
`No sponsored token configured for chain ${chainId}`
|
|
4474
5033
|
);
|
|
4475
|
-
const pub = deps.getPublicClient(chainId), wallet = deps.getWalletClient(chainId);
|
|
4476
|
-
await ensureWalletOnChain(
|
|
5034
|
+
const pub = deps.getPublicClient(chainId), walletChain = deps.getWalletChainClient?.(chainId) ?? pub, wallet = deps.getWalletClient(chainId);
|
|
5035
|
+
await ensureWalletOnChain(walletChain, wallet, chainId);
|
|
4477
5036
|
return sponsorTokenBatch({
|
|
4478
5037
|
apiKey: deps.apiKey,
|
|
4479
5038
|
baseUrl: deps.baseUrl,
|
|
@@ -4569,7 +5128,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
|
4569
5128
|
}
|
|
4570
5129
|
|
|
4571
5130
|
// src/lib/sponsored-calls-deposit.ts
|
|
4572
|
-
var
|
|
5131
|
+
var import_viem11 = require("viem");
|
|
4573
5132
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
4574
5133
|
var DEFAULT_MAX_POLLS = 30;
|
|
4575
5134
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -4577,7 +5136,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
4577
5136
|
method: "wallet_getCapabilities",
|
|
4578
5137
|
params: [owner]
|
|
4579
5138
|
});
|
|
4580
|
-
const forChain = caps?.[(0,
|
|
5139
|
+
const forChain = caps?.[(0, import_viem11.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
4581
5140
|
return Boolean(forChain?.paymasterService?.supported);
|
|
4582
5141
|
}
|
|
4583
5142
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -4614,8 +5173,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4614
5173
|
const calls = transfers.map((transfer) => ({
|
|
4615
5174
|
to: token,
|
|
4616
5175
|
value: "0x0",
|
|
4617
|
-
data: (0,
|
|
4618
|
-
abi:
|
|
5176
|
+
data: (0, import_viem11.encodeFunctionData)({
|
|
5177
|
+
abi: import_viem11.erc20Abi,
|
|
4619
5178
|
functionName: "transfer",
|
|
4620
5179
|
args: [transfer.to, BigInt(transfer.amount)]
|
|
4621
5180
|
})
|
|
@@ -4653,7 +5212,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4653
5212
|
{
|
|
4654
5213
|
version: "2.0.0",
|
|
4655
5214
|
from: deps.ownerAddress,
|
|
4656
|
-
chainId: (0,
|
|
5215
|
+
chainId: (0, import_viem11.toHex)(chainId),
|
|
4657
5216
|
atomicRequired: transfers.length > 1,
|
|
4658
5217
|
calls,
|
|
4659
5218
|
capabilities: {
|
|
@@ -4713,6 +5272,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4713
5272
|
}
|
|
4714
5273
|
|
|
4715
5274
|
// src/client.ts
|
|
5275
|
+
var PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS = 6;
|
|
5276
|
+
var PERMIT2_ALLOWANCE_VERIFY_DELAY_MS = 250;
|
|
4716
5277
|
function encodeMultiAgentCursor(map) {
|
|
4717
5278
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
4718
5279
|
}
|
|
@@ -4773,6 +5334,7 @@ var OwneySDK = class {
|
|
|
4773
5334
|
// leave every user's agent profile alone.
|
|
4774
5335
|
orgAgentConfig;
|
|
4775
5336
|
orgAgentConfigPromise = null;
|
|
5337
|
+
rpcUrls;
|
|
4776
5338
|
zyfaiRpcUrls;
|
|
4777
5339
|
yieldseekerApiBaseUrl;
|
|
4778
5340
|
yieldseekerSiweOrigin;
|
|
@@ -4797,7 +5359,12 @@ var OwneySDK = class {
|
|
|
4797
5359
|
constructor(config) {
|
|
4798
5360
|
this.apiKey = config.apiKey;
|
|
4799
5361
|
if (config.debug) setOwneyDebug(true);
|
|
4800
|
-
this.
|
|
5362
|
+
this.rpcUrls = resolveRpcUrls(
|
|
5363
|
+
config.rpcUrls,
|
|
5364
|
+
config.apiKey,
|
|
5365
|
+
config.routingApiBaseUrl
|
|
5366
|
+
);
|
|
5367
|
+
this.zyfaiRpcUrls = config.rpcUrls ? void 0 : config.zyfaiRpcUrls;
|
|
4801
5368
|
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
4802
5369
|
this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
|
|
4803
5370
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
@@ -4887,6 +5454,9 @@ var OwneySDK = class {
|
|
|
4887
5454
|
}
|
|
4888
5455
|
return this.state.provider;
|
|
4889
5456
|
}
|
|
5457
|
+
getPaidRpcClient(chainId) {
|
|
5458
|
+
return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
|
|
5459
|
+
}
|
|
4890
5460
|
/** Builds the default USDC batch callback for the connected wallet. */
|
|
4891
5461
|
getDefaultSponsoredCallback(onApproved) {
|
|
4892
5462
|
if (!onApproved && this.cachedSponsoredCallback)
|
|
@@ -4902,14 +5472,15 @@ var OwneySDK = class {
|
|
|
4902
5472
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
4903
5473
|
// PublicClient/WalletClient param types — structurally identical at
|
|
4904
5474
|
// runtime, but the two share a name TS treats as unrelated.
|
|
4905
|
-
getPublicClient: (cid) => (
|
|
5475
|
+
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
5476
|
+
getWalletChainClient: (cid) => (0, import_viem12.createPublicClient)({
|
|
4906
5477
|
chain: VIEM_CHAIN2[cid],
|
|
4907
|
-
transport: (0,
|
|
5478
|
+
transport: (0, import_viem12.custom)(provider)
|
|
4908
5479
|
}),
|
|
4909
|
-
getWalletClient: (cid) => (0,
|
|
5480
|
+
getWalletClient: (cid) => (0, import_viem12.createWalletClient)({
|
|
4910
5481
|
account: owner,
|
|
4911
5482
|
chain: VIEM_CHAIN2[cid],
|
|
4912
|
-
transport: (0,
|
|
5483
|
+
transport: (0, import_viem12.custom)(provider)
|
|
4913
5484
|
})
|
|
4914
5485
|
});
|
|
4915
5486
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -4952,14 +5523,15 @@ var OwneySDK = class {
|
|
|
4952
5523
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
4953
5524
|
// PublicClient/WalletClient param types — structurally identical at
|
|
4954
5525
|
// runtime, but the two share a name TS treats as unrelated.
|
|
4955
|
-
getPublicClient: (cid) => (
|
|
5526
|
+
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
5527
|
+
getWalletChainClient: (cid) => (0, import_viem12.createPublicClient)({
|
|
4956
5528
|
chain: VIEM_CHAIN2[cid],
|
|
4957
|
-
transport: (0,
|
|
5529
|
+
transport: (0, import_viem12.custom)(provider)
|
|
4958
5530
|
}),
|
|
4959
|
-
getWalletClient: (cid) => (0,
|
|
5531
|
+
getWalletClient: (cid) => (0, import_viem12.createWalletClient)({
|
|
4960
5532
|
account: owner,
|
|
4961
5533
|
chain: VIEM_CHAIN2[cid],
|
|
4962
|
-
transport: (0,
|
|
5534
|
+
transport: (0, import_viem12.custom)(provider)
|
|
4963
5535
|
})
|
|
4964
5536
|
});
|
|
4965
5537
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -5065,12 +5637,21 @@ var OwneySDK = class {
|
|
|
5065
5637
|
createAgent(agentId, key2) {
|
|
5066
5638
|
if (agentId === "zyfai") {
|
|
5067
5639
|
if (!key2) return null;
|
|
5068
|
-
return new ZyfaiAgent(
|
|
5640
|
+
return new ZyfaiAgent(
|
|
5641
|
+
key2,
|
|
5642
|
+
this.zyfaiRpcUrls ? resolveRpcUrls(
|
|
5643
|
+
this.zyfaiRpcUrls,
|
|
5644
|
+
this.apiKey,
|
|
5645
|
+
this.routingApiBaseUrl
|
|
5646
|
+
) : this.rpcUrls,
|
|
5647
|
+
this.referralSource
|
|
5648
|
+
);
|
|
5069
5649
|
}
|
|
5070
5650
|
if (agentId === "yieldseeker") {
|
|
5071
5651
|
return new YieldseekerAgent(this.apiKey, {
|
|
5072
5652
|
auth: { origin: this.yieldseekerSiweOrigin },
|
|
5073
|
-
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
5653
|
+
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl),
|
|
5654
|
+
rpcUrls: this.rpcUrls
|
|
5074
5655
|
});
|
|
5075
5656
|
}
|
|
5076
5657
|
return null;
|
|
@@ -5361,7 +5942,8 @@ var OwneySDK = class {
|
|
|
5361
5942
|
);
|
|
5362
5943
|
await this.approvePermit2(
|
|
5363
5944
|
asset,
|
|
5364
|
-
requiredAmount
|
|
5945
|
+
requiredAmount,
|
|
5946
|
+
cid
|
|
5365
5947
|
);
|
|
5366
5948
|
return batchTransfer(cid, transfers);
|
|
5367
5949
|
}
|
|
@@ -5405,10 +5987,12 @@ var OwneySDK = class {
|
|
|
5405
5987
|
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
5406
5988
|
* two independent auto-recovery mechanisms:
|
|
5407
5989
|
*
|
|
5408
|
-
* 1. Missing Permit2 allowance
|
|
5409
|
-
* callback and the attempt fails with
|
|
5410
|
-
*
|
|
5411
|
-
*
|
|
5990
|
+
* 1. Missing Permit2 allowance outside the atomic Base-USDC path: when the
|
|
5991
|
+
* app did not supply its own callback and the attempt fails with
|
|
5992
|
+
* `PERMIT2_APPROVAL_REQUIRED`, this is the wallet's first Permit2 deposit
|
|
5993
|
+
* for that token. Base USDC bundles a gasless ERC-2612 approval inside
|
|
5994
|
+
* its sponsored deposit and never reaches this branch. Other tokens send
|
|
5995
|
+
* the one-time user-paid Permit2 approval via `approvePermit2()` and
|
|
5412
5996
|
* retry the SAME sponsored attempt once. Bounded to one approval attempt
|
|
5413
5997
|
* per call so a wallet/agent that keeps reporting the allowance as
|
|
5414
5998
|
* missing can't loop forever. If `approvePermit2()` itself throws (e.g.
|
|
@@ -5451,7 +6035,8 @@ var OwneySDK = class {
|
|
|
5451
6035
|
);
|
|
5452
6036
|
await this.approvePermit2(
|
|
5453
6037
|
asset,
|
|
5454
|
-
BigInt(amount)
|
|
6038
|
+
BigInt(amount),
|
|
6039
|
+
chainId
|
|
5455
6040
|
);
|
|
5456
6041
|
continue;
|
|
5457
6042
|
}
|
|
@@ -5598,6 +6183,32 @@ var OwneySDK = class {
|
|
|
5598
6183
|
}
|
|
5599
6184
|
return eligible;
|
|
5600
6185
|
}
|
|
6186
|
+
/**
|
|
6187
|
+
* Run owner-approved withdrawals before relayer-only withdrawals. Wallet
|
|
6188
|
+
* approval is the only point at which the user can cancel the aggregate
|
|
6189
|
+
* operation, so no relayer leg should commit before it has completed.
|
|
6190
|
+
*/
|
|
6191
|
+
orderAgentsForWithdrawal(agents) {
|
|
6192
|
+
return agents.map((agent, index) => ({ agent, index })).sort((left, right) => {
|
|
6193
|
+
const approvalOrder = Number(Boolean(right.agent.withdrawalRequiresWalletApproval)) - Number(Boolean(left.agent.withdrawalRequiresWalletApproval));
|
|
6194
|
+
return approvalOrder || left.index - right.index;
|
|
6195
|
+
}).map(({ agent }) => agent);
|
|
6196
|
+
}
|
|
6197
|
+
isUserRejectedWithdrawal(error) {
|
|
6198
|
+
let current = error;
|
|
6199
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6200
|
+
while (current && typeof current === "object" && !seen.has(current)) {
|
|
6201
|
+
seen.add(current);
|
|
6202
|
+
const candidate = current;
|
|
6203
|
+
if (candidate.code === 4001 || candidate.code === "4001") return true;
|
|
6204
|
+
const message = [candidate.message, candidate.shortMessage].filter((value) => typeof value === "string").join(" ");
|
|
6205
|
+
if (/user (?:rejected|denied)|rejected by user/i.test(message)) {
|
|
6206
|
+
return true;
|
|
6207
|
+
}
|
|
6208
|
+
current = candidate.cause;
|
|
6209
|
+
}
|
|
6210
|
+
return false;
|
|
6211
|
+
}
|
|
5601
6212
|
// --- Fund operations ---
|
|
5602
6213
|
/**
|
|
5603
6214
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -5609,7 +6220,14 @@ var OwneySDK = class {
|
|
|
5609
6220
|
* @returns {AgentWithdrawResult} for a single agent, or {OwneyWithdrawResult} with per-agent results
|
|
5610
6221
|
*/
|
|
5611
6222
|
async withdraw(options) {
|
|
5612
|
-
const { asset, amount, agentId } = options;
|
|
6223
|
+
const { asset, amount, agentId, onAgentResult } = options;
|
|
6224
|
+
const notifyAgentResult = (id, result) => {
|
|
6225
|
+
try {
|
|
6226
|
+
onAgentResult?.(id, result);
|
|
6227
|
+
} catch (error) {
|
|
6228
|
+
console.warn("Withdrawal result observer failed:", error);
|
|
6229
|
+
}
|
|
6230
|
+
};
|
|
5613
6231
|
const state = this.requireState();
|
|
5614
6232
|
const chainId = this.requireChainId();
|
|
5615
6233
|
const token = asset;
|
|
@@ -5625,21 +6243,26 @@ var OwneySDK = class {
|
|
|
5625
6243
|
if (agentId) {
|
|
5626
6244
|
const agent = this.getAgent(agentId);
|
|
5627
6245
|
this.validateAssetSupport(agent, chainId, asset);
|
|
5628
|
-
|
|
6246
|
+
const result = await withFailureReporting(
|
|
5629
6247
|
this.apiKey,
|
|
5630
6248
|
agent.id,
|
|
5631
6249
|
() => agent.withdraw(state, chainId, token, amount),
|
|
5632
6250
|
this.routingApiBaseUrl
|
|
5633
6251
|
);
|
|
6252
|
+
notifyAgentResult(agent.id, result);
|
|
6253
|
+
return result;
|
|
5634
6254
|
}
|
|
5635
6255
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
6256
|
+
const withdrawalAgents = this.orderAgentsForWithdrawal(eligibleAgents);
|
|
5636
6257
|
if (!amount) {
|
|
5637
6258
|
const results2 = {};
|
|
5638
6259
|
const agentErrors2 = {};
|
|
5639
|
-
for (const agent of
|
|
6260
|
+
for (const agent of withdrawalAgents) {
|
|
5640
6261
|
try {
|
|
5641
6262
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
6263
|
+
notifyAgentResult(agent.id, results2[agent.id]);
|
|
5642
6264
|
} catch (err) {
|
|
6265
|
+
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
5643
6266
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
5644
6267
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
5645
6268
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
@@ -5720,12 +6343,13 @@ var OwneySDK = class {
|
|
|
5720
6343
|
planned: 0n
|
|
5721
6344
|
}));
|
|
5722
6345
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
6346
|
+
const orderedPlans = this.orderAgentsForWithdrawal(plans.map((p) => p.agent)).map((agent) => plans.find((plan) => plan.agent === agent));
|
|
5723
6347
|
const results = {};
|
|
5724
6348
|
const agentErrors = {
|
|
5725
6349
|
...aggregated.agentErrors ?? {}
|
|
5726
6350
|
};
|
|
5727
|
-
for (let i = 0; i <
|
|
5728
|
-
const p =
|
|
6351
|
+
for (let i = 0; i < orderedPlans.length; i++) {
|
|
6352
|
+
const p = orderedPlans[i];
|
|
5729
6353
|
if (p.planned === 0n) continue;
|
|
5730
6354
|
try {
|
|
5731
6355
|
results[p.agent.id] = await p.agent.withdraw(
|
|
@@ -5734,7 +6358,9 @@ var OwneySDK = class {
|
|
|
5734
6358
|
token,
|
|
5735
6359
|
p.planned.toString()
|
|
5736
6360
|
);
|
|
6361
|
+
notifyAgentResult(p.agent.id, results[p.agent.id]);
|
|
5737
6362
|
} catch (err) {
|
|
6363
|
+
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
5738
6364
|
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
5739
6365
|
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
5740
6366
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
@@ -5746,7 +6372,7 @@ var OwneySDK = class {
|
|
|
5746
6372
|
);
|
|
5747
6373
|
const failedAmount = p.planned;
|
|
5748
6374
|
p.planned = 0n;
|
|
5749
|
-
redistributeShare(
|
|
6375
|
+
redistributeShare(orderedPlans, i, failedAmount);
|
|
5750
6376
|
}
|
|
5751
6377
|
}
|
|
5752
6378
|
if (Object.keys(results).length === 0) {
|
|
@@ -5856,23 +6482,11 @@ var OwneySDK = class {
|
|
|
5856
6482
|
const agent = this.getAgent(agentId);
|
|
5857
6483
|
return this.readAgent(agent, "earnings", () => agent.getEarnings(state, chainId));
|
|
5858
6484
|
}
|
|
5859
|
-
let totalEarnings = 0;
|
|
5860
|
-
const results = {};
|
|
5861
6485
|
const entries = [...this.getActiveAgents().entries()];
|
|
5862
|
-
|
|
5863
|
-
entries
|
|
5864
|
-
|
|
5865
|
-
return [id, e];
|
|
5866
|
-
})
|
|
6486
|
+
return this.aggregateEarnings(
|
|
6487
|
+
entries,
|
|
6488
|
+
(agent) => this.readAgent(agent, "earnings", () => agent.getEarnings(state, chainId))
|
|
5867
6489
|
);
|
|
5868
|
-
for (const [id, e] of earningsResults) {
|
|
5869
|
-
results[id] = e;
|
|
5870
|
-
totalEarnings += Number(e.lifetimeEarnings);
|
|
5871
|
-
}
|
|
5872
|
-
return {
|
|
5873
|
-
totalEarnings: String(totalEarnings),
|
|
5874
|
-
agentEarnings: results
|
|
5875
|
-
};
|
|
5876
6490
|
}
|
|
5877
6491
|
/**
|
|
5878
6492
|
* Return refreshed earnings while letting each agent own its refresh policy.
|
|
@@ -5887,18 +6501,42 @@ var OwneySDK = class {
|
|
|
5887
6501
|
() => agent.refreshEarnings?.(state, chainId) ?? agent.getEarnings(state, chainId)
|
|
5888
6502
|
);
|
|
5889
6503
|
if (agentId) return refreshAgent(this.getAgent(agentId));
|
|
5890
|
-
|
|
6504
|
+
return this.aggregateEarnings([...this.getActiveAgents().entries()], refreshAgent);
|
|
6505
|
+
}
|
|
6506
|
+
async aggregateEarnings(entries, read2) {
|
|
6507
|
+
const settled = await Promise.allSettled(entries.map(([, agent]) => read2(agent)));
|
|
5891
6508
|
const agentEarnings = {};
|
|
5892
|
-
const
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
)
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
6509
|
+
const agentErrors = {};
|
|
6510
|
+
const agentRetryAt = {};
|
|
6511
|
+
const failures = [];
|
|
6512
|
+
let totalEarnings = 0;
|
|
6513
|
+
for (let i = 0; i < settled.length; i++) {
|
|
6514
|
+
const [id] = entries[i];
|
|
6515
|
+
const result = settled[i];
|
|
6516
|
+
if (result.status === "fulfilled") {
|
|
6517
|
+
agentEarnings[id] = result.value;
|
|
6518
|
+
totalEarnings += Number(result.value.lifetimeEarnings);
|
|
6519
|
+
continue;
|
|
6520
|
+
}
|
|
6521
|
+
const reason = result.reason;
|
|
6522
|
+
failures.push(reason);
|
|
6523
|
+
const retryDelay = rateLimitDelay(reason);
|
|
6524
|
+
if (retryDelay !== void 0) agentRetryAt[id] = Date.now() + retryDelay;
|
|
6525
|
+
agentErrors[id] = reason instanceof Error ? reason.message : String(reason);
|
|
6526
|
+
console.error(`[owney-sdk] Earnings fetch failed for agent "${id}":`, reason);
|
|
6527
|
+
}
|
|
6528
|
+
if (entries.length > 0 && Object.keys(agentEarnings).length === 0) {
|
|
6529
|
+
throw new OwneyError(
|
|
6530
|
+
"EARNINGS_ALL_FAILED",
|
|
6531
|
+
"Failed to fetch earnings for all active agents.",
|
|
6532
|
+
{ agentErrors, failures }
|
|
6533
|
+
);
|
|
5900
6534
|
}
|
|
5901
|
-
return {
|
|
6535
|
+
return {
|
|
6536
|
+
totalEarnings: String(totalEarnings),
|
|
6537
|
+
agentEarnings,
|
|
6538
|
+
...Object.keys(agentErrors).length > 0 ? { agentErrors, agentRetryAt } : {}
|
|
6539
|
+
};
|
|
5902
6540
|
}
|
|
5903
6541
|
/**
|
|
5904
6542
|
* Get the weighted APY for the user's account over a time period.
|
|
@@ -6172,16 +6810,18 @@ var OwneySDK = class {
|
|
|
6172
6810
|
}
|
|
6173
6811
|
/**
|
|
6174
6812
|
* User-paid approval of Permit2 on the selected token for the active chain.
|
|
6175
|
-
*
|
|
6176
|
-
*
|
|
6177
|
-
*
|
|
6813
|
+
* Grants the maximum ERC20 allowance so later deposits do not require another
|
|
6814
|
+
* approval. Resolves after one confirmation so the subsequent deposit attempt
|
|
6815
|
+
* sees the new allowance. Deposit retries pass their captured chain id so a
|
|
6816
|
+
* concurrent activation cannot redirect the approval to another network.
|
|
6178
6817
|
*
|
|
6179
6818
|
* @param requiredAmount Raw base-unit amount the pending deposit must cover.
|
|
6819
|
+
* @param expectedChainId Chain captured by the deposit that requested approval.
|
|
6180
6820
|
* @returns the approval transaction hash.
|
|
6181
6821
|
*/
|
|
6182
|
-
async approvePermit2(asset = "WETH", requiredAmount = 0n) {
|
|
6822
|
+
async approvePermit2(asset = "WETH", requiredAmount = 0n, expectedChainId) {
|
|
6183
6823
|
const state = this.requireState();
|
|
6184
|
-
const chainId = this.requireChainId();
|
|
6824
|
+
const chainId = expectedChainId ?? this.requireChainId();
|
|
6185
6825
|
this.getEligibleAgents(chainId, asset, { excludeDisabled: true });
|
|
6186
6826
|
const token = sponsoredTokensFor(asset)[chainId];
|
|
6187
6827
|
if (!token) {
|
|
@@ -6191,16 +6831,18 @@ var OwneySDK = class {
|
|
|
6191
6831
|
);
|
|
6192
6832
|
}
|
|
6193
6833
|
const provider = this.requireConnectedProvider();
|
|
6194
|
-
const
|
|
6834
|
+
const walletChainClient = (0, import_viem12.createPublicClient)({
|
|
6195
6835
|
chain: VIEM_CHAIN2[chainId],
|
|
6196
|
-
transport: (0,
|
|
6836
|
+
transport: (0, import_viem12.custom)(provider)
|
|
6197
6837
|
});
|
|
6838
|
+
const publicClient = this.getPaidRpcClient(chainId);
|
|
6198
6839
|
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
6199
|
-
const wallet = (0,
|
|
6840
|
+
const wallet = (0, import_viem12.createWalletClient)({
|
|
6200
6841
|
account: state.walletAddress,
|
|
6201
6842
|
chain: VIEM_CHAIN2[chainId],
|
|
6202
|
-
transport: (0,
|
|
6843
|
+
transport: (0, import_viem12.custom)(provider)
|
|
6203
6844
|
});
|
|
6845
|
+
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
6204
6846
|
const hash = await wallet.writeContract({
|
|
6205
6847
|
address: token,
|
|
6206
6848
|
abi: ERC20_ALLOWANCE_ABI,
|
|
@@ -6209,14 +6851,53 @@ var OwneySDK = class {
|
|
|
6209
6851
|
account: state.walletAddress,
|
|
6210
6852
|
chain: VIEM_CHAIN2[chainId]
|
|
6211
6853
|
});
|
|
6212
|
-
const receipt = await
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6854
|
+
const receipt = await withPaidRpcDiagnostics(
|
|
6855
|
+
() => publicClient.waitForTransactionReceipt({
|
|
6856
|
+
hash,
|
|
6857
|
+
confirmations: 1
|
|
6858
|
+
}),
|
|
6859
|
+
chainId,
|
|
6860
|
+
"eth_getTransactionReceipt"
|
|
6861
|
+
);
|
|
6216
6862
|
if (receipt.status !== "success") {
|
|
6217
6863
|
throw new Error(`Permit2 approval reverted (tx ${hash})`);
|
|
6218
6864
|
}
|
|
6219
|
-
|
|
6865
|
+
let observedAllowance = 0n;
|
|
6866
|
+
let verificationError;
|
|
6867
|
+
for (let attempt = 0; attempt < PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS; attempt += 1) {
|
|
6868
|
+
try {
|
|
6869
|
+
observedAllowance = await readPermit2Allowance(
|
|
6870
|
+
publicClient,
|
|
6871
|
+
token,
|
|
6872
|
+
state.walletAddress,
|
|
6873
|
+
attempt === 0 ? receipt.blockNumber : void 0
|
|
6874
|
+
);
|
|
6875
|
+
verificationError = void 0;
|
|
6876
|
+
if (observedAllowance >= requiredAmount) return hash;
|
|
6877
|
+
} catch (error) {
|
|
6878
|
+
verificationError = error;
|
|
6879
|
+
}
|
|
6880
|
+
if (attempt + 1 < PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS) {
|
|
6881
|
+
await new Promise(
|
|
6882
|
+
(resolve) => setTimeout(resolve, PERMIT2_ALLOWANCE_VERIFY_DELAY_MS)
|
|
6883
|
+
);
|
|
6884
|
+
}
|
|
6885
|
+
}
|
|
6886
|
+
throw new OwneyError(
|
|
6887
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
6888
|
+
"Permit2 approval was confirmed, but the required token allowance was not observable.",
|
|
6889
|
+
{
|
|
6890
|
+
approvalConfirmed: true,
|
|
6891
|
+
approvalTxHash: hash,
|
|
6892
|
+
owner: state.walletAddress,
|
|
6893
|
+
token,
|
|
6894
|
+
spender: PERMIT2_ADDRESS,
|
|
6895
|
+
chainId,
|
|
6896
|
+
requiredAmount: requiredAmount.toString(),
|
|
6897
|
+
observedAllowance: observedAllowance.toString(),
|
|
6898
|
+
...verificationError instanceof Error ? { verificationError: verificationError.message } : {}
|
|
6899
|
+
}
|
|
6900
|
+
);
|
|
6220
6901
|
}
|
|
6221
6902
|
// --- Discovery (no wallet required) ---
|
|
6222
6903
|
/**
|
|
@@ -6321,7 +7002,7 @@ var OwneySDK = class {
|
|
|
6321
7002
|
};
|
|
6322
7003
|
|
|
6323
7004
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
6324
|
-
var
|
|
7005
|
+
var import_viem13 = require("viem");
|
|
6325
7006
|
var import_siwe2 = require("siwe");
|
|
6326
7007
|
var import_sdk2 = require("@zyfai/sdk");
|
|
6327
7008
|
|
|
@@ -6448,7 +7129,7 @@ function buildSIWXConfig(deps) {
|
|
|
6448
7129
|
issuedAt,
|
|
6449
7130
|
toString() {
|
|
6450
7131
|
return new import_siwe2.SiweMessage({
|
|
6451
|
-
address: (0,
|
|
7132
|
+
address: (0, import_viem13.getAddress)(accountAddress),
|
|
6452
7133
|
chainId: numericChainId(chainId),
|
|
6453
7134
|
domain,
|
|
6454
7135
|
uri,
|