@owney/sdk 0.7.25-beta.4 → 0.7.25-beta.6
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 +72 -13
- package/dist/index.cjs +564 -180
- package/dist/index.d.cts +47 -6
- package/dist/index.d.ts +47 -6
- package/dist/index.js +505 -118
- 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,131 @@ 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
|
+
var DEFAULT_RPC_URLS = {
|
|
1106
|
+
1: "https://eth-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
1107
|
+
8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
1108
|
+
42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
|
|
1109
|
+
};
|
|
1110
|
+
function resolveRpcUrl(rpcUrls, chainId) {
|
|
1111
|
+
const url = rpcUrls?.[chainId]?.trim();
|
|
1112
|
+
if (url) return url;
|
|
1113
|
+
return DEFAULT_RPC_URLS[chainId];
|
|
1114
|
+
}
|
|
1115
|
+
function resolveRpcUrls(rpcUrls) {
|
|
1116
|
+
return {
|
|
1117
|
+
1: resolveRpcUrl(rpcUrls, 1),
|
|
1118
|
+
8453: resolveRpcUrl(rpcUrls, 8453),
|
|
1119
|
+
42161: resolveRpcUrl(rpcUrls, 42161)
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
function createPaidRpcClient(chain, rpcUrls) {
|
|
1123
|
+
const url = resolveRpcUrl(
|
|
1124
|
+
rpcUrls,
|
|
1125
|
+
chain.id
|
|
1126
|
+
);
|
|
1127
|
+
return createPublicClient({
|
|
1128
|
+
chain,
|
|
1129
|
+
transport: http(url, {
|
|
1130
|
+
retryCount: PAID_RPC_RETRY_COUNT,
|
|
1131
|
+
retryDelay: PAID_RPC_RETRY_DELAY_MS
|
|
1132
|
+
})
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
function headerValue(error, name) {
|
|
1136
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1137
|
+
let value;
|
|
1138
|
+
function visit(candidate, depth = 0) {
|
|
1139
|
+
if (value || depth > 6 || !candidate || typeof candidate !== "object")
|
|
1140
|
+
return;
|
|
1141
|
+
if (seen.has(candidate)) return;
|
|
1142
|
+
seen.add(candidate);
|
|
1143
|
+
const record = candidate;
|
|
1144
|
+
const headers = record.headers;
|
|
1145
|
+
const found = typeof headers?.get === "function" ? headers.get(name) : headers?.[name] ?? headers?.[name.toLowerCase()];
|
|
1146
|
+
if (typeof found === "string" && found) {
|
|
1147
|
+
value = found;
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
for (const key2 of ["cause", "details", "response", "error"])
|
|
1151
|
+
visit(record[key2], depth + 1);
|
|
1152
|
+
}
|
|
1153
|
+
visit(error);
|
|
1154
|
+
return value;
|
|
1155
|
+
}
|
|
1156
|
+
function statusCode(error) {
|
|
1157
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1158
|
+
let status;
|
|
1159
|
+
function visit(candidate, depth = 0) {
|
|
1160
|
+
if (status || depth > 6 || !candidate || typeof candidate !== "object")
|
|
1161
|
+
return;
|
|
1162
|
+
if (seen.has(candidate)) return;
|
|
1163
|
+
seen.add(candidate);
|
|
1164
|
+
const record = candidate;
|
|
1165
|
+
for (const key2 of ["status", "statusCode"]) {
|
|
1166
|
+
const parsed = Number(record[key2]);
|
|
1167
|
+
if (Number.isInteger(parsed) && parsed >= 100 && parsed <= 599) {
|
|
1168
|
+
status = parsed;
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
for (const key2 of ["cause", "details", "response", "error"])
|
|
1173
|
+
visit(record[key2], depth + 1);
|
|
1174
|
+
}
|
|
1175
|
+
visit(error);
|
|
1176
|
+
return status;
|
|
1177
|
+
}
|
|
1178
|
+
function paidRpcError(error, chainId, rpcMethod, agentId) {
|
|
1179
|
+
const delay = rateLimitDelay(error);
|
|
1180
|
+
const providerRequestId = headerValue(error, "x-alchemy-request-id") ?? headerValue(error, "x-request-id");
|
|
1181
|
+
if (delay === void 0) {
|
|
1182
|
+
return new OwneyError(
|
|
1183
|
+
"AGENT_API_ERROR",
|
|
1184
|
+
"The blockchain RPC request failed.",
|
|
1185
|
+
{
|
|
1186
|
+
rpcSource: "paid-rpc",
|
|
1187
|
+
chainId,
|
|
1188
|
+
rpcMethod,
|
|
1189
|
+
...statusCode(error) ? { statusCode: statusCode(error) } : {},
|
|
1190
|
+
...providerRequestId ? { providerRequestId } : {}
|
|
1191
|
+
},
|
|
1192
|
+
agentId
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
const retryAt = Date.now() + delay;
|
|
1196
|
+
return new OwneyError(
|
|
1197
|
+
"AGENT_RATE_LIMITED",
|
|
1198
|
+
"The blockchain RPC is rate limited. Please wait before trying again.",
|
|
1199
|
+
{
|
|
1200
|
+
rpcSource: "paid-rpc",
|
|
1201
|
+
chainId,
|
|
1202
|
+
rpcMethod,
|
|
1203
|
+
statusCode: 429,
|
|
1204
|
+
retryAt,
|
|
1205
|
+
retryAfterSeconds: Math.max(0, Math.ceil(delay / 1e3)),
|
|
1206
|
+
...providerRequestId ? { providerRequestId } : {}
|
|
1207
|
+
},
|
|
1208
|
+
agentId
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
|
|
1212
|
+
try {
|
|
1213
|
+
return await operation();
|
|
1214
|
+
} catch (error) {
|
|
1215
|
+
throw paidRpcError(error, chainId, rpcMethod, agentId);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1066
1219
|
// src/agents/zyfai/zyfai.agent.ts
|
|
1067
1220
|
var ERC7579_IS_MODULE_INSTALLED_ABI = parseAbi([
|
|
1068
1221
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
1069
1222
|
]);
|
|
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
1223
|
var WETH_ADDRESS_BY_CHAIN = {
|
|
1076
1224
|
8453: "0x4200000000000000000000000000000000000006",
|
|
1077
1225
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -1137,7 +1285,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1137
1285
|
earningsSnapshot = null;
|
|
1138
1286
|
earningsGeneration = 0;
|
|
1139
1287
|
constructor(apiKey, rpcUrls, referralSource) {
|
|
1140
|
-
this.rpcUrls = rpcUrls
|
|
1288
|
+
this.rpcUrls = resolveRpcUrls(rpcUrls);
|
|
1141
1289
|
this.sdk = new ZyfaiSDK({
|
|
1142
1290
|
apiKey,
|
|
1143
1291
|
rpcUrls: this.rpcUrls,
|
|
@@ -1151,10 +1299,10 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1151
1299
|
getPublicClient(chainId) {
|
|
1152
1300
|
const cached = this.publicClients.get(chainId);
|
|
1153
1301
|
if (cached) return cached;
|
|
1154
|
-
const client =
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1302
|
+
const client = createPaidRpcClient(
|
|
1303
|
+
VIEM_CHAIN[chainId],
|
|
1304
|
+
this.rpcUrls
|
|
1305
|
+
);
|
|
1158
1306
|
this.publicClients.set(chainId, client);
|
|
1159
1307
|
return client;
|
|
1160
1308
|
}
|
|
@@ -3191,6 +3339,9 @@ var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
|
3191
3339
|
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
3192
3340
|
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
3193
3341
|
var YIELDSEEKER_YIELD_OPTIONS_CACHE_MS = 6e4;
|
|
3342
|
+
var YIELDSEEKER_AGENT_LIST_CACHE_MS = 6e4;
|
|
3343
|
+
var YIELDSEEKER_PORTFOLIO_CACHE_MS = 3e4;
|
|
3344
|
+
var YIELDSEEKER_ACTIVITY_CACHE_MS = 6e4;
|
|
3194
3345
|
function generateYieldseekerUsername() {
|
|
3195
3346
|
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
3196
3347
|
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
@@ -3234,6 +3385,7 @@ function query(params) {
|
|
|
3234
3385
|
var YieldseekerAgent = class {
|
|
3235
3386
|
id = "yieldseeker";
|
|
3236
3387
|
balanceComposition = "tokens-plus-positions";
|
|
3388
|
+
withdrawalRequiresWalletApproval = true;
|
|
3237
3389
|
supportedChainIds = [8453];
|
|
3238
3390
|
supportedAssets = [
|
|
3239
3391
|
{
|
|
@@ -3247,11 +3399,17 @@ var YieldseekerAgent = class {
|
|
|
3247
3399
|
];
|
|
3248
3400
|
api;
|
|
3249
3401
|
auth;
|
|
3402
|
+
rpcUrls;
|
|
3403
|
+
receiptClient;
|
|
3250
3404
|
transactionExecutor;
|
|
3251
3405
|
unwindReceiptWaiter;
|
|
3252
3406
|
agentContexts = /* @__PURE__ */ new Map();
|
|
3253
3407
|
users = /* @__PURE__ */ new Map();
|
|
3254
3408
|
pendingAgents = /* @__PURE__ */ new Map();
|
|
3409
|
+
pendingWalletContexts = /* @__PURE__ */ new Map();
|
|
3410
|
+
readCache = /* @__PURE__ */ new Map();
|
|
3411
|
+
pendingReads = /* @__PURE__ */ new Map();
|
|
3412
|
+
readGeneration = 0;
|
|
3255
3413
|
yieldOptions = /* @__PURE__ */ new Map();
|
|
3256
3414
|
pendingYieldOptions = /* @__PURE__ */ new Map();
|
|
3257
3415
|
constructor(owneyApiKey, options = {}) {
|
|
@@ -3261,10 +3419,16 @@ var YieldseekerAgent = class {
|
|
|
3261
3419
|
options.fetchFn
|
|
3262
3420
|
);
|
|
3263
3421
|
this.auth = new YieldseekerAuth(options.auth);
|
|
3422
|
+
this.rpcUrls = options.rpcUrls;
|
|
3264
3423
|
this.transactionExecutor = options.transactionExecutor;
|
|
3265
3424
|
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
3266
3425
|
}
|
|
3426
|
+
getReceiptClient() {
|
|
3427
|
+
this.receiptClient ??= createPaidRpcClient(base3, this.rpcUrls);
|
|
3428
|
+
return this.receiptClient;
|
|
3429
|
+
}
|
|
3267
3430
|
async disconnect() {
|
|
3431
|
+
this.readGeneration += 1;
|
|
3268
3432
|
this.auth.clear();
|
|
3269
3433
|
for (const key2 of this.users.keys()) {
|
|
3270
3434
|
const [walletAddress, chainId] = key2.split(":");
|
|
@@ -3273,6 +3437,9 @@ var YieldseekerAgent = class {
|
|
|
3273
3437
|
this.users.clear();
|
|
3274
3438
|
this.agentContexts.clear();
|
|
3275
3439
|
this.pendingAgents.clear();
|
|
3440
|
+
this.pendingWalletContexts.clear();
|
|
3441
|
+
this.readCache.clear();
|
|
3442
|
+
this.pendingReads.clear();
|
|
3276
3443
|
}
|
|
3277
3444
|
async activateAgent(state, chainId, asset) {
|
|
3278
3445
|
this.assertChain(chainId);
|
|
@@ -3535,6 +3702,86 @@ var YieldseekerAgent = class {
|
|
|
3535
3702
|
contextKey(state, chainId, asset) {
|
|
3536
3703
|
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3537
3704
|
}
|
|
3705
|
+
cachedRead(key2, ttlMs, read2) {
|
|
3706
|
+
const cached = this.readCache.get(key2);
|
|
3707
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
3708
|
+
return Promise.resolve(cached.value);
|
|
3709
|
+
}
|
|
3710
|
+
const pending = this.pendingReads.get(key2);
|
|
3711
|
+
if (pending) return pending;
|
|
3712
|
+
const generation = this.readGeneration;
|
|
3713
|
+
const request = Promise.resolve().then(read2).then((value) => {
|
|
3714
|
+
if (this.readGeneration === generation) {
|
|
3715
|
+
this.readCache.set(key2, {
|
|
3716
|
+
expiresAt: Date.now() + ttlMs,
|
|
3717
|
+
value
|
|
3718
|
+
});
|
|
3719
|
+
}
|
|
3720
|
+
return value;
|
|
3721
|
+
}).finally(() => {
|
|
3722
|
+
if (this.pendingReads.get(key2) === request) {
|
|
3723
|
+
this.pendingReads.delete(key2);
|
|
3724
|
+
}
|
|
3725
|
+
});
|
|
3726
|
+
this.pendingReads.set(key2, request);
|
|
3727
|
+
return request;
|
|
3728
|
+
}
|
|
3729
|
+
agentListKey(state, chainId) {
|
|
3730
|
+
return `agents:${this.userKey(state, chainId)}`;
|
|
3731
|
+
}
|
|
3732
|
+
async listAgents(state, chainId, user) {
|
|
3733
|
+
const response = await this.cachedRead(
|
|
3734
|
+
this.agentListKey(state, chainId),
|
|
3735
|
+
YIELDSEEKER_AGENT_LIST_CACHE_MS,
|
|
3736
|
+
async () => {
|
|
3737
|
+
const response2 = await this.walletRequest(
|
|
3738
|
+
state,
|
|
3739
|
+
chainId,
|
|
3740
|
+
`/users/${user.userId}/agents`
|
|
3741
|
+
);
|
|
3742
|
+
if (!Array.isArray(response2?.agents)) {
|
|
3743
|
+
throw this.invalidResponse("agent list");
|
|
3744
|
+
}
|
|
3745
|
+
return response2;
|
|
3746
|
+
}
|
|
3747
|
+
);
|
|
3748
|
+
return response.agents;
|
|
3749
|
+
}
|
|
3750
|
+
async contextForAgent(state, chainId, user, agent, asset) {
|
|
3751
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3752
|
+
const cached = this.agentContexts.get(key2);
|
|
3753
|
+
if (cached?.agent.agentId === agent.agentId) return cached;
|
|
3754
|
+
const pending = this.pendingWalletContexts.get(key2);
|
|
3755
|
+
if (pending) return pending;
|
|
3756
|
+
const generation = this.readGeneration;
|
|
3757
|
+
const request = this.walletRequest(
|
|
3758
|
+
state,
|
|
3759
|
+
chainId,
|
|
3760
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3761
|
+
).then((walletResponse) => {
|
|
3762
|
+
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3763
|
+
throw this.invalidResponse("agent wallet");
|
|
3764
|
+
}
|
|
3765
|
+
const context = {
|
|
3766
|
+
user,
|
|
3767
|
+
agent,
|
|
3768
|
+
wallet: walletResponse.agentWallet,
|
|
3769
|
+
asset
|
|
3770
|
+
};
|
|
3771
|
+
if (this.readGeneration === generation) {
|
|
3772
|
+
this.agentContexts.set(key2, context);
|
|
3773
|
+
}
|
|
3774
|
+
return context;
|
|
3775
|
+
});
|
|
3776
|
+
this.pendingWalletContexts.set(key2, request);
|
|
3777
|
+
try {
|
|
3778
|
+
return await request;
|
|
3779
|
+
} finally {
|
|
3780
|
+
if (this.pendingWalletContexts.get(key2) === request) {
|
|
3781
|
+
this.pendingWalletContexts.delete(key2);
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3538
3785
|
async resolveUser(state, chainId) {
|
|
3539
3786
|
const key2 = this.userKey(state, chainId);
|
|
3540
3787
|
const inMemory = this.users.get(key2);
|
|
@@ -3631,16 +3878,9 @@ var YieldseekerAgent = class {
|
|
|
3631
3878
|
}
|
|
3632
3879
|
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3633
3880
|
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
|
-
}
|
|
3881
|
+
const agents = await this.listAgents(state, chainId, user);
|
|
3642
3882
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3643
|
-
let agent =
|
|
3883
|
+
let agent = agents.find(
|
|
3644
3884
|
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3645
3885
|
);
|
|
3646
3886
|
if (!agent && createIfMissing) {
|
|
@@ -3661,83 +3901,91 @@ var YieldseekerAgent = class {
|
|
|
3661
3901
|
}
|
|
3662
3902
|
);
|
|
3663
3903
|
agent = created?.agent;
|
|
3904
|
+
if (agent) {
|
|
3905
|
+
this.readCache.set(this.agentListKey(state, chainId), {
|
|
3906
|
+
expiresAt: Date.now() + YIELDSEEKER_AGENT_LIST_CACHE_MS,
|
|
3907
|
+
value: {
|
|
3908
|
+
agents: [...agents, agent]
|
|
3909
|
+
}
|
|
3910
|
+
});
|
|
3911
|
+
}
|
|
3664
3912
|
}
|
|
3665
3913
|
if (!agent) return null;
|
|
3666
3914
|
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 };
|
|
3915
|
+
return this.contextForAgent(state, chainId, user, agent, asset);
|
|
3676
3916
|
}
|
|
3677
3917
|
async loadPortfolio(state, chainId, options) {
|
|
3678
3918
|
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
|
-
}
|
|
3919
|
+
const agents = await this.listAgents(state, chainId, user);
|
|
3687
3920
|
const contexts = [];
|
|
3688
|
-
for (const agent of
|
|
3921
|
+
for (const agent of agents) {
|
|
3689
3922
|
const asset = this.assetForAgent(agent);
|
|
3690
3923
|
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3691
3924
|
continue;
|
|
3692
3925
|
}
|
|
3693
3926
|
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);
|
|
3927
|
+
contexts.push(this.contextForAgent(state, chainId, user, agent, asset));
|
|
3710
3928
|
}
|
|
3929
|
+
const resolvedContexts = await Promise.all(contexts);
|
|
3711
3930
|
return Promise.all(
|
|
3712
|
-
|
|
3931
|
+
resolvedContexts.map(
|
|
3713
3932
|
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
3714
3933
|
)
|
|
3715
3934
|
);
|
|
3716
3935
|
}
|
|
3717
3936
|
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3937
|
+
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
3718
3938
|
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3719
|
-
this.
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3939
|
+
this.cachedRead(
|
|
3940
|
+
`snapshot:${contextKey}`,
|
|
3941
|
+
YIELDSEEKER_PORTFOLIO_CACHE_MS,
|
|
3942
|
+
async () => {
|
|
3943
|
+
const response = await this.walletRequest(
|
|
3944
|
+
state,
|
|
3945
|
+
chainId,
|
|
3946
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3947
|
+
shouldOnlyUseRecentValue: true,
|
|
3948
|
+
shouldAllowStaleOnError: true
|
|
3949
|
+
})}`
|
|
3950
|
+
);
|
|
3951
|
+
if (!response?.agentSnapshot) {
|
|
3952
|
+
throw this.invalidResponse("agent snapshot");
|
|
3953
|
+
}
|
|
3954
|
+
return response;
|
|
3955
|
+
}
|
|
3726
3956
|
),
|
|
3727
|
-
this.
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3957
|
+
this.cachedRead(
|
|
3958
|
+
`positions:${contextKey}`,
|
|
3959
|
+
YIELDSEEKER_PORTFOLIO_CACHE_MS,
|
|
3960
|
+
async () => {
|
|
3961
|
+
const response = await this.walletRequest(
|
|
3962
|
+
state,
|
|
3963
|
+
chainId,
|
|
3964
|
+
this.agentPath(context, "yield-positions")
|
|
3965
|
+
);
|
|
3966
|
+
if (!Array.isArray(response?.yieldPositions)) {
|
|
3967
|
+
throw this.invalidResponse("yield positions");
|
|
3968
|
+
}
|
|
3969
|
+
return response;
|
|
3970
|
+
}
|
|
3731
3971
|
),
|
|
3732
|
-
options.historic ? this.
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
this.
|
|
3972
|
+
options.historic ? this.cachedRead(
|
|
3973
|
+
`historic:${contextKey}`,
|
|
3974
|
+
YIELDSEEKER_ACTIVITY_CACHE_MS,
|
|
3975
|
+
() => this.walletRequest(
|
|
3976
|
+
state,
|
|
3977
|
+
chainId,
|
|
3978
|
+
this.agentPath(context, "wallet/historic-position")
|
|
3979
|
+
)
|
|
3736
3980
|
) : Promise.resolve(void 0),
|
|
3737
|
-
options.actions ? this.
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
this.
|
|
3981
|
+
options.actions ? this.cachedRead(
|
|
3982
|
+
`actions:${contextKey}`,
|
|
3983
|
+
YIELDSEEKER_ACTIVITY_CACHE_MS,
|
|
3984
|
+
() => this.walletRequest(
|
|
3985
|
+
state,
|
|
3986
|
+
chainId,
|
|
3987
|
+
this.agentPath(context, "actions")
|
|
3988
|
+
)
|
|
3741
3989
|
) : Promise.resolve(void 0)
|
|
3742
3990
|
]);
|
|
3743
3991
|
if (!snapshot?.agentSnapshot) {
|
|
@@ -3771,6 +4019,13 @@ var YieldseekerAgent = class {
|
|
|
3771
4019
|
context.wallet = deployed.agentWallet;
|
|
3772
4020
|
}
|
|
3773
4021
|
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
4022
|
+
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
4023
|
+
const invalidatePortfolio = () => {
|
|
4024
|
+
for (const kind of ["snapshot", "positions", "historic", "actions"]) {
|
|
4025
|
+
this.readCache.delete(`${kind}:${contextKey}`);
|
|
4026
|
+
}
|
|
4027
|
+
};
|
|
4028
|
+
invalidatePortfolio();
|
|
3774
4029
|
try {
|
|
3775
4030
|
const response = await this.walletRequest(
|
|
3776
4031
|
state,
|
|
@@ -3787,6 +4042,8 @@ var YieldseekerAgent = class {
|
|
|
3787
4042
|
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3788
4043
|
error
|
|
3789
4044
|
);
|
|
4045
|
+
} finally {
|
|
4046
|
+
invalidatePortfolio();
|
|
3790
4047
|
}
|
|
3791
4048
|
}
|
|
3792
4049
|
agentPath(context, suffix) {
|
|
@@ -3837,6 +4094,7 @@ var YieldseekerAgent = class {
|
|
|
3837
4094
|
code,
|
|
3838
4095
|
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3839
4096
|
{
|
|
4097
|
+
rpcSource: "agent-api",
|
|
3840
4098
|
statusCode: error.status,
|
|
3841
4099
|
providerCode: error.providerCode,
|
|
3842
4100
|
...error.responseFields ? { fields: error.responseFields } : {}
|
|
@@ -3855,12 +4113,12 @@ var YieldseekerAgent = class {
|
|
|
3855
4113
|
chain: base3,
|
|
3856
4114
|
transport: custom2(state.provider)
|
|
3857
4115
|
});
|
|
3858
|
-
const
|
|
4116
|
+
const walletChainClient = createPublicClient3({
|
|
3859
4117
|
chain: base3,
|
|
3860
4118
|
transport: custom2(state.provider)
|
|
3861
4119
|
});
|
|
3862
4120
|
await ensureWalletOnChain(
|
|
3863
|
-
|
|
4121
|
+
walletChainClient,
|
|
3864
4122
|
walletClient,
|
|
3865
4123
|
8453
|
|
3866
4124
|
);
|
|
@@ -3871,10 +4129,15 @@ var YieldseekerAgent = class {
|
|
|
3871
4129
|
data: transaction.data,
|
|
3872
4130
|
value: BigInt(transaction.value)
|
|
3873
4131
|
});
|
|
3874
|
-
const receipt = await
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
4132
|
+
const receipt = await withPaidRpcDiagnostics(
|
|
4133
|
+
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
4134
|
+
hash,
|
|
4135
|
+
confirmations: 1
|
|
4136
|
+
}),
|
|
4137
|
+
8453,
|
|
4138
|
+
"eth_getTransactionReceipt",
|
|
4139
|
+
this.id
|
|
4140
|
+
);
|
|
3878
4141
|
if (receipt.status !== "success") {
|
|
3879
4142
|
throw new OwneyError(
|
|
3880
4143
|
"AGENT_TRANSACTION_REVERTED",
|
|
@@ -3890,14 +4153,15 @@ var YieldseekerAgent = class {
|
|
|
3890
4153
|
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3891
4154
|
return;
|
|
3892
4155
|
}
|
|
3893
|
-
const
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
4156
|
+
const receipt = await withPaidRpcDiagnostics(
|
|
4157
|
+
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
4158
|
+
hash: transactionHash,
|
|
4159
|
+
confirmations: 1
|
|
4160
|
+
}),
|
|
4161
|
+
8453,
|
|
4162
|
+
"eth_getTransactionReceipt",
|
|
4163
|
+
this.id
|
|
4164
|
+
);
|
|
3901
4165
|
if (receipt.status !== "success") {
|
|
3902
4166
|
throw new OwneyError(
|
|
3903
4167
|
"AGENT_TRANSACTION_REVERTED",
|
|
@@ -4269,6 +4533,41 @@ import {
|
|
|
4269
4533
|
// src/lib/permit2-batch.ts
|
|
4270
4534
|
import { parseAbi as parseAbi2, hashStruct } from "viem";
|
|
4271
4535
|
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
4536
|
+
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
4537
|
+
var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
4538
|
+
var ERC2612_PERMIT_TYPES = {
|
|
4539
|
+
Permit: [
|
|
4540
|
+
{ name: "owner", type: "address" },
|
|
4541
|
+
{ name: "spender", type: "address" },
|
|
4542
|
+
{ name: "value", type: "uint256" },
|
|
4543
|
+
{ name: "nonce", type: "uint256" },
|
|
4544
|
+
{ name: "deadline", type: "uint256" }
|
|
4545
|
+
]
|
|
4546
|
+
};
|
|
4547
|
+
var ERC2612_READ_ABI = parseAbi2([
|
|
4548
|
+
"function name() view returns (string)",
|
|
4549
|
+
"function version() view returns (string)",
|
|
4550
|
+
"function nonces(address owner) view returns (uint256)"
|
|
4551
|
+
]);
|
|
4552
|
+
function erc2612TypedData(input) {
|
|
4553
|
+
return {
|
|
4554
|
+
domain: {
|
|
4555
|
+
name: input.permit.tokenName,
|
|
4556
|
+
version: input.permit.tokenVersion,
|
|
4557
|
+
chainId: input.chainId,
|
|
4558
|
+
verifyingContract: input.token
|
|
4559
|
+
},
|
|
4560
|
+
types: ERC2612_PERMIT_TYPES,
|
|
4561
|
+
primaryType: "Permit",
|
|
4562
|
+
message: {
|
|
4563
|
+
owner: input.owner,
|
|
4564
|
+
spender: BATCH_PERMIT2_ADDRESS,
|
|
4565
|
+
value: BigInt(input.permit.value),
|
|
4566
|
+
nonce: BigInt(input.permit.nonce),
|
|
4567
|
+
deadline: BigInt(input.permit.deadline)
|
|
4568
|
+
}
|
|
4569
|
+
};
|
|
4570
|
+
}
|
|
4272
4571
|
var PERMIT_BATCH_TYPES = {
|
|
4273
4572
|
PermitBatchWitnessTransferFrom: [
|
|
4274
4573
|
{ name: "permitted", type: "TokenPermissions[]" },
|
|
@@ -4420,28 +4719,67 @@ async function execute(i, key2, plan) {
|
|
|
4420
4719
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
4421
4720
|
"Insufficient token balance for this deposit."
|
|
4422
4721
|
);
|
|
4423
|
-
if (allowance < total)
|
|
4722
|
+
if (allowance < total && (i.chainId !== 8453 || !isAddressEqual(i.token, BASE_USDC_ADDRESS)))
|
|
4424
4723
|
throw new OwneyError(
|
|
4425
4724
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
4426
4725
|
"token deposits need a one-time Permit2 approval."
|
|
4427
4726
|
);
|
|
4428
|
-
const
|
|
4727
|
+
const now = (await i.pub.getBlock()).timestamp;
|
|
4728
|
+
let erc2612Permit;
|
|
4729
|
+
if (allowance < total) {
|
|
4730
|
+
const [tokenName, tokenVersion, permitNonce] = await Promise.all([
|
|
4731
|
+
i.pub.readContract({
|
|
4732
|
+
address: i.token,
|
|
4733
|
+
abi: ERC2612_READ_ABI,
|
|
4734
|
+
functionName: "name"
|
|
4735
|
+
}),
|
|
4736
|
+
i.pub.readContract({
|
|
4737
|
+
address: i.token,
|
|
4738
|
+
abi: ERC2612_READ_ABI,
|
|
4739
|
+
functionName: "version"
|
|
4740
|
+
}),
|
|
4741
|
+
i.pub.readContract({
|
|
4742
|
+
address: i.token,
|
|
4743
|
+
abi: ERC2612_READ_ABI,
|
|
4744
|
+
functionName: "nonces",
|
|
4745
|
+
args: [i.owner]
|
|
4746
|
+
})
|
|
4747
|
+
]);
|
|
4748
|
+
const unsignedPermit = {
|
|
4749
|
+
value: MAX_UINT256.toString(),
|
|
4750
|
+
nonce: permitNonce.toString(),
|
|
4751
|
+
deadline: (now + 900n).toString(),
|
|
4752
|
+
tokenName,
|
|
4753
|
+
tokenVersion
|
|
4754
|
+
};
|
|
4755
|
+
const signature2 = await i.wallet.signTypedData({
|
|
4756
|
+
account: i.owner,
|
|
4757
|
+
...erc2612TypedData({
|
|
4758
|
+
chainId: 8453,
|
|
4759
|
+
token: i.token,
|
|
4760
|
+
owner: i.owner,
|
|
4761
|
+
permit: unsignedPermit
|
|
4762
|
+
})
|
|
4763
|
+
});
|
|
4764
|
+
erc2612Permit = { ...unsignedPermit, signature: signature2 };
|
|
4765
|
+
}
|
|
4766
|
+
const spender = erc2612Permit ? MULTICALL3_ADDRESS : await getSponsorRelayerAddress({
|
|
4429
4767
|
apiKey: i.apiKey,
|
|
4430
4768
|
baseUrl: i.baseUrl,
|
|
4431
4769
|
chainId: i.chainId
|
|
4432
4770
|
});
|
|
4433
|
-
const now = (await i.pub.getBlock()).timestamp;
|
|
4434
4771
|
const unsigned = {
|
|
4435
4772
|
chainId: i.chainId,
|
|
4436
4773
|
token: i.token,
|
|
4437
4774
|
from: i.owner,
|
|
4438
4775
|
transfers: i.transfers,
|
|
4439
4776
|
nonce: randomPermit2Nonce().toString(),
|
|
4440
|
-
deadline: (now + 900n).toString()
|
|
4777
|
+
deadline: (now + 900n).toString(),
|
|
4778
|
+
...erc2612Permit ? { erc2612Permit } : {}
|
|
4441
4779
|
};
|
|
4442
4780
|
const signature = await i.wallet.signTypedData({
|
|
4443
4781
|
account: i.owner,
|
|
4444
|
-
...batchTypedData(unsigned,
|
|
4782
|
+
...batchTypedData(unsigned, spender)
|
|
4445
4783
|
});
|
|
4446
4784
|
i.onApproved?.();
|
|
4447
4785
|
return send({ ...unsigned, signature });
|
|
@@ -4461,8 +4799,8 @@ function makeSponsoredTokenCallback(deps) {
|
|
|
4461
4799
|
"CHAIN_UNSUPPORTED",
|
|
4462
4800
|
`No sponsored token configured for chain ${chainId}`
|
|
4463
4801
|
);
|
|
4464
|
-
const pub = deps.getPublicClient(chainId), wallet = deps.getWalletClient(chainId);
|
|
4465
|
-
await ensureWalletOnChain(
|
|
4802
|
+
const pub = deps.getPublicClient(chainId), walletChain = deps.getWalletChainClient?.(chainId) ?? pub, wallet = deps.getWalletClient(chainId);
|
|
4803
|
+
await ensureWalletOnChain(walletChain, wallet, chainId);
|
|
4466
4804
|
return sponsorTokenBatch({
|
|
4467
4805
|
apiKey: deps.apiKey,
|
|
4468
4806
|
baseUrl: deps.baseUrl,
|
|
@@ -4764,6 +5102,7 @@ var OwneySDK = class {
|
|
|
4764
5102
|
// leave every user's agent profile alone.
|
|
4765
5103
|
orgAgentConfig;
|
|
4766
5104
|
orgAgentConfigPromise = null;
|
|
5105
|
+
rpcUrls;
|
|
4767
5106
|
zyfaiRpcUrls;
|
|
4768
5107
|
yieldseekerApiBaseUrl;
|
|
4769
5108
|
yieldseekerSiweOrigin;
|
|
@@ -4788,6 +5127,7 @@ var OwneySDK = class {
|
|
|
4788
5127
|
constructor(config) {
|
|
4789
5128
|
this.apiKey = config.apiKey;
|
|
4790
5129
|
if (config.debug) setOwneyDebug(true);
|
|
5130
|
+
this.rpcUrls = config.rpcUrls;
|
|
4791
5131
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
4792
5132
|
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
4793
5133
|
this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
|
|
@@ -4878,6 +5218,9 @@ var OwneySDK = class {
|
|
|
4878
5218
|
}
|
|
4879
5219
|
return this.state.provider;
|
|
4880
5220
|
}
|
|
5221
|
+
getPaidRpcClient(chainId) {
|
|
5222
|
+
return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
|
|
5223
|
+
}
|
|
4881
5224
|
/** Builds the default USDC batch callback for the connected wallet. */
|
|
4882
5225
|
getDefaultSponsoredCallback(onApproved) {
|
|
4883
5226
|
if (!onApproved && this.cachedSponsoredCallback)
|
|
@@ -4893,7 +5236,8 @@ var OwneySDK = class {
|
|
|
4893
5236
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
4894
5237
|
// PublicClient/WalletClient param types — structurally identical at
|
|
4895
5238
|
// runtime, but the two share a name TS treats as unrelated.
|
|
4896
|
-
getPublicClient: (cid) =>
|
|
5239
|
+
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
5240
|
+
getWalletChainClient: (cid) => createPublicClient4({
|
|
4897
5241
|
chain: VIEM_CHAIN2[cid],
|
|
4898
5242
|
transport: custom3(provider)
|
|
4899
5243
|
}),
|
|
@@ -4943,7 +5287,8 @@ var OwneySDK = class {
|
|
|
4943
5287
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
4944
5288
|
// PublicClient/WalletClient param types — structurally identical at
|
|
4945
5289
|
// runtime, but the two share a name TS treats as unrelated.
|
|
4946
|
-
getPublicClient: (cid) =>
|
|
5290
|
+
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
5291
|
+
getWalletChainClient: (cid) => createPublicClient4({
|
|
4947
5292
|
chain: VIEM_CHAIN2[cid],
|
|
4948
5293
|
transport: custom3(provider)
|
|
4949
5294
|
}),
|
|
@@ -5056,12 +5401,17 @@ var OwneySDK = class {
|
|
|
5056
5401
|
createAgent(agentId, key2) {
|
|
5057
5402
|
if (agentId === "zyfai") {
|
|
5058
5403
|
if (!key2) return null;
|
|
5059
|
-
return new ZyfaiAgent(
|
|
5404
|
+
return new ZyfaiAgent(
|
|
5405
|
+
key2,
|
|
5406
|
+
this.rpcUrls ?? this.zyfaiRpcUrls,
|
|
5407
|
+
this.referralSource
|
|
5408
|
+
);
|
|
5060
5409
|
}
|
|
5061
5410
|
if (agentId === "yieldseeker") {
|
|
5062
5411
|
return new YieldseekerAgent(this.apiKey, {
|
|
5063
5412
|
auth: { origin: this.yieldseekerSiweOrigin },
|
|
5064
|
-
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
5413
|
+
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl),
|
|
5414
|
+
rpcUrls: this.rpcUrls
|
|
5065
5415
|
});
|
|
5066
5416
|
}
|
|
5067
5417
|
return null;
|
|
@@ -5397,10 +5747,12 @@ var OwneySDK = class {
|
|
|
5397
5747
|
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
5398
5748
|
* two independent auto-recovery mechanisms:
|
|
5399
5749
|
*
|
|
5400
|
-
* 1. Missing Permit2 allowance
|
|
5401
|
-
* callback and the attempt fails with
|
|
5402
|
-
*
|
|
5403
|
-
*
|
|
5750
|
+
* 1. Missing Permit2 allowance outside the atomic Base-USDC path: when the
|
|
5751
|
+
* app did not supply its own callback and the attempt fails with
|
|
5752
|
+
* `PERMIT2_APPROVAL_REQUIRED`, this is the wallet's first Permit2 deposit
|
|
5753
|
+
* for that token. Base USDC bundles a gasless ERC-2612 approval inside
|
|
5754
|
+
* its sponsored deposit and never reaches this branch. Other tokens send
|
|
5755
|
+
* the one-time user-paid Permit2 approval via `approvePermit2()` and
|
|
5404
5756
|
* retry the SAME sponsored attempt once. Bounded to one approval attempt
|
|
5405
5757
|
* per call so a wallet/agent that keeps reporting the allowance as
|
|
5406
5758
|
* missing can't loop forever. If `approvePermit2()` itself throws (e.g.
|
|
@@ -5591,6 +5943,32 @@ var OwneySDK = class {
|
|
|
5591
5943
|
}
|
|
5592
5944
|
return eligible;
|
|
5593
5945
|
}
|
|
5946
|
+
/**
|
|
5947
|
+
* Run owner-approved withdrawals before relayer-only withdrawals. Wallet
|
|
5948
|
+
* approval is the only point at which the user can cancel the aggregate
|
|
5949
|
+
* operation, so no relayer leg should commit before it has completed.
|
|
5950
|
+
*/
|
|
5951
|
+
orderAgentsForWithdrawal(agents) {
|
|
5952
|
+
return agents.map((agent, index) => ({ agent, index })).sort((left, right) => {
|
|
5953
|
+
const approvalOrder = Number(Boolean(right.agent.withdrawalRequiresWalletApproval)) - Number(Boolean(left.agent.withdrawalRequiresWalletApproval));
|
|
5954
|
+
return approvalOrder || left.index - right.index;
|
|
5955
|
+
}).map(({ agent }) => agent);
|
|
5956
|
+
}
|
|
5957
|
+
isUserRejectedWithdrawal(error) {
|
|
5958
|
+
let current = error;
|
|
5959
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5960
|
+
while (current && typeof current === "object" && !seen.has(current)) {
|
|
5961
|
+
seen.add(current);
|
|
5962
|
+
const candidate = current;
|
|
5963
|
+
if (candidate.code === 4001 || candidate.code === "4001") return true;
|
|
5964
|
+
const message = [candidate.message, candidate.shortMessage].filter((value) => typeof value === "string").join(" ");
|
|
5965
|
+
if (/user (?:rejected|denied)|rejected by user/i.test(message)) {
|
|
5966
|
+
return true;
|
|
5967
|
+
}
|
|
5968
|
+
current = candidate.cause;
|
|
5969
|
+
}
|
|
5970
|
+
return false;
|
|
5971
|
+
}
|
|
5594
5972
|
// --- Fund operations ---
|
|
5595
5973
|
/**
|
|
5596
5974
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -5626,13 +6004,15 @@ var OwneySDK = class {
|
|
|
5626
6004
|
);
|
|
5627
6005
|
}
|
|
5628
6006
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
6007
|
+
const withdrawalAgents = this.orderAgentsForWithdrawal(eligibleAgents);
|
|
5629
6008
|
if (!amount) {
|
|
5630
6009
|
const results2 = {};
|
|
5631
6010
|
const agentErrors2 = {};
|
|
5632
|
-
for (const agent of
|
|
6011
|
+
for (const agent of withdrawalAgents) {
|
|
5633
6012
|
try {
|
|
5634
6013
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
5635
6014
|
} catch (err) {
|
|
6015
|
+
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
5636
6016
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
5637
6017
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
5638
6018
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
@@ -5713,12 +6093,13 @@ var OwneySDK = class {
|
|
|
5713
6093
|
planned: 0n
|
|
5714
6094
|
}));
|
|
5715
6095
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
6096
|
+
const orderedPlans = this.orderAgentsForWithdrawal(plans.map((p) => p.agent)).map((agent) => plans.find((plan) => plan.agent === agent));
|
|
5716
6097
|
const results = {};
|
|
5717
6098
|
const agentErrors = {
|
|
5718
6099
|
...aggregated.agentErrors ?? {}
|
|
5719
6100
|
};
|
|
5720
|
-
for (let i = 0; i <
|
|
5721
|
-
const p =
|
|
6101
|
+
for (let i = 0; i < orderedPlans.length; i++) {
|
|
6102
|
+
const p = orderedPlans[i];
|
|
5722
6103
|
if (p.planned === 0n) continue;
|
|
5723
6104
|
try {
|
|
5724
6105
|
results[p.agent.id] = await p.agent.withdraw(
|
|
@@ -5728,6 +6109,7 @@ var OwneySDK = class {
|
|
|
5728
6109
|
p.planned.toString()
|
|
5729
6110
|
);
|
|
5730
6111
|
} catch (err) {
|
|
6112
|
+
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
5731
6113
|
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
5732
6114
|
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
5733
6115
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
@@ -5739,7 +6121,7 @@ var OwneySDK = class {
|
|
|
5739
6121
|
);
|
|
5740
6122
|
const failedAmount = p.planned;
|
|
5741
6123
|
p.planned = 0n;
|
|
5742
|
-
redistributeShare(
|
|
6124
|
+
redistributeShare(orderedPlans, i, failedAmount);
|
|
5743
6125
|
}
|
|
5744
6126
|
}
|
|
5745
6127
|
if (Object.keys(results).length === 0) {
|
|
@@ -6186,17 +6568,18 @@ var OwneySDK = class {
|
|
|
6186
6568
|
);
|
|
6187
6569
|
}
|
|
6188
6570
|
const provider = this.requireConnectedProvider();
|
|
6189
|
-
const
|
|
6571
|
+
const walletChainClient = createPublicClient4({
|
|
6190
6572
|
chain: VIEM_CHAIN2[chainId],
|
|
6191
6573
|
transport: custom3(provider)
|
|
6192
6574
|
});
|
|
6575
|
+
const publicClient = this.getPaidRpcClient(chainId);
|
|
6193
6576
|
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
6194
6577
|
const wallet = createWalletClient3({
|
|
6195
6578
|
account: state.walletAddress,
|
|
6196
6579
|
chain: VIEM_CHAIN2[chainId],
|
|
6197
6580
|
transport: custom3(provider)
|
|
6198
6581
|
});
|
|
6199
|
-
await ensureWalletOnChain(
|
|
6582
|
+
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
6200
6583
|
const hash = await wallet.writeContract({
|
|
6201
6584
|
address: token,
|
|
6202
6585
|
abi: ERC20_ALLOWANCE_ABI,
|
|
@@ -6205,10 +6588,14 @@ var OwneySDK = class {
|
|
|
6205
6588
|
account: state.walletAddress,
|
|
6206
6589
|
chain: VIEM_CHAIN2[chainId]
|
|
6207
6590
|
});
|
|
6208
|
-
const receipt = await
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6591
|
+
const receipt = await withPaidRpcDiagnostics(
|
|
6592
|
+
() => publicClient.waitForTransactionReceipt({
|
|
6593
|
+
hash,
|
|
6594
|
+
confirmations: 1
|
|
6595
|
+
}),
|
|
6596
|
+
chainId,
|
|
6597
|
+
"eth_getTransactionReceipt"
|
|
6598
|
+
);
|
|
6212
6599
|
if (receipt.status !== "success") {
|
|
6213
6600
|
throw new Error(`Permit2 approval reverted (tx ${hash})`);
|
|
6214
6601
|
}
|