@owney/sdk 0.7.25-beta.4 → 0.7.25-beta.7
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 +67 -14
- package/dist/index.cjs +590 -181
- package/dist/index.d.cts +51 -9
- package/dist/index.d.ts +51 -9
- package/dist/index.js +531 -119
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -125,6 +125,34 @@ function rateLimitDelay(error, now = Date.now()) {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// src/lib/agent-reads.ts
|
|
128
|
+
function rateLimitDiagnostics(error) {
|
|
129
|
+
const seen = /* @__PURE__ */ new Set();
|
|
130
|
+
let details = {};
|
|
131
|
+
function visit(value, depth = 0) {
|
|
132
|
+
if (depth > 6 || !value || typeof value !== "object" || seen.has(value))
|
|
133
|
+
return;
|
|
134
|
+
seen.add(value);
|
|
135
|
+
const record = value;
|
|
136
|
+
for (const key2 of [
|
|
137
|
+
"rpcSource",
|
|
138
|
+
"chainId",
|
|
139
|
+
"rpcMethod",
|
|
140
|
+
"providerRequestId",
|
|
141
|
+
"statusCode"
|
|
142
|
+
]) {
|
|
143
|
+
const candidate = record[key2];
|
|
144
|
+
if (candidate !== void 0 && details[key2] === void 0)
|
|
145
|
+
details = { ...details, [key2]: candidate };
|
|
146
|
+
}
|
|
147
|
+
for (const key2 of ["details", "cause", "response", "fields", "error"])
|
|
148
|
+
visit(record[key2], depth + 1);
|
|
149
|
+
}
|
|
150
|
+
visit(error);
|
|
151
|
+
return {
|
|
152
|
+
rpcSource: details.rpcSource ?? "agent-api",
|
|
153
|
+
...details
|
|
154
|
+
};
|
|
155
|
+
}
|
|
128
156
|
var AgentReads = class {
|
|
129
157
|
inFlight = /* @__PURE__ */ new Map();
|
|
130
158
|
cooldowns = /* @__PURE__ */ new Map();
|
|
@@ -132,14 +160,15 @@ var AgentReads = class {
|
|
|
132
160
|
clearInFlight() {
|
|
133
161
|
this.inFlight.clear();
|
|
134
162
|
}
|
|
135
|
-
limited(agentId, until) {
|
|
163
|
+
limited(agentId, until, diagnostics) {
|
|
136
164
|
return new OwneyError(
|
|
137
165
|
"AGENT_RATE_LIMITED",
|
|
138
166
|
"Too many requests. Please wait before trying again.",
|
|
139
167
|
{
|
|
140
168
|
statusCode: 429,
|
|
141
169
|
retryAt: until,
|
|
142
|
-
retryAfterSeconds: Math.max(0, Math.ceil((until - Date.now()) / 1e3))
|
|
170
|
+
retryAfterSeconds: Math.max(0, Math.ceil((until - Date.now()) / 1e3)),
|
|
171
|
+
...diagnostics
|
|
143
172
|
},
|
|
144
173
|
agentId
|
|
145
174
|
);
|
|
@@ -147,7 +176,9 @@ var AgentReads = class {
|
|
|
147
176
|
run(agentId, key2, fetch2) {
|
|
148
177
|
const cooldown = this.cooldowns.get(agentId);
|
|
149
178
|
if (cooldown && cooldown.until > Date.now()) {
|
|
150
|
-
return Promise.reject(
|
|
179
|
+
return Promise.reject(
|
|
180
|
+
this.limited(agentId, cooldown.until, cooldown.diagnostics)
|
|
181
|
+
);
|
|
151
182
|
}
|
|
152
183
|
const requestKey = JSON.stringify([agentId, key2]);
|
|
153
184
|
const existing = this.inFlight.get(requestKey);
|
|
@@ -168,8 +199,9 @@ var AgentReads = class {
|
|
|
168
199
|
Math.min(3e4 * 2 ** (failures - 1), 3e5)
|
|
169
200
|
);
|
|
170
201
|
const until = Math.max(previous?.until ?? 0, Date.now() + delay);
|
|
171
|
-
|
|
172
|
-
|
|
202
|
+
const diagnostics = rateLimitDiagnostics(error);
|
|
203
|
+
this.cooldowns.set(agentId, { until, failures, diagnostics });
|
|
204
|
+
throw this.limited(agentId, until, diagnostics);
|
|
173
205
|
}
|
|
174
206
|
).finally(() => {
|
|
175
207
|
if (this.inFlight.get(requestKey) === promise)
|
|
@@ -182,7 +214,7 @@ var AgentReads = class {
|
|
|
182
214
|
|
|
183
215
|
// src/agents/zyfai/zyfai.agent.ts
|
|
184
216
|
import { SMART_SESSIONS_VALIDATOR, ZyfaiSDK } from "@zyfai/sdk";
|
|
185
|
-
import {
|
|
217
|
+
import { parseAbi } from "viem";
|
|
186
218
|
import { base, arbitrum, mainnet } from "viem/chains";
|
|
187
219
|
|
|
188
220
|
// src/types/config.ts
|
|
@@ -1063,15 +1095,148 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
1063
1095
|
return !protocolListsEqual(current.protocols, desiredProtocols);
|
|
1064
1096
|
}
|
|
1065
1097
|
|
|
1098
|
+
// src/lib/paid-rpc.ts
|
|
1099
|
+
import {
|
|
1100
|
+
createPublicClient,
|
|
1101
|
+
http
|
|
1102
|
+
} from "viem";
|
|
1103
|
+
var PAID_RPC_RETRY_COUNT = 3;
|
|
1104
|
+
var PAID_RPC_RETRY_DELAY_MS = 1e3;
|
|
1105
|
+
function configuredRpcProxyBaseUrl(baseUrl) {
|
|
1106
|
+
const configured = (baseUrl ?? "")?.trim();
|
|
1107
|
+
if (!configured) {
|
|
1108
|
+
throw new Error(
|
|
1109
|
+
"OWNEY_ROUTING_API_BASE_URL is required for RPC proxy requests unless routingApiBaseUrl or all rpcUrls are configured."
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
return configured;
|
|
1113
|
+
}
|
|
1114
|
+
function rpcProxyUrl(chainId, apiKey, baseUrl) {
|
|
1115
|
+
const url = new URL(
|
|
1116
|
+
`${configuredRpcProxyBaseUrl(baseUrl).replace(/\/$/, "")}/api/v1/rpc/${chainId}`
|
|
1117
|
+
);
|
|
1118
|
+
if (apiKey) url.searchParams.set("apiKey", apiKey);
|
|
1119
|
+
return url.toString();
|
|
1120
|
+
}
|
|
1121
|
+
function resolveRpcUrl(rpcUrls, chainId, apiKey = "", baseUrl) {
|
|
1122
|
+
const url = rpcUrls?.[chainId]?.trim();
|
|
1123
|
+
if (url) return url;
|
|
1124
|
+
return rpcProxyUrl(chainId, apiKey, baseUrl);
|
|
1125
|
+
}
|
|
1126
|
+
function resolveRpcUrls(rpcUrls, apiKey = "", baseUrl) {
|
|
1127
|
+
return {
|
|
1128
|
+
1: resolveRpcUrl(rpcUrls, 1, apiKey, baseUrl),
|
|
1129
|
+
8453: resolveRpcUrl(rpcUrls, 8453, apiKey, baseUrl),
|
|
1130
|
+
42161: resolveRpcUrl(rpcUrls, 42161, apiKey, baseUrl)
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
function createPaidRpcClient(chain, rpcUrls) {
|
|
1134
|
+
const url = resolveRpcUrl(
|
|
1135
|
+
rpcUrls,
|
|
1136
|
+
chain.id
|
|
1137
|
+
);
|
|
1138
|
+
return createPublicClient({
|
|
1139
|
+
chain,
|
|
1140
|
+
transport: http(url, {
|
|
1141
|
+
retryCount: PAID_RPC_RETRY_COUNT,
|
|
1142
|
+
retryDelay: PAID_RPC_RETRY_DELAY_MS
|
|
1143
|
+
})
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
function headerValue(error, name) {
|
|
1147
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1148
|
+
let value;
|
|
1149
|
+
function visit(candidate, depth = 0) {
|
|
1150
|
+
if (value || depth > 6 || !candidate || typeof candidate !== "object")
|
|
1151
|
+
return;
|
|
1152
|
+
if (seen.has(candidate)) return;
|
|
1153
|
+
seen.add(candidate);
|
|
1154
|
+
const record = candidate;
|
|
1155
|
+
const headers = record.headers;
|
|
1156
|
+
let found;
|
|
1157
|
+
if (typeof headers?.get === "function") {
|
|
1158
|
+
found = headers.get(name);
|
|
1159
|
+
} else {
|
|
1160
|
+
const headerRecord = headers;
|
|
1161
|
+
found = headerRecord?.[name] ?? headerRecord?.[name.toLowerCase()];
|
|
1162
|
+
}
|
|
1163
|
+
if (typeof found === "string" && found) {
|
|
1164
|
+
value = found;
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
for (const key2 of ["cause", "details", "response", "error"])
|
|
1168
|
+
visit(record[key2], depth + 1);
|
|
1169
|
+
}
|
|
1170
|
+
visit(error);
|
|
1171
|
+
return value;
|
|
1172
|
+
}
|
|
1173
|
+
function statusCode(error) {
|
|
1174
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1175
|
+
let status;
|
|
1176
|
+
function visit(candidate, depth = 0) {
|
|
1177
|
+
if (status || depth > 6 || !candidate || typeof candidate !== "object")
|
|
1178
|
+
return;
|
|
1179
|
+
if (seen.has(candidate)) return;
|
|
1180
|
+
seen.add(candidate);
|
|
1181
|
+
const record = candidate;
|
|
1182
|
+
for (const key2 of ["status", "statusCode"]) {
|
|
1183
|
+
const parsed = Number(record[key2]);
|
|
1184
|
+
if (Number.isInteger(parsed) && parsed >= 100 && parsed <= 599) {
|
|
1185
|
+
status = parsed;
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
for (const key2 of ["cause", "details", "response", "error"])
|
|
1190
|
+
visit(record[key2], depth + 1);
|
|
1191
|
+
}
|
|
1192
|
+
visit(error);
|
|
1193
|
+
return status;
|
|
1194
|
+
}
|
|
1195
|
+
function paidRpcError(error, chainId, rpcMethod, agentId) {
|
|
1196
|
+
const delay = rateLimitDelay(error);
|
|
1197
|
+
const providerRequestId = headerValue(error, "x-alchemy-request-id") ?? headerValue(error, "x-request-id");
|
|
1198
|
+
if (delay === void 0) {
|
|
1199
|
+
return new OwneyError(
|
|
1200
|
+
"AGENT_API_ERROR",
|
|
1201
|
+
"The blockchain RPC request failed.",
|
|
1202
|
+
{
|
|
1203
|
+
rpcSource: "paid-rpc",
|
|
1204
|
+
chainId,
|
|
1205
|
+
rpcMethod,
|
|
1206
|
+
...statusCode(error) ? { statusCode: statusCode(error) } : {},
|
|
1207
|
+
...providerRequestId ? { providerRequestId } : {}
|
|
1208
|
+
},
|
|
1209
|
+
agentId
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
const retryAt = Date.now() + delay;
|
|
1213
|
+
return new OwneyError(
|
|
1214
|
+
"AGENT_RATE_LIMITED",
|
|
1215
|
+
"The blockchain RPC is rate limited. Please wait before trying again.",
|
|
1216
|
+
{
|
|
1217
|
+
rpcSource: "paid-rpc",
|
|
1218
|
+
chainId,
|
|
1219
|
+
rpcMethod,
|
|
1220
|
+
statusCode: 429,
|
|
1221
|
+
retryAt,
|
|
1222
|
+
retryAfterSeconds: Math.max(0, Math.ceil(delay / 1e3)),
|
|
1223
|
+
...providerRequestId ? { providerRequestId } : {}
|
|
1224
|
+
},
|
|
1225
|
+
agentId
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
|
|
1229
|
+
try {
|
|
1230
|
+
return await operation();
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
throw paidRpcError(error, chainId, rpcMethod, agentId);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1066
1236
|
// src/agents/zyfai/zyfai.agent.ts
|
|
1067
1237
|
var ERC7579_IS_MODULE_INSTALLED_ABI = parseAbi([
|
|
1068
1238
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
1069
1239
|
]);
|
|
1070
|
-
var DEFAULT_ZYFAI_RPC_URLS = {
|
|
1071
|
-
8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
1072
|
-
42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
1073
|
-
1: "https://eth-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
|
|
1074
|
-
};
|
|
1075
1240
|
var WETH_ADDRESS_BY_CHAIN = {
|
|
1076
1241
|
8453: "0x4200000000000000000000000000000000000006",
|
|
1077
1242
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -1137,7 +1302,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1137
1302
|
earningsSnapshot = null;
|
|
1138
1303
|
earningsGeneration = 0;
|
|
1139
1304
|
constructor(apiKey, rpcUrls, referralSource) {
|
|
1140
|
-
this.rpcUrls = rpcUrls
|
|
1305
|
+
this.rpcUrls = resolveRpcUrls(rpcUrls);
|
|
1141
1306
|
this.sdk = new ZyfaiSDK({
|
|
1142
1307
|
apiKey,
|
|
1143
1308
|
rpcUrls: this.rpcUrls,
|
|
@@ -1151,10 +1316,10 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1151
1316
|
getPublicClient(chainId) {
|
|
1152
1317
|
const cached = this.publicClients.get(chainId);
|
|
1153
1318
|
if (cached) return cached;
|
|
1154
|
-
const client =
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1319
|
+
const client = createPaidRpcClient(
|
|
1320
|
+
VIEM_CHAIN[chainId],
|
|
1321
|
+
this.rpcUrls
|
|
1322
|
+
);
|
|
1158
1323
|
this.publicClients.set(chainId, client);
|
|
1159
1324
|
return client;
|
|
1160
1325
|
}
|
|
@@ -3191,6 +3356,9 @@ var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
|
3191
3356
|
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
3192
3357
|
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
3193
3358
|
var YIELDSEEKER_YIELD_OPTIONS_CACHE_MS = 6e4;
|
|
3359
|
+
var YIELDSEEKER_AGENT_LIST_CACHE_MS = 6e4;
|
|
3360
|
+
var YIELDSEEKER_PORTFOLIO_CACHE_MS = 3e4;
|
|
3361
|
+
var YIELDSEEKER_ACTIVITY_CACHE_MS = 6e4;
|
|
3194
3362
|
function generateYieldseekerUsername() {
|
|
3195
3363
|
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
3196
3364
|
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
@@ -3234,6 +3402,7 @@ function query(params) {
|
|
|
3234
3402
|
var YieldseekerAgent = class {
|
|
3235
3403
|
id = "yieldseeker";
|
|
3236
3404
|
balanceComposition = "tokens-plus-positions";
|
|
3405
|
+
withdrawalRequiresWalletApproval = true;
|
|
3237
3406
|
supportedChainIds = [8453];
|
|
3238
3407
|
supportedAssets = [
|
|
3239
3408
|
{
|
|
@@ -3247,11 +3416,17 @@ var YieldseekerAgent = class {
|
|
|
3247
3416
|
];
|
|
3248
3417
|
api;
|
|
3249
3418
|
auth;
|
|
3419
|
+
rpcUrls;
|
|
3420
|
+
receiptClient;
|
|
3250
3421
|
transactionExecutor;
|
|
3251
3422
|
unwindReceiptWaiter;
|
|
3252
3423
|
agentContexts = /* @__PURE__ */ new Map();
|
|
3253
3424
|
users = /* @__PURE__ */ new Map();
|
|
3254
3425
|
pendingAgents = /* @__PURE__ */ new Map();
|
|
3426
|
+
pendingWalletContexts = /* @__PURE__ */ new Map();
|
|
3427
|
+
readCache = /* @__PURE__ */ new Map();
|
|
3428
|
+
pendingReads = /* @__PURE__ */ new Map();
|
|
3429
|
+
readGeneration = 0;
|
|
3255
3430
|
yieldOptions = /* @__PURE__ */ new Map();
|
|
3256
3431
|
pendingYieldOptions = /* @__PURE__ */ new Map();
|
|
3257
3432
|
constructor(owneyApiKey, options = {}) {
|
|
@@ -3261,10 +3436,16 @@ var YieldseekerAgent = class {
|
|
|
3261
3436
|
options.fetchFn
|
|
3262
3437
|
);
|
|
3263
3438
|
this.auth = new YieldseekerAuth(options.auth);
|
|
3439
|
+
this.rpcUrls = options.rpcUrls;
|
|
3264
3440
|
this.transactionExecutor = options.transactionExecutor;
|
|
3265
3441
|
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
3266
3442
|
}
|
|
3443
|
+
getReceiptClient() {
|
|
3444
|
+
this.receiptClient ??= createPaidRpcClient(base3, this.rpcUrls);
|
|
3445
|
+
return this.receiptClient;
|
|
3446
|
+
}
|
|
3267
3447
|
async disconnect() {
|
|
3448
|
+
this.readGeneration += 1;
|
|
3268
3449
|
this.auth.clear();
|
|
3269
3450
|
for (const key2 of this.users.keys()) {
|
|
3270
3451
|
const [walletAddress, chainId] = key2.split(":");
|
|
@@ -3273,6 +3454,9 @@ var YieldseekerAgent = class {
|
|
|
3273
3454
|
this.users.clear();
|
|
3274
3455
|
this.agentContexts.clear();
|
|
3275
3456
|
this.pendingAgents.clear();
|
|
3457
|
+
this.pendingWalletContexts.clear();
|
|
3458
|
+
this.readCache.clear();
|
|
3459
|
+
this.pendingReads.clear();
|
|
3276
3460
|
}
|
|
3277
3461
|
async activateAgent(state, chainId, asset) {
|
|
3278
3462
|
this.assertChain(chainId);
|
|
@@ -3535,6 +3719,86 @@ var YieldseekerAgent = class {
|
|
|
3535
3719
|
contextKey(state, chainId, asset) {
|
|
3536
3720
|
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3537
3721
|
}
|
|
3722
|
+
cachedRead(key2, ttlMs, read2) {
|
|
3723
|
+
const cached = this.readCache.get(key2);
|
|
3724
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
3725
|
+
return Promise.resolve(cached.value);
|
|
3726
|
+
}
|
|
3727
|
+
const pending = this.pendingReads.get(key2);
|
|
3728
|
+
if (pending) return pending;
|
|
3729
|
+
const generation = this.readGeneration;
|
|
3730
|
+
const request = Promise.resolve().then(read2).then((value) => {
|
|
3731
|
+
if (this.readGeneration === generation) {
|
|
3732
|
+
this.readCache.set(key2, {
|
|
3733
|
+
expiresAt: Date.now() + ttlMs,
|
|
3734
|
+
value
|
|
3735
|
+
});
|
|
3736
|
+
}
|
|
3737
|
+
return value;
|
|
3738
|
+
}).finally(() => {
|
|
3739
|
+
if (this.pendingReads.get(key2) === request) {
|
|
3740
|
+
this.pendingReads.delete(key2);
|
|
3741
|
+
}
|
|
3742
|
+
});
|
|
3743
|
+
this.pendingReads.set(key2, request);
|
|
3744
|
+
return request;
|
|
3745
|
+
}
|
|
3746
|
+
agentListKey(state, chainId) {
|
|
3747
|
+
return `agents:${this.userKey(state, chainId)}`;
|
|
3748
|
+
}
|
|
3749
|
+
async listAgents(state, chainId, user) {
|
|
3750
|
+
const response = await this.cachedRead(
|
|
3751
|
+
this.agentListKey(state, chainId),
|
|
3752
|
+
YIELDSEEKER_AGENT_LIST_CACHE_MS,
|
|
3753
|
+
async () => {
|
|
3754
|
+
const response2 = await this.walletRequest(
|
|
3755
|
+
state,
|
|
3756
|
+
chainId,
|
|
3757
|
+
`/users/${user.userId}/agents`
|
|
3758
|
+
);
|
|
3759
|
+
if (!Array.isArray(response2?.agents)) {
|
|
3760
|
+
throw this.invalidResponse("agent list");
|
|
3761
|
+
}
|
|
3762
|
+
return response2;
|
|
3763
|
+
}
|
|
3764
|
+
);
|
|
3765
|
+
return response.agents;
|
|
3766
|
+
}
|
|
3767
|
+
async contextForAgent(state, chainId, user, agent, asset) {
|
|
3768
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3769
|
+
const cached = this.agentContexts.get(key2);
|
|
3770
|
+
if (cached?.agent.agentId === agent.agentId) return cached;
|
|
3771
|
+
const pending = this.pendingWalletContexts.get(key2);
|
|
3772
|
+
if (pending) return pending;
|
|
3773
|
+
const generation = this.readGeneration;
|
|
3774
|
+
const request = this.walletRequest(
|
|
3775
|
+
state,
|
|
3776
|
+
chainId,
|
|
3777
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3778
|
+
).then((walletResponse) => {
|
|
3779
|
+
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3780
|
+
throw this.invalidResponse("agent wallet");
|
|
3781
|
+
}
|
|
3782
|
+
const context = {
|
|
3783
|
+
user,
|
|
3784
|
+
agent,
|
|
3785
|
+
wallet: walletResponse.agentWallet,
|
|
3786
|
+
asset
|
|
3787
|
+
};
|
|
3788
|
+
if (this.readGeneration === generation) {
|
|
3789
|
+
this.agentContexts.set(key2, context);
|
|
3790
|
+
}
|
|
3791
|
+
return context;
|
|
3792
|
+
});
|
|
3793
|
+
this.pendingWalletContexts.set(key2, request);
|
|
3794
|
+
try {
|
|
3795
|
+
return await request;
|
|
3796
|
+
} finally {
|
|
3797
|
+
if (this.pendingWalletContexts.get(key2) === request) {
|
|
3798
|
+
this.pendingWalletContexts.delete(key2);
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3538
3802
|
async resolveUser(state, chainId) {
|
|
3539
3803
|
const key2 = this.userKey(state, chainId);
|
|
3540
3804
|
const inMemory = this.users.get(key2);
|
|
@@ -3631,16 +3895,9 @@ var YieldseekerAgent = class {
|
|
|
3631
3895
|
}
|
|
3632
3896
|
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3633
3897
|
const user = await this.resolveUser(state, chainId);
|
|
3634
|
-
const
|
|
3635
|
-
state,
|
|
3636
|
-
chainId,
|
|
3637
|
-
`/users/${user.userId}/agents`
|
|
3638
|
-
);
|
|
3639
|
-
if (!Array.isArray(response?.agents)) {
|
|
3640
|
-
throw this.invalidResponse("agent list");
|
|
3641
|
-
}
|
|
3898
|
+
const agents = await this.listAgents(state, chainId, user);
|
|
3642
3899
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3643
|
-
let agent =
|
|
3900
|
+
let agent = agents.find(
|
|
3644
3901
|
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3645
3902
|
);
|
|
3646
3903
|
if (!agent && createIfMissing) {
|
|
@@ -3661,83 +3918,91 @@ var YieldseekerAgent = class {
|
|
|
3661
3918
|
}
|
|
3662
3919
|
);
|
|
3663
3920
|
agent = created?.agent;
|
|
3921
|
+
if (agent) {
|
|
3922
|
+
this.readCache.set(this.agentListKey(state, chainId), {
|
|
3923
|
+
expiresAt: Date.now() + YIELDSEEKER_AGENT_LIST_CACHE_MS,
|
|
3924
|
+
value: {
|
|
3925
|
+
agents: [...agents, agent]
|
|
3926
|
+
}
|
|
3927
|
+
});
|
|
3928
|
+
}
|
|
3664
3929
|
}
|
|
3665
3930
|
if (!agent) return null;
|
|
3666
3931
|
this.assertAgent(agent);
|
|
3667
|
-
|
|
3668
|
-
state,
|
|
3669
|
-
chainId,
|
|
3670
|
-
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3671
|
-
);
|
|
3672
|
-
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3673
|
-
throw this.invalidResponse("agent wallet");
|
|
3674
|
-
}
|
|
3675
|
-
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3932
|
+
return this.contextForAgent(state, chainId, user, agent, asset);
|
|
3676
3933
|
}
|
|
3677
3934
|
async loadPortfolio(state, chainId, options) {
|
|
3678
3935
|
const user = await this.resolveUser(state, chainId);
|
|
3679
|
-
const
|
|
3680
|
-
state,
|
|
3681
|
-
chainId,
|
|
3682
|
-
`/users/${user.userId}/agents`
|
|
3683
|
-
);
|
|
3684
|
-
if (!Array.isArray(response?.agents)) {
|
|
3685
|
-
throw this.invalidResponse("agent list");
|
|
3686
|
-
}
|
|
3936
|
+
const agents = await this.listAgents(state, chainId, user);
|
|
3687
3937
|
const contexts = [];
|
|
3688
|
-
for (const agent of
|
|
3938
|
+
for (const agent of agents) {
|
|
3689
3939
|
const asset = this.assetForAgent(agent);
|
|
3690
3940
|
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3691
3941
|
continue;
|
|
3692
3942
|
}
|
|
3693
3943
|
this.assertAgent(agent);
|
|
3694
|
-
|
|
3695
|
-
state,
|
|
3696
|
-
chainId,
|
|
3697
|
-
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3698
|
-
);
|
|
3699
|
-
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3700
|
-
throw this.invalidResponse("agent wallet");
|
|
3701
|
-
}
|
|
3702
|
-
const context = {
|
|
3703
|
-
user,
|
|
3704
|
-
agent,
|
|
3705
|
-
wallet: walletResponse.agentWallet,
|
|
3706
|
-
asset
|
|
3707
|
-
};
|
|
3708
|
-
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3709
|
-
contexts.push(context);
|
|
3944
|
+
contexts.push(this.contextForAgent(state, chainId, user, agent, asset));
|
|
3710
3945
|
}
|
|
3946
|
+
const resolvedContexts = await Promise.all(contexts);
|
|
3711
3947
|
return Promise.all(
|
|
3712
|
-
|
|
3948
|
+
resolvedContexts.map(
|
|
3713
3949
|
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
3714
3950
|
)
|
|
3715
3951
|
);
|
|
3716
3952
|
}
|
|
3717
3953
|
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3954
|
+
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
3718
3955
|
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3719
|
-
this.
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3956
|
+
this.cachedRead(
|
|
3957
|
+
`snapshot:${contextKey}`,
|
|
3958
|
+
YIELDSEEKER_PORTFOLIO_CACHE_MS,
|
|
3959
|
+
async () => {
|
|
3960
|
+
const response = await this.walletRequest(
|
|
3961
|
+
state,
|
|
3962
|
+
chainId,
|
|
3963
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3964
|
+
shouldOnlyUseRecentValue: true,
|
|
3965
|
+
shouldAllowStaleOnError: true
|
|
3966
|
+
})}`
|
|
3967
|
+
);
|
|
3968
|
+
if (!response?.agentSnapshot) {
|
|
3969
|
+
throw this.invalidResponse("agent snapshot");
|
|
3970
|
+
}
|
|
3971
|
+
return response;
|
|
3972
|
+
}
|
|
3726
3973
|
),
|
|
3727
|
-
this.
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3974
|
+
this.cachedRead(
|
|
3975
|
+
`positions:${contextKey}`,
|
|
3976
|
+
YIELDSEEKER_PORTFOLIO_CACHE_MS,
|
|
3977
|
+
async () => {
|
|
3978
|
+
const response = await this.walletRequest(
|
|
3979
|
+
state,
|
|
3980
|
+
chainId,
|
|
3981
|
+
this.agentPath(context, "yield-positions")
|
|
3982
|
+
);
|
|
3983
|
+
if (!Array.isArray(response?.yieldPositions)) {
|
|
3984
|
+
throw this.invalidResponse("yield positions");
|
|
3985
|
+
}
|
|
3986
|
+
return response;
|
|
3987
|
+
}
|
|
3731
3988
|
),
|
|
3732
|
-
options.historic ? this.
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
this.
|
|
3989
|
+
options.historic ? this.cachedRead(
|
|
3990
|
+
`historic:${contextKey}`,
|
|
3991
|
+
YIELDSEEKER_ACTIVITY_CACHE_MS,
|
|
3992
|
+
() => this.walletRequest(
|
|
3993
|
+
state,
|
|
3994
|
+
chainId,
|
|
3995
|
+
this.agentPath(context, "wallet/historic-position")
|
|
3996
|
+
)
|
|
3736
3997
|
) : Promise.resolve(void 0),
|
|
3737
|
-
options.actions ? this.
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
this.
|
|
3998
|
+
options.actions ? this.cachedRead(
|
|
3999
|
+
`actions:${contextKey}`,
|
|
4000
|
+
YIELDSEEKER_ACTIVITY_CACHE_MS,
|
|
4001
|
+
() => this.walletRequest(
|
|
4002
|
+
state,
|
|
4003
|
+
chainId,
|
|
4004
|
+
this.agentPath(context, "actions")
|
|
4005
|
+
)
|
|
3741
4006
|
) : Promise.resolve(void 0)
|
|
3742
4007
|
]);
|
|
3743
4008
|
if (!snapshot?.agentSnapshot) {
|
|
@@ -3771,6 +4036,13 @@ var YieldseekerAgent = class {
|
|
|
3771
4036
|
context.wallet = deployed.agentWallet;
|
|
3772
4037
|
}
|
|
3773
4038
|
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
4039
|
+
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
4040
|
+
const invalidatePortfolio = () => {
|
|
4041
|
+
for (const kind of ["snapshot", "positions", "historic", "actions"]) {
|
|
4042
|
+
this.readCache.delete(`${kind}:${contextKey}`);
|
|
4043
|
+
}
|
|
4044
|
+
};
|
|
4045
|
+
invalidatePortfolio();
|
|
3774
4046
|
try {
|
|
3775
4047
|
const response = await this.walletRequest(
|
|
3776
4048
|
state,
|
|
@@ -3787,6 +4059,8 @@ var YieldseekerAgent = class {
|
|
|
3787
4059
|
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3788
4060
|
error
|
|
3789
4061
|
);
|
|
4062
|
+
} finally {
|
|
4063
|
+
invalidatePortfolio();
|
|
3790
4064
|
}
|
|
3791
4065
|
}
|
|
3792
4066
|
agentPath(context, suffix) {
|
|
@@ -3837,6 +4111,7 @@ var YieldseekerAgent = class {
|
|
|
3837
4111
|
code,
|
|
3838
4112
|
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3839
4113
|
{
|
|
4114
|
+
rpcSource: "agent-api",
|
|
3840
4115
|
statusCode: error.status,
|
|
3841
4116
|
providerCode: error.providerCode,
|
|
3842
4117
|
...error.responseFields ? { fields: error.responseFields } : {}
|
|
@@ -3855,12 +4130,12 @@ var YieldseekerAgent = class {
|
|
|
3855
4130
|
chain: base3,
|
|
3856
4131
|
transport: custom2(state.provider)
|
|
3857
4132
|
});
|
|
3858
|
-
const
|
|
4133
|
+
const walletChainClient = createPublicClient3({
|
|
3859
4134
|
chain: base3,
|
|
3860
4135
|
transport: custom2(state.provider)
|
|
3861
4136
|
});
|
|
3862
4137
|
await ensureWalletOnChain(
|
|
3863
|
-
|
|
4138
|
+
walletChainClient,
|
|
3864
4139
|
walletClient,
|
|
3865
4140
|
8453
|
|
3866
4141
|
);
|
|
@@ -3871,10 +4146,15 @@ var YieldseekerAgent = class {
|
|
|
3871
4146
|
data: transaction.data,
|
|
3872
4147
|
value: BigInt(transaction.value)
|
|
3873
4148
|
});
|
|
3874
|
-
const receipt = await
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
4149
|
+
const receipt = await withPaidRpcDiagnostics(
|
|
4150
|
+
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
4151
|
+
hash,
|
|
4152
|
+
confirmations: 1
|
|
4153
|
+
}),
|
|
4154
|
+
8453,
|
|
4155
|
+
"eth_getTransactionReceipt",
|
|
4156
|
+
this.id
|
|
4157
|
+
);
|
|
3878
4158
|
if (receipt.status !== "success") {
|
|
3879
4159
|
throw new OwneyError(
|
|
3880
4160
|
"AGENT_TRANSACTION_REVERTED",
|
|
@@ -3890,14 +4170,15 @@ var YieldseekerAgent = class {
|
|
|
3890
4170
|
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3891
4171
|
return;
|
|
3892
4172
|
}
|
|
3893
|
-
const
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
4173
|
+
const receipt = await withPaidRpcDiagnostics(
|
|
4174
|
+
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
4175
|
+
hash: transactionHash,
|
|
4176
|
+
confirmations: 1
|
|
4177
|
+
}),
|
|
4178
|
+
8453,
|
|
4179
|
+
"eth_getTransactionReceipt",
|
|
4180
|
+
this.id
|
|
4181
|
+
);
|
|
3901
4182
|
if (receipt.status !== "success") {
|
|
3902
4183
|
throw new OwneyError(
|
|
3903
4184
|
"AGENT_TRANSACTION_REVERTED",
|
|
@@ -4269,6 +4550,41 @@ import {
|
|
|
4269
4550
|
// src/lib/permit2-batch.ts
|
|
4270
4551
|
import { parseAbi as parseAbi2, hashStruct } from "viem";
|
|
4271
4552
|
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
4553
|
+
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
4554
|
+
var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
4555
|
+
var ERC2612_PERMIT_TYPES = {
|
|
4556
|
+
Permit: [
|
|
4557
|
+
{ name: "owner", type: "address" },
|
|
4558
|
+
{ name: "spender", type: "address" },
|
|
4559
|
+
{ name: "value", type: "uint256" },
|
|
4560
|
+
{ name: "nonce", type: "uint256" },
|
|
4561
|
+
{ name: "deadline", type: "uint256" }
|
|
4562
|
+
]
|
|
4563
|
+
};
|
|
4564
|
+
var ERC2612_READ_ABI = parseAbi2([
|
|
4565
|
+
"function name() view returns (string)",
|
|
4566
|
+
"function version() view returns (string)",
|
|
4567
|
+
"function nonces(address owner) view returns (uint256)"
|
|
4568
|
+
]);
|
|
4569
|
+
function erc2612TypedData(input) {
|
|
4570
|
+
return {
|
|
4571
|
+
domain: {
|
|
4572
|
+
name: input.permit.tokenName,
|
|
4573
|
+
version: input.permit.tokenVersion,
|
|
4574
|
+
chainId: input.chainId,
|
|
4575
|
+
verifyingContract: input.token
|
|
4576
|
+
},
|
|
4577
|
+
types: ERC2612_PERMIT_TYPES,
|
|
4578
|
+
primaryType: "Permit",
|
|
4579
|
+
message: {
|
|
4580
|
+
owner: input.owner,
|
|
4581
|
+
spender: BATCH_PERMIT2_ADDRESS,
|
|
4582
|
+
value: BigInt(input.permit.value),
|
|
4583
|
+
nonce: BigInt(input.permit.nonce),
|
|
4584
|
+
deadline: BigInt(input.permit.deadline)
|
|
4585
|
+
}
|
|
4586
|
+
};
|
|
4587
|
+
}
|
|
4272
4588
|
var PERMIT_BATCH_TYPES = {
|
|
4273
4589
|
PermitBatchWitnessTransferFrom: [
|
|
4274
4590
|
{ name: "permitted", type: "TokenPermissions[]" },
|
|
@@ -4420,28 +4736,67 @@ async function execute(i, key2, plan) {
|
|
|
4420
4736
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
4421
4737
|
"Insufficient token balance for this deposit."
|
|
4422
4738
|
);
|
|
4423
|
-
if (allowance < total)
|
|
4739
|
+
if (allowance < total && (i.chainId !== 8453 || !isAddressEqual(i.token, BASE_USDC_ADDRESS)))
|
|
4424
4740
|
throw new OwneyError(
|
|
4425
4741
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
4426
4742
|
"token deposits need a one-time Permit2 approval."
|
|
4427
4743
|
);
|
|
4428
|
-
const
|
|
4744
|
+
const now = (await i.pub.getBlock()).timestamp;
|
|
4745
|
+
let erc2612Permit;
|
|
4746
|
+
if (allowance < total) {
|
|
4747
|
+
const [tokenName, tokenVersion, permitNonce] = await Promise.all([
|
|
4748
|
+
i.pub.readContract({
|
|
4749
|
+
address: i.token,
|
|
4750
|
+
abi: ERC2612_READ_ABI,
|
|
4751
|
+
functionName: "name"
|
|
4752
|
+
}),
|
|
4753
|
+
i.pub.readContract({
|
|
4754
|
+
address: i.token,
|
|
4755
|
+
abi: ERC2612_READ_ABI,
|
|
4756
|
+
functionName: "version"
|
|
4757
|
+
}),
|
|
4758
|
+
i.pub.readContract({
|
|
4759
|
+
address: i.token,
|
|
4760
|
+
abi: ERC2612_READ_ABI,
|
|
4761
|
+
functionName: "nonces",
|
|
4762
|
+
args: [i.owner]
|
|
4763
|
+
})
|
|
4764
|
+
]);
|
|
4765
|
+
const unsignedPermit = {
|
|
4766
|
+
value: MAX_UINT256.toString(),
|
|
4767
|
+
nonce: permitNonce.toString(),
|
|
4768
|
+
deadline: (now + 900n).toString(),
|
|
4769
|
+
tokenName,
|
|
4770
|
+
tokenVersion
|
|
4771
|
+
};
|
|
4772
|
+
const signature2 = await i.wallet.signTypedData({
|
|
4773
|
+
account: i.owner,
|
|
4774
|
+
...erc2612TypedData({
|
|
4775
|
+
chainId: 8453,
|
|
4776
|
+
token: i.token,
|
|
4777
|
+
owner: i.owner,
|
|
4778
|
+
permit: unsignedPermit
|
|
4779
|
+
})
|
|
4780
|
+
});
|
|
4781
|
+
erc2612Permit = { ...unsignedPermit, signature: signature2 };
|
|
4782
|
+
}
|
|
4783
|
+
const spender = erc2612Permit ? MULTICALL3_ADDRESS : await getSponsorRelayerAddress({
|
|
4429
4784
|
apiKey: i.apiKey,
|
|
4430
4785
|
baseUrl: i.baseUrl,
|
|
4431
4786
|
chainId: i.chainId
|
|
4432
4787
|
});
|
|
4433
|
-
const now = (await i.pub.getBlock()).timestamp;
|
|
4434
4788
|
const unsigned = {
|
|
4435
4789
|
chainId: i.chainId,
|
|
4436
4790
|
token: i.token,
|
|
4437
4791
|
from: i.owner,
|
|
4438
4792
|
transfers: i.transfers,
|
|
4439
4793
|
nonce: randomPermit2Nonce().toString(),
|
|
4440
|
-
deadline: (now + 900n).toString()
|
|
4794
|
+
deadline: (now + 900n).toString(),
|
|
4795
|
+
...erc2612Permit ? { erc2612Permit } : {}
|
|
4441
4796
|
};
|
|
4442
4797
|
const signature = await i.wallet.signTypedData({
|
|
4443
4798
|
account: i.owner,
|
|
4444
|
-
...batchTypedData(unsigned,
|
|
4799
|
+
...batchTypedData(unsigned, spender)
|
|
4445
4800
|
});
|
|
4446
4801
|
i.onApproved?.();
|
|
4447
4802
|
return send({ ...unsigned, signature });
|
|
@@ -4461,8 +4816,8 @@ function makeSponsoredTokenCallback(deps) {
|
|
|
4461
4816
|
"CHAIN_UNSUPPORTED",
|
|
4462
4817
|
`No sponsored token configured for chain ${chainId}`
|
|
4463
4818
|
);
|
|
4464
|
-
const pub = deps.getPublicClient(chainId), wallet = deps.getWalletClient(chainId);
|
|
4465
|
-
await ensureWalletOnChain(
|
|
4819
|
+
const pub = deps.getPublicClient(chainId), walletChain = deps.getWalletChainClient?.(chainId) ?? pub, wallet = deps.getWalletClient(chainId);
|
|
4820
|
+
await ensureWalletOnChain(walletChain, wallet, chainId);
|
|
4466
4821
|
return sponsorTokenBatch({
|
|
4467
4822
|
apiKey: deps.apiKey,
|
|
4468
4823
|
baseUrl: deps.baseUrl,
|
|
@@ -4764,6 +5119,7 @@ var OwneySDK = class {
|
|
|
4764
5119
|
// leave every user's agent profile alone.
|
|
4765
5120
|
orgAgentConfig;
|
|
4766
5121
|
orgAgentConfigPromise = null;
|
|
5122
|
+
rpcUrls;
|
|
4767
5123
|
zyfaiRpcUrls;
|
|
4768
5124
|
yieldseekerApiBaseUrl;
|
|
4769
5125
|
yieldseekerSiweOrigin;
|
|
@@ -4788,7 +5144,12 @@ var OwneySDK = class {
|
|
|
4788
5144
|
constructor(config) {
|
|
4789
5145
|
this.apiKey = config.apiKey;
|
|
4790
5146
|
if (config.debug) setOwneyDebug(true);
|
|
4791
|
-
this.
|
|
5147
|
+
this.rpcUrls = resolveRpcUrls(
|
|
5148
|
+
config.rpcUrls,
|
|
5149
|
+
config.apiKey,
|
|
5150
|
+
config.routingApiBaseUrl
|
|
5151
|
+
);
|
|
5152
|
+
this.zyfaiRpcUrls = config.rpcUrls ? void 0 : config.zyfaiRpcUrls;
|
|
4792
5153
|
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
4793
5154
|
this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
|
|
4794
5155
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
@@ -4878,6 +5239,9 @@ var OwneySDK = class {
|
|
|
4878
5239
|
}
|
|
4879
5240
|
return this.state.provider;
|
|
4880
5241
|
}
|
|
5242
|
+
getPaidRpcClient(chainId) {
|
|
5243
|
+
return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
|
|
5244
|
+
}
|
|
4881
5245
|
/** Builds the default USDC batch callback for the connected wallet. */
|
|
4882
5246
|
getDefaultSponsoredCallback(onApproved) {
|
|
4883
5247
|
if (!onApproved && this.cachedSponsoredCallback)
|
|
@@ -4893,7 +5257,8 @@ var OwneySDK = class {
|
|
|
4893
5257
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
4894
5258
|
// PublicClient/WalletClient param types — structurally identical at
|
|
4895
5259
|
// runtime, but the two share a name TS treats as unrelated.
|
|
4896
|
-
getPublicClient: (cid) =>
|
|
5260
|
+
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
5261
|
+
getWalletChainClient: (cid) => createPublicClient4({
|
|
4897
5262
|
chain: VIEM_CHAIN2[cid],
|
|
4898
5263
|
transport: custom3(provider)
|
|
4899
5264
|
}),
|
|
@@ -4943,7 +5308,8 @@ var OwneySDK = class {
|
|
|
4943
5308
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
4944
5309
|
// PublicClient/WalletClient param types — structurally identical at
|
|
4945
5310
|
// runtime, but the two share a name TS treats as unrelated.
|
|
4946
|
-
getPublicClient: (cid) =>
|
|
5311
|
+
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
5312
|
+
getWalletChainClient: (cid) => createPublicClient4({
|
|
4947
5313
|
chain: VIEM_CHAIN2[cid],
|
|
4948
5314
|
transport: custom3(provider)
|
|
4949
5315
|
}),
|
|
@@ -5056,12 +5422,21 @@ var OwneySDK = class {
|
|
|
5056
5422
|
createAgent(agentId, key2) {
|
|
5057
5423
|
if (agentId === "zyfai") {
|
|
5058
5424
|
if (!key2) return null;
|
|
5059
|
-
return new ZyfaiAgent(
|
|
5425
|
+
return new ZyfaiAgent(
|
|
5426
|
+
key2,
|
|
5427
|
+
this.zyfaiRpcUrls ? resolveRpcUrls(
|
|
5428
|
+
this.zyfaiRpcUrls,
|
|
5429
|
+
this.apiKey,
|
|
5430
|
+
this.routingApiBaseUrl
|
|
5431
|
+
) : this.rpcUrls,
|
|
5432
|
+
this.referralSource
|
|
5433
|
+
);
|
|
5060
5434
|
}
|
|
5061
5435
|
if (agentId === "yieldseeker") {
|
|
5062
5436
|
return new YieldseekerAgent(this.apiKey, {
|
|
5063
5437
|
auth: { origin: this.yieldseekerSiweOrigin },
|
|
5064
|
-
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
5438
|
+
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl),
|
|
5439
|
+
rpcUrls: this.rpcUrls
|
|
5065
5440
|
});
|
|
5066
5441
|
}
|
|
5067
5442
|
return null;
|
|
@@ -5397,10 +5772,12 @@ var OwneySDK = class {
|
|
|
5397
5772
|
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
5398
5773
|
* two independent auto-recovery mechanisms:
|
|
5399
5774
|
*
|
|
5400
|
-
* 1. Missing Permit2 allowance
|
|
5401
|
-
* callback and the attempt fails with
|
|
5402
|
-
*
|
|
5403
|
-
*
|
|
5775
|
+
* 1. Missing Permit2 allowance outside the atomic Base-USDC path: when the
|
|
5776
|
+
* app did not supply its own callback and the attempt fails with
|
|
5777
|
+
* `PERMIT2_APPROVAL_REQUIRED`, this is the wallet's first Permit2 deposit
|
|
5778
|
+
* for that token. Base USDC bundles a gasless ERC-2612 approval inside
|
|
5779
|
+
* its sponsored deposit and never reaches this branch. Other tokens send
|
|
5780
|
+
* the one-time user-paid Permit2 approval via `approvePermit2()` and
|
|
5404
5781
|
* retry the SAME sponsored attempt once. Bounded to one approval attempt
|
|
5405
5782
|
* per call so a wallet/agent that keeps reporting the allowance as
|
|
5406
5783
|
* missing can't loop forever. If `approvePermit2()` itself throws (e.g.
|
|
@@ -5591,6 +5968,32 @@ var OwneySDK = class {
|
|
|
5591
5968
|
}
|
|
5592
5969
|
return eligible;
|
|
5593
5970
|
}
|
|
5971
|
+
/**
|
|
5972
|
+
* Run owner-approved withdrawals before relayer-only withdrawals. Wallet
|
|
5973
|
+
* approval is the only point at which the user can cancel the aggregate
|
|
5974
|
+
* operation, so no relayer leg should commit before it has completed.
|
|
5975
|
+
*/
|
|
5976
|
+
orderAgentsForWithdrawal(agents) {
|
|
5977
|
+
return agents.map((agent, index) => ({ agent, index })).sort((left, right) => {
|
|
5978
|
+
const approvalOrder = Number(Boolean(right.agent.withdrawalRequiresWalletApproval)) - Number(Boolean(left.agent.withdrawalRequiresWalletApproval));
|
|
5979
|
+
return approvalOrder || left.index - right.index;
|
|
5980
|
+
}).map(({ agent }) => agent);
|
|
5981
|
+
}
|
|
5982
|
+
isUserRejectedWithdrawal(error) {
|
|
5983
|
+
let current = error;
|
|
5984
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5985
|
+
while (current && typeof current === "object" && !seen.has(current)) {
|
|
5986
|
+
seen.add(current);
|
|
5987
|
+
const candidate = current;
|
|
5988
|
+
if (candidate.code === 4001 || candidate.code === "4001") return true;
|
|
5989
|
+
const message = [candidate.message, candidate.shortMessage].filter((value) => typeof value === "string").join(" ");
|
|
5990
|
+
if (/user (?:rejected|denied)|rejected by user/i.test(message)) {
|
|
5991
|
+
return true;
|
|
5992
|
+
}
|
|
5993
|
+
current = candidate.cause;
|
|
5994
|
+
}
|
|
5995
|
+
return false;
|
|
5996
|
+
}
|
|
5594
5997
|
// --- Fund operations ---
|
|
5595
5998
|
/**
|
|
5596
5999
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -5626,13 +6029,15 @@ var OwneySDK = class {
|
|
|
5626
6029
|
);
|
|
5627
6030
|
}
|
|
5628
6031
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
6032
|
+
const withdrawalAgents = this.orderAgentsForWithdrawal(eligibleAgents);
|
|
5629
6033
|
if (!amount) {
|
|
5630
6034
|
const results2 = {};
|
|
5631
6035
|
const agentErrors2 = {};
|
|
5632
|
-
for (const agent of
|
|
6036
|
+
for (const agent of withdrawalAgents) {
|
|
5633
6037
|
try {
|
|
5634
6038
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
5635
6039
|
} catch (err) {
|
|
6040
|
+
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
5636
6041
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
5637
6042
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
5638
6043
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
@@ -5713,12 +6118,13 @@ var OwneySDK = class {
|
|
|
5713
6118
|
planned: 0n
|
|
5714
6119
|
}));
|
|
5715
6120
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
6121
|
+
const orderedPlans = this.orderAgentsForWithdrawal(plans.map((p) => p.agent)).map((agent) => plans.find((plan) => plan.agent === agent));
|
|
5716
6122
|
const results = {};
|
|
5717
6123
|
const agentErrors = {
|
|
5718
6124
|
...aggregated.agentErrors ?? {}
|
|
5719
6125
|
};
|
|
5720
|
-
for (let i = 0; i <
|
|
5721
|
-
const p =
|
|
6126
|
+
for (let i = 0; i < orderedPlans.length; i++) {
|
|
6127
|
+
const p = orderedPlans[i];
|
|
5722
6128
|
if (p.planned === 0n) continue;
|
|
5723
6129
|
try {
|
|
5724
6130
|
results[p.agent.id] = await p.agent.withdraw(
|
|
@@ -5728,6 +6134,7 @@ var OwneySDK = class {
|
|
|
5728
6134
|
p.planned.toString()
|
|
5729
6135
|
);
|
|
5730
6136
|
} catch (err) {
|
|
6137
|
+
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
5731
6138
|
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
5732
6139
|
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
5733
6140
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
@@ -5739,7 +6146,7 @@ var OwneySDK = class {
|
|
|
5739
6146
|
);
|
|
5740
6147
|
const failedAmount = p.planned;
|
|
5741
6148
|
p.planned = 0n;
|
|
5742
|
-
redistributeShare(
|
|
6149
|
+
redistributeShare(orderedPlans, i, failedAmount);
|
|
5743
6150
|
}
|
|
5744
6151
|
}
|
|
5745
6152
|
if (Object.keys(results).length === 0) {
|
|
@@ -6186,17 +6593,18 @@ var OwneySDK = class {
|
|
|
6186
6593
|
);
|
|
6187
6594
|
}
|
|
6188
6595
|
const provider = this.requireConnectedProvider();
|
|
6189
|
-
const
|
|
6596
|
+
const walletChainClient = createPublicClient4({
|
|
6190
6597
|
chain: VIEM_CHAIN2[chainId],
|
|
6191
6598
|
transport: custom3(provider)
|
|
6192
6599
|
});
|
|
6600
|
+
const publicClient = this.getPaidRpcClient(chainId);
|
|
6193
6601
|
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
6194
6602
|
const wallet = createWalletClient3({
|
|
6195
6603
|
account: state.walletAddress,
|
|
6196
6604
|
chain: VIEM_CHAIN2[chainId],
|
|
6197
6605
|
transport: custom3(provider)
|
|
6198
6606
|
});
|
|
6199
|
-
await ensureWalletOnChain(
|
|
6607
|
+
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
6200
6608
|
const hash = await wallet.writeContract({
|
|
6201
6609
|
address: token,
|
|
6202
6610
|
abi: ERC20_ALLOWANCE_ABI,
|
|
@@ -6205,10 +6613,14 @@ var OwneySDK = class {
|
|
|
6205
6613
|
account: state.walletAddress,
|
|
6206
6614
|
chain: VIEM_CHAIN2[chainId]
|
|
6207
6615
|
});
|
|
6208
|
-
const receipt = await
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6616
|
+
const receipt = await withPaidRpcDiagnostics(
|
|
6617
|
+
() => publicClient.waitForTransactionReceipt({
|
|
6618
|
+
hash,
|
|
6619
|
+
confirmations: 1
|
|
6620
|
+
}),
|
|
6621
|
+
chainId,
|
|
6622
|
+
"eth_getTransactionReceipt"
|
|
6623
|
+
);
|
|
6212
6624
|
if (receipt.status !== "success") {
|
|
6213
6625
|
throw new Error(`Permit2 approval reverted (tx ${hash})`);
|
|
6214
6626
|
}
|