@owney/sdk 0.7.16-beta.0 → 0.7.16-beta.1
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 +2 -0
- package/dist/index.cjs +1700 -640
- package/dist/index.d.cts +120 -10
- package/dist/index.d.ts +120 -10
- package/dist/index.js +1706 -635
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -50,23 +50,6 @@ var SupportedAssets = [
|
|
|
50
50
|
}
|
|
51
51
|
];
|
|
52
52
|
|
|
53
|
-
// src/lib/debug.ts
|
|
54
|
-
var configuredDebug = false;
|
|
55
|
-
function setOwneyDebug(enabled) {
|
|
56
|
-
configuredDebug = enabled;
|
|
57
|
-
}
|
|
58
|
-
function isOwneyDebug() {
|
|
59
|
-
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
60
|
-
}
|
|
61
|
-
function debugLog(scope, message, data) {
|
|
62
|
-
if (!isOwneyDebug()) return;
|
|
63
|
-
if (data === void 0) {
|
|
64
|
-
console.log(`[${scope}] ${message}`);
|
|
65
|
-
} else {
|
|
66
|
-
console.log(`[${scope}] ${message}`, data);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
53
|
// src/errors.ts
|
|
71
54
|
var OwneyError = class extends Error {
|
|
72
55
|
code;
|
|
@@ -266,7 +249,7 @@ function mapBalances(raw, _chainId, smartWallet) {
|
|
|
266
249
|
)
|
|
267
250
|
)
|
|
268
251
|
),
|
|
269
|
-
apy:
|
|
252
|
+
apy: p.pool_apy,
|
|
270
253
|
tvl: p.pool_tvl,
|
|
271
254
|
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
272
255
|
// structural read keeps it undefined today and auto-populates later.
|
|
@@ -323,27 +306,14 @@ function mapWeightedApyByChain(raw) {
|
|
|
323
306
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
324
307
|
const perAsset = {};
|
|
325
308
|
for (const [symbol, apy] of Object.entries(tokenApy)) {
|
|
326
|
-
const
|
|
327
|
-
if (!SUPPORTED_TOKENS.includes(
|
|
328
|
-
perAsset[
|
|
309
|
+
const token2 = symbol;
|
|
310
|
+
if (!SUPPORTED_TOKENS.includes(token2)) continue;
|
|
311
|
+
perAsset[token2] = apy;
|
|
329
312
|
}
|
|
330
313
|
if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
|
|
331
314
|
}
|
|
332
315
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
333
316
|
}
|
|
334
|
-
var warnedGrossApyFallbacks = /* @__PURE__ */ new Set();
|
|
335
|
-
function netApy(net, gross, field) {
|
|
336
|
-
if (net !== void 0 && net !== null) return net;
|
|
337
|
-
if (gross === void 0 || gross === null) return void 0;
|
|
338
|
-
if (!warnedGrossApyFallbacks.has(field)) {
|
|
339
|
-
warnedGrossApyFallbacks.add(field);
|
|
340
|
-
console.warn(
|
|
341
|
-
`[owney] @zyfai/sdk omitted "${field}"; falling back to the gross APY, which does not deduct Zyfai's performance fee and so reads high.`
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
debugLog("zyfai:apy", `gross fallback for ${field}`, { gross });
|
|
345
|
-
return gross;
|
|
346
|
-
}
|
|
347
317
|
function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
348
318
|
let weightedSum = 0;
|
|
349
319
|
let totalBalance = 0;
|
|
@@ -351,9 +321,8 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
351
321
|
if (p.chainId !== chainId) continue;
|
|
352
322
|
if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
|
|
353
323
|
const balance = p.balance ?? 0;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
weightedSum += apy * balance;
|
|
324
|
+
if (balance <= 0 || typeof p.apy !== "number") continue;
|
|
325
|
+
weightedSum += p.apy * balance;
|
|
357
326
|
totalBalance += balance;
|
|
358
327
|
}
|
|
359
328
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
@@ -401,12 +370,8 @@ function mapEntries(rawEntries, chainId) {
|
|
|
401
370
|
const matched = positions.find(
|
|
402
371
|
(p) => p.protocol_name ? rawLog.newOpportunity.includes(`${p.protocol_name} (${p.pool})`) : false
|
|
403
372
|
);
|
|
404
|
-
oldApy = Number(
|
|
405
|
-
|
|
406
|
-
);
|
|
407
|
-
newApy = Number(
|
|
408
|
-
netApy(rawLog.newApy_withFee, rawLog.newApy, "newApy_withFee")
|
|
409
|
-
);
|
|
373
|
+
oldApy = Number(rawLog.oldApy);
|
|
374
|
+
newApy = Number(rawLog.newApy);
|
|
410
375
|
const from = splitOpportunity(rawLog.oldOpportunity);
|
|
411
376
|
const to = splitOpportunity(rawLog.newOpportunity);
|
|
412
377
|
rebalanceLog = [
|
|
@@ -486,11 +451,7 @@ function mapApyByStrategy(raw) {
|
|
|
486
451
|
const chainKey = supported.chainId;
|
|
487
452
|
const symbolKey = supported.symbol;
|
|
488
453
|
const bucket = apyPerAsset[chainKey] ?? {};
|
|
489
|
-
const apy =
|
|
490
|
-
entry.average_apy_withFee,
|
|
491
|
-
entry.average_apy,
|
|
492
|
-
"average_apy_withFee"
|
|
493
|
-
) ?? entry.average_apy;
|
|
454
|
+
const apy = entry.average_apy_with_fee ?? entry.average_apy;
|
|
494
455
|
bucket[symbolKey] = apy;
|
|
495
456
|
apyPerAsset[chainKey] = bucket;
|
|
496
457
|
apySum += apy;
|
|
@@ -516,23 +477,23 @@ function computeAllocationApy(positions) {
|
|
|
516
477
|
weightedSum += apy * value;
|
|
517
478
|
totalValue += value;
|
|
518
479
|
const chainId = resolveChainId(p.chain);
|
|
519
|
-
const
|
|
480
|
+
const token2 = p.asset;
|
|
520
481
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
521
|
-
if (!SUPPORTED_TOKENS.includes(
|
|
482
|
+
if (!SUPPORTED_TOKENS.includes(token2)) continue;
|
|
522
483
|
const perAsset = buckets[chainId] ?? {};
|
|
523
|
-
const cell = perAsset[
|
|
484
|
+
const cell = perAsset[token2] ?? { weightedSum: 0, value: 0 };
|
|
524
485
|
cell.weightedSum += apy * value;
|
|
525
486
|
cell.value += value;
|
|
526
|
-
perAsset[
|
|
487
|
+
perAsset[token2] = cell;
|
|
527
488
|
buckets[chainId] = perAsset;
|
|
528
489
|
}
|
|
529
490
|
const apyByChainAndAsset = {};
|
|
530
491
|
for (const [chainKey, perAsset] of Object.entries(buckets)) {
|
|
531
492
|
const chainId = Number(chainKey);
|
|
532
493
|
const out = {};
|
|
533
|
-
for (const [
|
|
494
|
+
for (const [token2, cell] of Object.entries(perAsset ?? {})) {
|
|
534
495
|
if (cell && cell.value > 0) {
|
|
535
|
-
out[
|
|
496
|
+
out[token2] = cell.weightedSum / cell.value;
|
|
536
497
|
}
|
|
537
498
|
}
|
|
538
499
|
if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
|
|
@@ -546,9 +507,9 @@ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4d
|
|
|
546
507
|
function topicToAddress(topic) {
|
|
547
508
|
return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
|
|
548
509
|
}
|
|
549
|
-
function extractWithdrawnAmount(logs, recipient,
|
|
510
|
+
function extractWithdrawnAmount(logs, recipient, token2, decimals) {
|
|
550
511
|
const wantRecipient = recipient.toLowerCase();
|
|
551
|
-
const wantToken =
|
|
512
|
+
const wantToken = token2.toLowerCase();
|
|
552
513
|
let total = 0n;
|
|
553
514
|
let matched = false;
|
|
554
515
|
for (const log of logs) {
|
|
@@ -574,11 +535,11 @@ var InvalidHistoryCursorError = class extends Error {
|
|
|
574
535
|
function encodeHistoryCursor(payload) {
|
|
575
536
|
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
|
|
576
537
|
}
|
|
577
|
-
function decodeHistoryCursor(
|
|
578
|
-
if (!
|
|
538
|
+
function decodeHistoryCursor(token2) {
|
|
539
|
+
if (!token2) throw new InvalidHistoryCursorError("Cursor is empty.");
|
|
579
540
|
let parsed;
|
|
580
541
|
try {
|
|
581
|
-
const json = Buffer.from(
|
|
542
|
+
const json = Buffer.from(token2, "base64").toString("utf8");
|
|
582
543
|
parsed = JSON.parse(json);
|
|
583
544
|
} catch {
|
|
584
545
|
throw new InvalidHistoryCursorError("Cursor is not valid base64 JSON.");
|
|
@@ -601,8 +562,8 @@ var storage = () => {
|
|
|
601
562
|
};
|
|
602
563
|
var buildKey = (address) => `${KEY_PREFIX}:${address.toLowerCase()}`;
|
|
603
564
|
var legacyKeyPrefix = (address) => `${KEY_PREFIX}:${address.toLowerCase()}:`;
|
|
604
|
-
var isJwtExpired = (
|
|
605
|
-
const parts =
|
|
565
|
+
var isJwtExpired = (token2) => {
|
|
566
|
+
const parts = token2.split(".");
|
|
606
567
|
if (parts.length !== 3) return false;
|
|
607
568
|
try {
|
|
608
569
|
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -850,6 +811,23 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
850
811
|
return !protocolListsEqual(current.protocols, desiredProtocols);
|
|
851
812
|
}
|
|
852
813
|
|
|
814
|
+
// src/lib/debug.ts
|
|
815
|
+
var configuredDebug = false;
|
|
816
|
+
function setOwneyDebug(enabled) {
|
|
817
|
+
configuredDebug = enabled;
|
|
818
|
+
}
|
|
819
|
+
function isOwneyDebug() {
|
|
820
|
+
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
821
|
+
}
|
|
822
|
+
function debugLog(scope, message, data) {
|
|
823
|
+
if (!isOwneyDebug()) return;
|
|
824
|
+
if (data === void 0) {
|
|
825
|
+
console.log(`[${scope}] ${message}`);
|
|
826
|
+
} else {
|
|
827
|
+
console.log(`[${scope}] ${message}`, data);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
853
831
|
// src/agents/zyfai/zyfai.agent.ts
|
|
854
832
|
var ERC7579_IS_MODULE_INSTALLED_ABI = parseAbi([
|
|
855
833
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
@@ -1687,20 +1665,20 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1687
1665
|
}
|
|
1688
1666
|
}
|
|
1689
1667
|
// --- IAgent: Fund operations ---
|
|
1690
|
-
async withdraw(state, chainId,
|
|
1668
|
+
async withdraw(state, chainId, token2, amount) {
|
|
1691
1669
|
const validChainId = isValidChainId(chainId);
|
|
1692
1670
|
await this.ensureConnected(state, validChainId);
|
|
1693
1671
|
const raw = await this.sdk.withdrawFunds(
|
|
1694
1672
|
this.getAddress(),
|
|
1695
1673
|
validChainId,
|
|
1696
1674
|
amount,
|
|
1697
|
-
|
|
1675
|
+
token2
|
|
1698
1676
|
);
|
|
1699
1677
|
if (!raw.success) {
|
|
1700
1678
|
throw new OwneyError(
|
|
1701
1679
|
"WITHDRAW_FAILED",
|
|
1702
1680
|
raw.message || "Zyfai withdraw failed.",
|
|
1703
|
-
{ chainId: validChainId, token, amount, response: raw },
|
|
1681
|
+
{ chainId: validChainId, token: token2, amount, response: raw },
|
|
1704
1682
|
this.id
|
|
1705
1683
|
);
|
|
1706
1684
|
}
|
|
@@ -1851,349 +1829,176 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1851
1829
|
}
|
|
1852
1830
|
};
|
|
1853
1831
|
|
|
1854
|
-
// src/
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1832
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
1833
|
+
import {
|
|
1834
|
+
createPublicClient as createPublicClient3,
|
|
1835
|
+
createWalletClient as createWalletClient2,
|
|
1836
|
+
custom as custom2,
|
|
1837
|
+
getAddress as getAddress2,
|
|
1838
|
+
isAddress,
|
|
1839
|
+
parseUnits
|
|
1840
|
+
} from "viem";
|
|
1841
|
+
import { base as base3 } from "viem/chains";
|
|
1842
|
+
|
|
1843
|
+
// src/lib/chain-guard.ts
|
|
1844
|
+
var CHAIN_NAMES = {
|
|
1845
|
+
1: "Ethereum",
|
|
1846
|
+
8453: "Base",
|
|
1847
|
+
42161: "Arbitrum"
|
|
1848
|
+
};
|
|
1849
|
+
function chainName(chainId) {
|
|
1850
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
1851
|
+
}
|
|
1852
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
1853
|
+
const actual = await pub.getChainId();
|
|
1854
|
+
if (actual === expected) return;
|
|
1858
1855
|
try {
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
console.warn(
|
|
1869
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
1870
|
-
);
|
|
1856
|
+
await wallet.switchChain({ id: expected });
|
|
1857
|
+
} catch (error) {
|
|
1858
|
+
throw new OwneyError(
|
|
1859
|
+
"CHAIN_MISMATCH",
|
|
1860
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
1861
|
+
{
|
|
1862
|
+
expectedChainId: expected,
|
|
1863
|
+
actualChainId: actual,
|
|
1864
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
1871
1865
|
}
|
|
1872
|
-
return null;
|
|
1873
|
-
}
|
|
1874
|
-
const json = await res.json();
|
|
1875
|
-
const policy = json.success ? json.data ?? null : null;
|
|
1876
|
-
debugLog(
|
|
1877
|
-
"owney-sdk",
|
|
1878
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
1879
|
-
policy ?? void 0
|
|
1880
1866
|
);
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1867
|
+
}
|
|
1868
|
+
const after = await pub.getChainId();
|
|
1869
|
+
if (after !== expected) {
|
|
1870
|
+
throw new OwneyError(
|
|
1871
|
+
"CHAIN_MISMATCH",
|
|
1872
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
1873
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
1886
1874
|
);
|
|
1887
|
-
return null;
|
|
1888
1875
|
}
|
|
1889
1876
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1877
|
+
|
|
1878
|
+
// src/lib/transfer-auth.ts
|
|
1879
|
+
import { bytesToHex } from "viem";
|
|
1880
|
+
var ERC20_META_ABI = [
|
|
1881
|
+
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
1882
|
+
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
1883
|
+
];
|
|
1884
|
+
function buildTransferWithAuthorizationTypedData(input) {
|
|
1885
|
+
return {
|
|
1886
|
+
domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
|
|
1887
|
+
types: {
|
|
1888
|
+
TransferWithAuthorization: [
|
|
1889
|
+
{ name: "from", type: "address" },
|
|
1890
|
+
{ name: "to", type: "address" },
|
|
1891
|
+
{ name: "value", type: "uint256" },
|
|
1892
|
+
{ name: "validAfter", type: "uint256" },
|
|
1893
|
+
{ name: "validBefore", type: "uint256" },
|
|
1894
|
+
{ name: "nonce", type: "bytes32" }
|
|
1895
|
+
]
|
|
1896
|
+
},
|
|
1897
|
+
primaryType: "TransferWithAuthorization",
|
|
1898
|
+
message: input.message
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
async function readTokenMeta(publicClient, token2) {
|
|
1902
|
+
const [tokenName, tokenVersion] = await Promise.all([
|
|
1903
|
+
publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "name" }),
|
|
1904
|
+
publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
1905
|
+
]);
|
|
1906
|
+
return { tokenName, tokenVersion };
|
|
1907
|
+
}
|
|
1908
|
+
function randomAuthNonce() {
|
|
1909
|
+
const bytes = new Uint8Array(32);
|
|
1910
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
1911
|
+
return bytesToHex(bytes);
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
// src/lib/sponsor-client.ts
|
|
1915
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1916
|
+
async function postSponsorTransferAuth(input) {
|
|
1917
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
1918
|
+
let res;
|
|
1919
|
+
try {
|
|
1920
|
+
res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
1921
|
+
method: "POST",
|
|
1922
|
+
headers: {
|
|
1923
|
+
"content-type": "application/json",
|
|
1924
|
+
"x-owney-api-key": input.apiKey,
|
|
1925
|
+
...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
|
|
1926
|
+
},
|
|
1927
|
+
body: JSON.stringify(input.body)
|
|
1928
|
+
});
|
|
1929
|
+
} catch (networkError) {
|
|
1901
1930
|
throw new OwneyError(
|
|
1902
|
-
"
|
|
1903
|
-
`
|
|
1904
|
-
{
|
|
1931
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1932
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1933
|
+
{ cause: String(networkError) }
|
|
1905
1934
|
);
|
|
1906
1935
|
}
|
|
1907
|
-
const
|
|
1908
|
-
|
|
1936
|
+
const text = await res.text();
|
|
1937
|
+
let parsed = null;
|
|
1938
|
+
try {
|
|
1939
|
+
parsed = JSON.parse(text);
|
|
1940
|
+
} catch {
|
|
1941
|
+
}
|
|
1942
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
1909
1943
|
throw new OwneyError(
|
|
1910
|
-
"
|
|
1911
|
-
`
|
|
1912
|
-
{
|
|
1944
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1945
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1946
|
+
{
|
|
1947
|
+
statusCode: res.status,
|
|
1948
|
+
responseBody: text.slice(0, 500),
|
|
1949
|
+
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
1950
|
+
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
1951
|
+
safeToFallback: res.status === 503
|
|
1952
|
+
}
|
|
1913
1953
|
);
|
|
1914
1954
|
}
|
|
1915
|
-
return
|
|
1955
|
+
return parsed.data;
|
|
1916
1956
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
async function reportAgentFailure(apiKey, agentType, errorCode) {
|
|
1957
|
+
async function postSponsorPermit2Transfer(input) {
|
|
1958
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
1959
|
+
let res;
|
|
1921
1960
|
try {
|
|
1922
|
-
await fetch(`${
|
|
1961
|
+
res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
|
|
1923
1962
|
method: "POST",
|
|
1924
1963
|
headers: {
|
|
1925
|
-
"
|
|
1926
|
-
"x-owney-api-key": apiKey
|
|
1964
|
+
"content-type": "application/json",
|
|
1965
|
+
"x-owney-api-key": input.apiKey,
|
|
1966
|
+
...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
|
|
1927
1967
|
},
|
|
1928
|
-
body: JSON.stringify(
|
|
1929
|
-
agent_type: agentType,
|
|
1930
|
-
error_code: errorCode,
|
|
1931
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1932
|
-
})
|
|
1968
|
+
body: JSON.stringify(input.body)
|
|
1933
1969
|
});
|
|
1934
|
-
} catch (
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1970
|
+
} catch (networkError) {
|
|
1971
|
+
throw new OwneyError(
|
|
1972
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1973
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1974
|
+
{ cause: String(networkError), safeToFallback: false }
|
|
1938
1975
|
);
|
|
1939
1976
|
}
|
|
1940
|
-
|
|
1941
|
-
|
|
1977
|
+
const text = await res.text();
|
|
1978
|
+
let parsed = null;
|
|
1942
1979
|
try {
|
|
1943
|
-
|
|
1944
|
-
} catch
|
|
1945
|
-
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
1946
|
-
void reportAgentFailure(apiKey, agentType, errorCode);
|
|
1947
|
-
throw err;
|
|
1980
|
+
parsed = JSON.parse(text);
|
|
1981
|
+
} catch {
|
|
1948
1982
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
1983
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
1984
|
+
throw new OwneyError(
|
|
1985
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1986
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1987
|
+
{
|
|
1988
|
+
statusCode: res.status,
|
|
1989
|
+
responseBody: text.slice(0, 500),
|
|
1990
|
+
safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
|
|
1991
|
+
}
|
|
1959
1992
|
);
|
|
1960
|
-
if (!tokenBalance) return { agent, balance: 0n };
|
|
1961
|
-
return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
|
|
1962
|
-
});
|
|
1963
|
-
}
|
|
1964
|
-
function planProportionalShares(balances, requested, totalAvailable) {
|
|
1965
|
-
const plans = balances.map(({ agent, balance }) => ({
|
|
1966
|
-
agent,
|
|
1967
|
-
balance,
|
|
1968
|
-
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
1969
|
-
}));
|
|
1970
|
-
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
1971
|
-
let remainder = requested - assigned;
|
|
1972
|
-
const byHeadroom = [...plans].sort((a, b) => {
|
|
1973
|
-
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
1974
|
-
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
1975
|
-
});
|
|
1976
|
-
for (const p of byHeadroom) {
|
|
1977
|
-
if (remainder === 0n) break;
|
|
1978
|
-
const headroom = p.balance - p.planned;
|
|
1979
|
-
if (headroom <= 0n) continue;
|
|
1980
|
-
const take = headroom < remainder ? headroom : remainder;
|
|
1981
|
-
p.planned += take;
|
|
1982
|
-
remainder -= take;
|
|
1983
1993
|
}
|
|
1984
|
-
return
|
|
1985
|
-
}
|
|
1986
|
-
function planDisabledDrain(disabled, requested) {
|
|
1987
|
-
const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
1988
|
-
const plans = [];
|
|
1989
|
-
let remaining = requested;
|
|
1990
|
-
for (const { agent, balance } of sorted) {
|
|
1991
|
-
if (remaining === 0n) {
|
|
1992
|
-
plans.push({ agent, balance, planned: 0n });
|
|
1993
|
-
continue;
|
|
1994
|
-
}
|
|
1995
|
-
const take = balance < remaining ? balance : remaining;
|
|
1996
|
-
plans.push({ agent, balance, planned: take });
|
|
1997
|
-
remaining -= take;
|
|
1998
|
-
}
|
|
1999
|
-
return { plans, remaining };
|
|
2000
|
-
}
|
|
2001
|
-
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
2002
|
-
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
2003
|
-
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
2004
|
-
const totalHeadroom = candidates.reduce(
|
|
2005
|
-
(s, c) => s + (c.balance - c.planned),
|
|
2006
|
-
0n
|
|
2007
|
-
);
|
|
2008
|
-
if (totalHeadroom === 0n) return;
|
|
2009
|
-
let distributed = 0n;
|
|
2010
|
-
for (const c of candidates) {
|
|
2011
|
-
const headroom = c.balance - c.planned;
|
|
2012
|
-
const proportional = headroom * amount / totalHeadroom;
|
|
2013
|
-
const give = proportional > headroom ? headroom : proportional;
|
|
2014
|
-
c.planned += give;
|
|
2015
|
-
distributed += give;
|
|
2016
|
-
}
|
|
2017
|
-
let leftover = amount - distributed;
|
|
2018
|
-
for (const c of candidates) {
|
|
2019
|
-
if (leftover === 0n) break;
|
|
2020
|
-
const headroom = c.balance - c.planned;
|
|
2021
|
-
if (headroom <= 0n) continue;
|
|
2022
|
-
const take = headroom < leftover ? headroom : leftover;
|
|
2023
|
-
c.planned += take;
|
|
2024
|
-
leftover -= take;
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2027
|
-
function sumWithdrawnAmount(results) {
|
|
2028
|
-
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
2029
|
-
}
|
|
2030
|
-
|
|
2031
|
-
// src/lib/helpers/account-apy-helper.ts
|
|
2032
|
-
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
2033
|
-
const sums = {};
|
|
2034
|
-
const weights = {};
|
|
2035
|
-
for (const id of Object.keys(agentApys)) {
|
|
2036
|
-
const cells = agentApys[id].apyByChainAndAsset;
|
|
2037
|
-
const balance = agentBalances[id] ?? 0;
|
|
2038
|
-
if (!cells || balance <= 0) continue;
|
|
2039
|
-
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
2040
|
-
if (!perAsset) continue;
|
|
2041
|
-
const chainId = Number(chainKey);
|
|
2042
|
-
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
2043
|
-
const apy = Number(apyValue ?? 0);
|
|
2044
|
-
if (apy === 0) continue;
|
|
2045
|
-
sums[chainId] ??= {};
|
|
2046
|
-
weights[chainId] ??= {};
|
|
2047
|
-
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
2048
|
-
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
const out = {};
|
|
2053
|
-
for (const chainKey of Object.keys(sums)) {
|
|
2054
|
-
const chainId = Number(chainKey);
|
|
2055
|
-
const perAssetOut = {};
|
|
2056
|
-
for (const asset of Object.keys(sums[chainId])) {
|
|
2057
|
-
const w = weights[chainId][asset];
|
|
2058
|
-
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
2059
|
-
}
|
|
2060
|
-
if (Object.keys(perAssetOut).length > 0) {
|
|
2061
|
-
out[chainId] = perAssetOut;
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
return out;
|
|
2065
|
-
}
|
|
2066
|
-
|
|
2067
|
-
// src/client.ts
|
|
2068
|
-
import {
|
|
2069
|
-
createPublicClient as createPublicClient2,
|
|
2070
|
-
createWalletClient,
|
|
2071
|
-
custom
|
|
2072
|
-
} from "viem";
|
|
2073
|
-
import { base as base2, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
2074
|
-
|
|
2075
|
-
// src/lib/transfer-auth.ts
|
|
2076
|
-
import { bytesToHex } from "viem";
|
|
2077
|
-
var ERC20_META_ABI = [
|
|
2078
|
-
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
2079
|
-
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
2080
|
-
];
|
|
2081
|
-
function buildTransferWithAuthorizationTypedData(input) {
|
|
2082
|
-
return {
|
|
2083
|
-
domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
|
|
2084
|
-
types: {
|
|
2085
|
-
TransferWithAuthorization: [
|
|
2086
|
-
{ name: "from", type: "address" },
|
|
2087
|
-
{ name: "to", type: "address" },
|
|
2088
|
-
{ name: "value", type: "uint256" },
|
|
2089
|
-
{ name: "validAfter", type: "uint256" },
|
|
2090
|
-
{ name: "validBefore", type: "uint256" },
|
|
2091
|
-
{ name: "nonce", type: "bytes32" }
|
|
2092
|
-
]
|
|
2093
|
-
},
|
|
2094
|
-
primaryType: "TransferWithAuthorization",
|
|
2095
|
-
message: input.message
|
|
2096
|
-
};
|
|
2097
|
-
}
|
|
2098
|
-
async function readTokenMeta(publicClient, token) {
|
|
2099
|
-
const [tokenName, tokenVersion] = await Promise.all([
|
|
2100
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
2101
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
2102
|
-
]);
|
|
2103
|
-
return { tokenName, tokenVersion };
|
|
2104
|
-
}
|
|
2105
|
-
function randomAuthNonce() {
|
|
2106
|
-
const bytes = new Uint8Array(32);
|
|
2107
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2108
|
-
return bytesToHex(bytes);
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
|
-
// src/lib/sponsor-client.ts
|
|
2112
|
-
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2113
|
-
async function postSponsorTransferAuth(input) {
|
|
2114
|
-
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2115
|
-
let res;
|
|
2116
|
-
try {
|
|
2117
|
-
res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2118
|
-
method: "POST",
|
|
2119
|
-
headers: {
|
|
2120
|
-
"content-type": "application/json",
|
|
2121
|
-
"x-owney-api-key": input.apiKey
|
|
2122
|
-
},
|
|
2123
|
-
body: JSON.stringify(input.body)
|
|
2124
|
-
});
|
|
2125
|
-
} catch (networkError) {
|
|
2126
|
-
throw new OwneyError(
|
|
2127
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2128
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2129
|
-
{ cause: String(networkError) }
|
|
2130
|
-
);
|
|
2131
|
-
}
|
|
2132
|
-
const text = await res.text();
|
|
2133
|
-
let parsed = null;
|
|
2134
|
-
try {
|
|
2135
|
-
parsed = JSON.parse(text);
|
|
2136
|
-
} catch {
|
|
2137
|
-
}
|
|
2138
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2139
|
-
throw new OwneyError(
|
|
2140
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2141
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2142
|
-
{
|
|
2143
|
-
statusCode: res.status,
|
|
2144
|
-
responseBody: text.slice(0, 500),
|
|
2145
|
-
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
2146
|
-
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2147
|
-
safeToFallback: res.status === 503
|
|
2148
|
-
}
|
|
2149
|
-
);
|
|
2150
|
-
}
|
|
2151
|
-
return parsed.data;
|
|
2152
|
-
}
|
|
2153
|
-
async function postSponsorPermit2Transfer(input) {
|
|
2154
|
-
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2155
|
-
let res;
|
|
2156
|
-
try {
|
|
2157
|
-
res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
|
|
2158
|
-
method: "POST",
|
|
2159
|
-
headers: {
|
|
2160
|
-
"content-type": "application/json",
|
|
2161
|
-
"x-owney-api-key": input.apiKey
|
|
2162
|
-
},
|
|
2163
|
-
body: JSON.stringify(input.body)
|
|
2164
|
-
});
|
|
2165
|
-
} catch (networkError) {
|
|
2166
|
-
throw new OwneyError(
|
|
2167
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2168
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2169
|
-
{ cause: String(networkError), safeToFallback: false }
|
|
2170
|
-
);
|
|
2171
|
-
}
|
|
2172
|
-
const text = await res.text();
|
|
2173
|
-
let parsed = null;
|
|
2174
|
-
try {
|
|
2175
|
-
parsed = JSON.parse(text);
|
|
2176
|
-
} catch {
|
|
2177
|
-
}
|
|
2178
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2179
|
-
throw new OwneyError(
|
|
2180
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2181
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2182
|
-
{
|
|
2183
|
-
statusCode: res.status,
|
|
2184
|
-
responseBody: text.slice(0, 500),
|
|
2185
|
-
safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
|
|
2186
|
-
}
|
|
2187
|
-
);
|
|
2188
|
-
}
|
|
2189
|
-
return parsed.data;
|
|
1994
|
+
return parsed.data;
|
|
2190
1995
|
}
|
|
2191
1996
|
async function getSponsorRelayerAddress(input) {
|
|
2192
|
-
const
|
|
1997
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2193
1998
|
let res;
|
|
2194
1999
|
try {
|
|
2195
2000
|
res = await fetch(
|
|
2196
|
-
`${
|
|
2001
|
+
`${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2197
2002
|
{
|
|
2198
2003
|
headers: { "x-owney-api-key": input.apiKey }
|
|
2199
2004
|
}
|
|
@@ -2286,217 +2091,1394 @@ function randomPermit2Nonce() {
|
|
|
2286
2091
|
globalThis.crypto.getRandomValues(bytes);
|
|
2287
2092
|
return BigInt(bytesToHex2(bytes));
|
|
2288
2093
|
}
|
|
2289
|
-
async function readPermit2Allowance(publicClient,
|
|
2094
|
+
async function readPermit2Allowance(publicClient, token2, owner) {
|
|
2290
2095
|
return publicClient.readContract({
|
|
2291
|
-
address:
|
|
2096
|
+
address: token2,
|
|
2292
2097
|
abi: ERC20_ALLOWANCE_ABI,
|
|
2293
2098
|
functionName: "allowance",
|
|
2294
2099
|
args: [owner, PERMIT2_ADDRESS]
|
|
2295
2100
|
});
|
|
2296
2101
|
}
|
|
2297
|
-
async function readErc20Balance(publicClient,
|
|
2298
|
-
return publicClient.readContract({
|
|
2299
|
-
address:
|
|
2300
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2301
|
-
functionName: "balanceOf",
|
|
2302
|
-
args: [owner]
|
|
2102
|
+
async function readErc20Balance(publicClient, token2, owner) {
|
|
2103
|
+
return publicClient.readContract({
|
|
2104
|
+
address: token2,
|
|
2105
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2106
|
+
functionName: "balanceOf",
|
|
2107
|
+
args: [owner]
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
// src/lib/sponsored-deposit.ts
|
|
2112
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2113
|
+
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2114
|
+
function provideDepositVerificationContext(callback, context) {
|
|
2115
|
+
callback[verificationSetter]?.(context);
|
|
2116
|
+
}
|
|
2117
|
+
function makeVerificationAwareDepositCallback(implementation) {
|
|
2118
|
+
let nextVerification;
|
|
2119
|
+
const callback = async (smartWallet, chainId, amount) => {
|
|
2120
|
+
const verification = nextVerification;
|
|
2121
|
+
nextVerification = void 0;
|
|
2122
|
+
return implementation(smartWallet, chainId, amount, verification);
|
|
2123
|
+
};
|
|
2124
|
+
Object.defineProperty(callback, verificationSetter, {
|
|
2125
|
+
value: (context) => {
|
|
2126
|
+
nextVerification = context;
|
|
2127
|
+
}
|
|
2128
|
+
});
|
|
2129
|
+
return callback;
|
|
2130
|
+
}
|
|
2131
|
+
function makeSponsoredDepositCallback(deps) {
|
|
2132
|
+
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2133
|
+
return makeVerificationAwareDepositCallback(
|
|
2134
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
2135
|
+
const cid = chainId;
|
|
2136
|
+
const token2 = deps.tokenAddressByChain[cid];
|
|
2137
|
+
if (!token2) {
|
|
2138
|
+
throw new OwneyError(
|
|
2139
|
+
"CHAIN_UNSUPPORTED",
|
|
2140
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2141
|
+
);
|
|
2142
|
+
}
|
|
2143
|
+
const pub = deps.getPublicClient(cid);
|
|
2144
|
+
const wallet = deps.getWalletClient(cid);
|
|
2145
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2146
|
+
try {
|
|
2147
|
+
const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
|
|
2148
|
+
if (balance < BigInt(amount)) {
|
|
2149
|
+
throw new OwneyError(
|
|
2150
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2151
|
+
"Insufficient balance for this deposit.",
|
|
2152
|
+
{ token: token2, chainId: cid, balance: balance.toString(), amount }
|
|
2153
|
+
);
|
|
2154
|
+
}
|
|
2155
|
+
} catch (err) {
|
|
2156
|
+
if (err instanceof OwneyError) throw err;
|
|
2157
|
+
console.warn(
|
|
2158
|
+
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2159
|
+
err instanceof Error ? err.message : String(err)
|
|
2160
|
+
);
|
|
2161
|
+
}
|
|
2162
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token2);
|
|
2163
|
+
const validAfter = 0n;
|
|
2164
|
+
const validBefore = BigInt(
|
|
2165
|
+
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2166
|
+
);
|
|
2167
|
+
const nonce = randomAuthNonce();
|
|
2168
|
+
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2169
|
+
token: token2,
|
|
2170
|
+
chainId: cid,
|
|
2171
|
+
tokenName,
|
|
2172
|
+
tokenVersion,
|
|
2173
|
+
message: {
|
|
2174
|
+
from: deps.ownerAddress,
|
|
2175
|
+
to: smartWallet,
|
|
2176
|
+
value: BigInt(amount),
|
|
2177
|
+
validAfter,
|
|
2178
|
+
validBefore,
|
|
2179
|
+
nonce
|
|
2180
|
+
}
|
|
2181
|
+
});
|
|
2182
|
+
const authSignature = await wallet.signTypedData({
|
|
2183
|
+
account: deps.ownerAddress,
|
|
2184
|
+
...typedData
|
|
2185
|
+
});
|
|
2186
|
+
deps.onApproved?.();
|
|
2187
|
+
const result = await post({
|
|
2188
|
+
baseUrl: deps.baseUrl,
|
|
2189
|
+
apiKey: deps.apiKey,
|
|
2190
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2191
|
+
body: {
|
|
2192
|
+
chainId: cid,
|
|
2193
|
+
token: token2,
|
|
2194
|
+
from: deps.ownerAddress,
|
|
2195
|
+
to: smartWallet,
|
|
2196
|
+
value: amount,
|
|
2197
|
+
validAfter: validAfter.toString(),
|
|
2198
|
+
validBefore: validBefore.toString(),
|
|
2199
|
+
nonce,
|
|
2200
|
+
authSignature,
|
|
2201
|
+
tokenName,
|
|
2202
|
+
tokenVersion
|
|
2203
|
+
}
|
|
2204
|
+
});
|
|
2205
|
+
return result.txHash;
|
|
2206
|
+
}
|
|
2207
|
+
);
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2211
|
+
import { SiweMessage, generateNonce } from "siwe";
|
|
2212
|
+
import {
|
|
2213
|
+
createPublicClient as createPublicClient2,
|
|
2214
|
+
createWalletClient,
|
|
2215
|
+
custom,
|
|
2216
|
+
getAddress
|
|
2217
|
+
} from "viem";
|
|
2218
|
+
import { base as base2 } from "viem/chains";
|
|
2219
|
+
|
|
2220
|
+
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2221
|
+
var KEY_PREFIX2 = "owney.yieldseeker.session";
|
|
2222
|
+
var storage2 = () => {
|
|
2223
|
+
if (typeof window === "undefined") return null;
|
|
2224
|
+
try {
|
|
2225
|
+
return window.localStorage;
|
|
2226
|
+
} catch {
|
|
2227
|
+
return null;
|
|
2228
|
+
}
|
|
2229
|
+
};
|
|
2230
|
+
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2231
|
+
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2232
|
+
var isValidSession = (session) => {
|
|
2233
|
+
if (!session?.token) return false;
|
|
2234
|
+
try {
|
|
2235
|
+
const parsed = JSON.parse(atob(session.token));
|
|
2236
|
+
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2237
|
+
} catch {
|
|
2238
|
+
return false;
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
2241
|
+
var readYieldseekerSession = (address, chainId) => {
|
|
2242
|
+
if (typeof window === "undefined") return null;
|
|
2243
|
+
const key2 = buildKey2(address, chainId);
|
|
2244
|
+
const store = storage2();
|
|
2245
|
+
let raw = null;
|
|
2246
|
+
try {
|
|
2247
|
+
raw = store?.getItem(key2) ?? null;
|
|
2248
|
+
} catch {
|
|
2249
|
+
raw = null;
|
|
2250
|
+
}
|
|
2251
|
+
if (raw) {
|
|
2252
|
+
try {
|
|
2253
|
+
const parsed = JSON.parse(raw);
|
|
2254
|
+
if (isValidSession(parsed)) return parsed.token;
|
|
2255
|
+
} catch {
|
|
2256
|
+
}
|
|
2257
|
+
memorySessions2.delete(key2);
|
|
2258
|
+
try {
|
|
2259
|
+
store?.removeItem(key2);
|
|
2260
|
+
} catch {
|
|
2261
|
+
}
|
|
2262
|
+
return null;
|
|
2263
|
+
}
|
|
2264
|
+
const cached = memorySessions2.get(key2);
|
|
2265
|
+
if (isValidSession(cached)) return cached.token;
|
|
2266
|
+
if (cached) memorySessions2.delete(key2);
|
|
2267
|
+
return null;
|
|
2268
|
+
};
|
|
2269
|
+
var writeYieldseekerSession = (address, chainId, token2) => {
|
|
2270
|
+
if (typeof window === "undefined") return;
|
|
2271
|
+
const session = { token: token2 };
|
|
2272
|
+
if (!isValidSession(session)) return;
|
|
2273
|
+
const key2 = buildKey2(address, chainId);
|
|
2274
|
+
memorySessions2.set(key2, session);
|
|
2275
|
+
const store = storage2();
|
|
2276
|
+
try {
|
|
2277
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
2278
|
+
} catch {
|
|
2279
|
+
}
|
|
2280
|
+
};
|
|
2281
|
+
var clearYieldseekerSession = (address, chainId) => {
|
|
2282
|
+
const key2 = buildKey2(address, chainId);
|
|
2283
|
+
memorySessions2.delete(key2);
|
|
2284
|
+
const store = storage2();
|
|
2285
|
+
try {
|
|
2286
|
+
store?.removeItem(key2);
|
|
2287
|
+
} catch {
|
|
2288
|
+
}
|
|
2289
|
+
};
|
|
2290
|
+
|
|
2291
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2292
|
+
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2293
|
+
return new SiweMessage({
|
|
2294
|
+
domain: "yieldseeker.xyz",
|
|
2295
|
+
address: getAddress(address),
|
|
2296
|
+
uri: "https://yieldseeker.xyz",
|
|
2297
|
+
version: "1",
|
|
2298
|
+
chainId,
|
|
2299
|
+
nonce: (dependencies.nonce ?? generateNonce)(),
|
|
2300
|
+
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2301
|
+
}).prepareMessage();
|
|
2302
|
+
}
|
|
2303
|
+
function encodeYieldseekerAuthToken(token2) {
|
|
2304
|
+
const bytes = new TextEncoder().encode(JSON.stringify(token2));
|
|
2305
|
+
let binary = "";
|
|
2306
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2307
|
+
return btoa(binary);
|
|
2308
|
+
}
|
|
2309
|
+
var YieldseekerAuth = class {
|
|
2310
|
+
constructor(dependencies = {}) {
|
|
2311
|
+
this.dependencies = dependencies;
|
|
2312
|
+
}
|
|
2313
|
+
dependencies;
|
|
2314
|
+
tokens = /* @__PURE__ */ new Map();
|
|
2315
|
+
pending = /* @__PURE__ */ new Map();
|
|
2316
|
+
scopes = /* @__PURE__ */ new Map();
|
|
2317
|
+
key(state, chainId) {
|
|
2318
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
2319
|
+
}
|
|
2320
|
+
async getToken(state, chainId) {
|
|
2321
|
+
const key2 = this.key(state, chainId);
|
|
2322
|
+
const scope = { address: state.walletAddress, chainId };
|
|
2323
|
+
this.scopes.set(key2, scope);
|
|
2324
|
+
const cached = this.tokens.get(key2);
|
|
2325
|
+
if (cached) return cached;
|
|
2326
|
+
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2327
|
+
if (persisted) {
|
|
2328
|
+
this.tokens.set(key2, persisted);
|
|
2329
|
+
return persisted;
|
|
2330
|
+
}
|
|
2331
|
+
const inFlight = this.pending.get(key2);
|
|
2332
|
+
if (inFlight) return inFlight;
|
|
2333
|
+
const request = this.sign(state, chainId).then((token2) => {
|
|
2334
|
+
this.tokens.set(key2, token2);
|
|
2335
|
+
writeYieldseekerSession(scope.address, scope.chainId, token2);
|
|
2336
|
+
return token2;
|
|
2337
|
+
});
|
|
2338
|
+
this.pending.set(key2, request);
|
|
2339
|
+
try {
|
|
2340
|
+
return await request;
|
|
2341
|
+
} finally {
|
|
2342
|
+
this.pending.delete(key2);
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
clear(state, chainId) {
|
|
2346
|
+
if (!state || chainId === void 0) {
|
|
2347
|
+
for (const scope of this.scopes.values()) {
|
|
2348
|
+
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2349
|
+
}
|
|
2350
|
+
this.tokens.clear();
|
|
2351
|
+
this.pending.clear();
|
|
2352
|
+
this.scopes.clear();
|
|
2353
|
+
return;
|
|
2354
|
+
}
|
|
2355
|
+
const key2 = this.key(state, chainId);
|
|
2356
|
+
this.tokens.delete(key2);
|
|
2357
|
+
this.pending.delete(key2);
|
|
2358
|
+
this.scopes.delete(key2);
|
|
2359
|
+
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2360
|
+
}
|
|
2361
|
+
async sign(state, chainId) {
|
|
2362
|
+
const account = getAddress(state.walletAddress);
|
|
2363
|
+
const publicClient = createPublicClient2({
|
|
2364
|
+
chain: base2,
|
|
2365
|
+
transport: custom(state.provider)
|
|
2366
|
+
});
|
|
2367
|
+
const walletClient = createWalletClient({
|
|
2368
|
+
account,
|
|
2369
|
+
chain: base2,
|
|
2370
|
+
transport: custom(state.provider)
|
|
2371
|
+
});
|
|
2372
|
+
await ensureWalletOnChain(
|
|
2373
|
+
publicClient,
|
|
2374
|
+
walletClient,
|
|
2375
|
+
8453
|
|
2376
|
+
);
|
|
2377
|
+
const message = createYieldseekerSiweMessage(
|
|
2378
|
+
account,
|
|
2379
|
+
chainId,
|
|
2380
|
+
this.dependencies
|
|
2381
|
+
);
|
|
2382
|
+
const signature = await walletClient.signMessage({ account, message });
|
|
2383
|
+
return encodeYieldseekerAuthToken({ message, signature });
|
|
2384
|
+
}
|
|
2385
|
+
};
|
|
2386
|
+
|
|
2387
|
+
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2388
|
+
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2389
|
+
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2390
|
+
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2391
|
+
}
|
|
2392
|
+
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2393
|
+
var YieldseekerApiError = class extends Error {
|
|
2394
|
+
constructor(status, providerCode, responseFields) {
|
|
2395
|
+
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2396
|
+
this.status = status;
|
|
2397
|
+
this.providerCode = providerCode;
|
|
2398
|
+
this.responseFields = responseFields;
|
|
2399
|
+
this.name = "YieldseekerApiError";
|
|
2400
|
+
}
|
|
2401
|
+
status;
|
|
2402
|
+
providerCode;
|
|
2403
|
+
responseFields;
|
|
2404
|
+
get isAuthenticationError() {
|
|
2405
|
+
return this.status === 401 || this.status === 403;
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2408
|
+
function providerError(body, fallback) {
|
|
2409
|
+
if (!body || typeof body !== "object") return { code: fallback };
|
|
2410
|
+
const record = body;
|
|
2411
|
+
return {
|
|
2412
|
+
code: typeof record.message === "string" ? record.message : fallback,
|
|
2413
|
+
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
var YieldseekerApiClient = class {
|
|
2417
|
+
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
|
|
2418
|
+
this.owneyApiKey = owneyApiKey;
|
|
2419
|
+
this.baseUrl = baseUrl;
|
|
2420
|
+
this.fetchFn = fetchFn;
|
|
2421
|
+
}
|
|
2422
|
+
owneyApiKey;
|
|
2423
|
+
baseUrl;
|
|
2424
|
+
fetchFn;
|
|
2425
|
+
async request(path, options = {}) {
|
|
2426
|
+
const controller = new AbortController();
|
|
2427
|
+
const timer = setTimeout(
|
|
2428
|
+
() => controller.abort(),
|
|
2429
|
+
options.timeoutMs ?? 15e3
|
|
2430
|
+
);
|
|
2431
|
+
try {
|
|
2432
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2433
|
+
method: options.method ?? "GET",
|
|
2434
|
+
headers: {
|
|
2435
|
+
"Content-Type": "application/json",
|
|
2436
|
+
"x-owney-api-key": this.owneyApiKey,
|
|
2437
|
+
...options.signature ? { "X-Signature": options.signature } : {}
|
|
2438
|
+
},
|
|
2439
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2440
|
+
signal: controller.signal
|
|
2441
|
+
});
|
|
2442
|
+
const payload = await response.json().catch(() => null);
|
|
2443
|
+
if (!response.ok) {
|
|
2444
|
+
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2445
|
+
throw new YieldseekerApiError(
|
|
2446
|
+
response.status,
|
|
2447
|
+
error.code,
|
|
2448
|
+
error.fields
|
|
2449
|
+
);
|
|
2450
|
+
}
|
|
2451
|
+
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2452
|
+
return payload.data;
|
|
2453
|
+
}
|
|
2454
|
+
return payload;
|
|
2455
|
+
} catch (error) {
|
|
2456
|
+
if (error instanceof YieldseekerApiError) throw error;
|
|
2457
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2458
|
+
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2459
|
+
}
|
|
2460
|
+
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2461
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2462
|
+
});
|
|
2463
|
+
} finally {
|
|
2464
|
+
clearTimeout(timer);
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
};
|
|
2468
|
+
|
|
2469
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2470
|
+
import { formatUnits } from "viem";
|
|
2471
|
+
var REBALANCE_AMOUNT_DECIMALS = {
|
|
2472
|
+
USDC: 6,
|
|
2473
|
+
WETH: 18
|
|
2474
|
+
};
|
|
2475
|
+
function rebalanceAmount(value, tokenSymbol) {
|
|
2476
|
+
const decimals = REBALANCE_AMOUNT_DECIMALS[tokenSymbol.toUpperCase()];
|
|
2477
|
+
if (decimals === void 0) {
|
|
2478
|
+
return invalid(
|
|
2479
|
+
"history",
|
|
2480
|
+
`unsupported rebalance token ${tokenSymbol || "<empty>"}`
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2483
|
+
try {
|
|
2484
|
+
return formatUnits(BigInt(value), decimals);
|
|
2485
|
+
} catch {
|
|
2486
|
+
return invalid("history", `invalid rebalance amount ${value}`);
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
function invalid(endpoint, detail) {
|
|
2490
|
+
throw new OwneyError(
|
|
2491
|
+
"AGENT_INVALID_RESPONSE",
|
|
2492
|
+
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2493
|
+
{ endpoint, detail },
|
|
2494
|
+
"yieldseeker"
|
|
2495
|
+
);
|
|
2496
|
+
}
|
|
2497
|
+
function requireObject(value, endpoint) {
|
|
2498
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2499
|
+
return invalid(endpoint, "expected an object");
|
|
2500
|
+
}
|
|
2501
|
+
return value;
|
|
2502
|
+
}
|
|
2503
|
+
function requireEnvelope(value, key2, endpoint) {
|
|
2504
|
+
const root = requireObject(value, endpoint);
|
|
2505
|
+
return requireObject(root[key2], endpoint);
|
|
2506
|
+
}
|
|
2507
|
+
function token(value) {
|
|
2508
|
+
return {
|
|
2509
|
+
chain: value.chain,
|
|
2510
|
+
chainId: value.chainId,
|
|
2511
|
+
asset: value.asset,
|
|
2512
|
+
amount: String(value.amount)
|
|
2513
|
+
};
|
|
2514
|
+
}
|
|
2515
|
+
function position(value) {
|
|
2516
|
+
return {
|
|
2517
|
+
chain: String(value.chain),
|
|
2518
|
+
protocol: String(value.protocol),
|
|
2519
|
+
...value.protocolId ? { protocolId: value.protocolId } : {},
|
|
2520
|
+
...value.pool ? { pool: value.pool } : {},
|
|
2521
|
+
asset: String(value.asset),
|
|
2522
|
+
amount: String(value.amount),
|
|
2523
|
+
...value.amountRaw !== void 0 ? { amountRaw: value.amountRaw } : {},
|
|
2524
|
+
...typeof value.apy === "number" ? { apy: value.apy } : {},
|
|
2525
|
+
...typeof value.tvl === "number" ? { tvl: value.tvl } : {},
|
|
2526
|
+
...typeof value.liquidity === "number" ? { liquidity: value.liquidity } : {}
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
function mapApyMatrix(matrix) {
|
|
2530
|
+
const mapped = {};
|
|
2531
|
+
for (const [chain, assets] of Object.entries(matrix ?? {})) {
|
|
2532
|
+
const chainId = Number(chain);
|
|
2533
|
+
if (chainId !== 8453 || !assets || typeof assets !== "object") continue;
|
|
2534
|
+
mapped[8453] = {};
|
|
2535
|
+
for (const [asset, apy] of Object.entries(assets)) {
|
|
2536
|
+
if ((asset === "USDC" || asset === "WETH") && typeof apy === "number") {
|
|
2537
|
+
mapped[8453][asset] = apy;
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
return mapped;
|
|
2542
|
+
}
|
|
2543
|
+
function mapYieldseekerProfile(value) {
|
|
2544
|
+
const profile = requireEnvelope(
|
|
2545
|
+
value,
|
|
2546
|
+
"profile",
|
|
2547
|
+
"profile"
|
|
2548
|
+
);
|
|
2549
|
+
if (typeof profile.address !== "string" || typeof profile.smartWallet !== "string" || !Array.isArray(profile.chains) || typeof profile.hasActiveSessionKey !== "boolean" || !Array.isArray(profile.protocols)) {
|
|
2550
|
+
return invalid("profile", "missing required fields");
|
|
2551
|
+
}
|
|
2552
|
+
return {
|
|
2553
|
+
address: profile.address,
|
|
2554
|
+
smartWallet: profile.smartWallet,
|
|
2555
|
+
chains: profile.chains.map(Number),
|
|
2556
|
+
hasActiveSessionKey: profile.hasActiveSessionKey,
|
|
2557
|
+
protocols: profile.protocols.map(String)
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
function mapYieldseekerBalances(value) {
|
|
2561
|
+
const balances = requireEnvelope(
|
|
2562
|
+
value,
|
|
2563
|
+
"balances",
|
|
2564
|
+
"balances"
|
|
2565
|
+
);
|
|
2566
|
+
if (typeof balances.smartWallet !== "string" || typeof balances.totalBalance !== "string" || typeof balances.totalBalanceAsset !== "string" || !Array.isArray(balances.tokens) || !Array.isArray(balances.positions)) {
|
|
2567
|
+
return invalid("balances", "missing required fields");
|
|
2568
|
+
}
|
|
2569
|
+
return {
|
|
2570
|
+
smartWallet: balances.smartWallet,
|
|
2571
|
+
totalBalance: balances.totalBalance,
|
|
2572
|
+
totalBalanceAsset: balances.totalBalanceAsset.toLowerCase(),
|
|
2573
|
+
tokens: balances.tokens.map(token),
|
|
2574
|
+
positions: balances.positions.map(position)
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
function mapYieldseekerEarnings(value) {
|
|
2578
|
+
const earnings = requireEnvelope(
|
|
2579
|
+
value,
|
|
2580
|
+
"earnings",
|
|
2581
|
+
"earnings"
|
|
2582
|
+
);
|
|
2583
|
+
if (typeof earnings.smartWallet !== "string" || typeof earnings.lifetimeEarnings !== "number" || !Array.isArray(earnings.tokens)) {
|
|
2584
|
+
return invalid("earnings", "missing required fields");
|
|
2585
|
+
}
|
|
2586
|
+
return {
|
|
2587
|
+
smartWallet: earnings.smartWallet,
|
|
2588
|
+
lifetimeEarnings: earnings.lifetimeEarnings,
|
|
2589
|
+
tokens: earnings.tokens.map(token)
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
function mapYieldseekerApy(value) {
|
|
2593
|
+
const apy = requireEnvelope(
|
|
2594
|
+
value,
|
|
2595
|
+
"apy",
|
|
2596
|
+
"apy"
|
|
2597
|
+
);
|
|
2598
|
+
if (typeof apy.walletAddress !== "string" || typeof apy.weightedApyAfterFee !== "number" || !apy.apyByChainAndAsset || typeof apy.apyByChainAndAsset !== "object" || !Array.isArray(apy.history)) {
|
|
2599
|
+
return invalid("apy", "missing required fields");
|
|
2600
|
+
}
|
|
2601
|
+
return {
|
|
2602
|
+
walletAddress: apy.walletAddress,
|
|
2603
|
+
weightedApyAfterFee: apy.weightedApyAfterFee,
|
|
2604
|
+
apyByChainAndAsset: mapApyMatrix(apy.apyByChainAndAsset),
|
|
2605
|
+
history: apy.history.map((point) => ({
|
|
2606
|
+
date: String(point.date),
|
|
2607
|
+
apy: Number(point.apy)
|
|
2608
|
+
}))
|
|
2609
|
+
};
|
|
2610
|
+
}
|
|
2611
|
+
function historyAction(value) {
|
|
2612
|
+
const normalized = value.trim().toLowerCase();
|
|
2613
|
+
if (normalized === "rebalance") return "Rebalance";
|
|
2614
|
+
if (normalized === "deposit") return "Deposit";
|
|
2615
|
+
if (normalized === "top up" || normalized === "topup") return "Top up";
|
|
2616
|
+
if (normalized === "withdraw" || normalized === "withdrawal")
|
|
2617
|
+
return "Withdraw";
|
|
2618
|
+
if (normalized === "earned" || normalized === "earnings") return "Earned";
|
|
2619
|
+
return invalid("history", `unsupported action ${value}`);
|
|
2620
|
+
}
|
|
2621
|
+
function historyItem(value) {
|
|
2622
|
+
return {
|
|
2623
|
+
agent: "yieldseeker",
|
|
2624
|
+
action: historyAction(value.action),
|
|
2625
|
+
date: String(value.date),
|
|
2626
|
+
oldApy: value.oldApy === null ? null : String(value.oldApy),
|
|
2627
|
+
newApy: value.newApy === null ? null : String(value.newApy),
|
|
2628
|
+
transactions: (value.transactions ?? []).map((transaction) => ({
|
|
2629
|
+
txHashes: transaction.txHashes.map(String),
|
|
2630
|
+
...typeof transaction.chainId === "number" ? { chainId: transaction.chainId } : {},
|
|
2631
|
+
...typeof transaction.tokenSymbol === "string" ? { tokenSymbol: transaction.tokenSymbol } : {},
|
|
2632
|
+
...typeof transaction.amount === "string" ? { amount: transaction.amount } : {}
|
|
2633
|
+
})),
|
|
2634
|
+
rebalanceLog: (value.rebalanceLog ?? []).map((entry) => ({
|
|
2635
|
+
fromProtocol: String(entry.fromProtocol),
|
|
2636
|
+
toProtocol: String(entry.toProtocol),
|
|
2637
|
+
...entry.fromPool ? { fromPool: entry.fromPool } : {},
|
|
2638
|
+
...entry.toPool ? { toPool: entry.toPool } : {},
|
|
2639
|
+
tokenSymbol: String(entry.tokenSymbol),
|
|
2640
|
+
// Unlike Yieldseeker's other read-response amounts, live history
|
|
2641
|
+
// rebalance amounts are returned in the token's smallest unit. Normalize
|
|
2642
|
+
// them at the adapter boundary so every Owney consumer receives the
|
|
2643
|
+
// shared human-readable decimal shape.
|
|
2644
|
+
amount: rebalanceAmount(String(entry.amount), String(entry.tokenSymbol)),
|
|
2645
|
+
status: entry.status
|
|
2646
|
+
}))
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
function mapYieldseekerHistory(value) {
|
|
2650
|
+
const history = requireEnvelope(
|
|
2651
|
+
value,
|
|
2652
|
+
"history",
|
|
2653
|
+
"history"
|
|
2654
|
+
);
|
|
2655
|
+
if (!Array.isArray(history.data) || typeof history.hasMore !== "boolean") {
|
|
2656
|
+
return invalid("history", "missing required fields");
|
|
2657
|
+
}
|
|
2658
|
+
return {
|
|
2659
|
+
data: history.data.map(historyItem),
|
|
2660
|
+
hasMore: history.hasMore,
|
|
2661
|
+
...history.hasMore && typeof history.nextCursor === "string" ? { nextCursor: history.nextCursor } : {}
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
function mapYieldseekerAgentApy(value) {
|
|
2665
|
+
const agentApy = requireEnvelope(
|
|
2666
|
+
value,
|
|
2667
|
+
"agentApy",
|
|
2668
|
+
"agent APY"
|
|
2669
|
+
);
|
|
2670
|
+
if (typeof agentApy.averageApy !== "number") {
|
|
2671
|
+
return invalid("agent APY", "missing averageApy");
|
|
2672
|
+
}
|
|
2673
|
+
return {
|
|
2674
|
+
averageApy: agentApy.averageApy,
|
|
2675
|
+
...agentApy.detailedApys ? {
|
|
2676
|
+
detailedApys: {
|
|
2677
|
+
apyPerAsset: mapApyMatrix(agentApy.detailedApys.apyPerAsset)
|
|
2678
|
+
}
|
|
2679
|
+
} : {}
|
|
2680
|
+
};
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2684
|
+
function query(params) {
|
|
2685
|
+
const search = new URLSearchParams();
|
|
2686
|
+
for (const [key2, value] of Object.entries(params)) {
|
|
2687
|
+
if (value !== void 0) search.set(key2, String(value));
|
|
2688
|
+
}
|
|
2689
|
+
const encoded = search.toString();
|
|
2690
|
+
return encoded ? `?${encoded}` : "";
|
|
2691
|
+
}
|
|
2692
|
+
var YieldseekerAgent = class {
|
|
2693
|
+
id = "yieldseeker";
|
|
2694
|
+
balanceComposition = "tokens-plus-positions";
|
|
2695
|
+
supportedChainIds = [8453];
|
|
2696
|
+
supportedAssets = [
|
|
2697
|
+
{
|
|
2698
|
+
chainId: 8453,
|
|
2699
|
+
chain: "BASE",
|
|
2700
|
+
assets: [
|
|
2701
|
+
{ symbol: "USDC", minDepositAmount: "1" },
|
|
2702
|
+
{ symbol: "WETH", minDepositAmount: "1" }
|
|
2703
|
+
]
|
|
2704
|
+
}
|
|
2705
|
+
];
|
|
2706
|
+
api;
|
|
2707
|
+
auth;
|
|
2708
|
+
transactionExecutor;
|
|
2709
|
+
unwindReceiptWaiter;
|
|
2710
|
+
activated = /* @__PURE__ */ new Set();
|
|
2711
|
+
constructor(owneyApiKey, options = {}) {
|
|
2712
|
+
this.api = new YieldseekerApiClient(
|
|
2713
|
+
owneyApiKey,
|
|
2714
|
+
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
2715
|
+
options.fetchFn
|
|
2716
|
+
);
|
|
2717
|
+
this.auth = new YieldseekerAuth(options.auth);
|
|
2718
|
+
this.transactionExecutor = options.transactionExecutor;
|
|
2719
|
+
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
2720
|
+
}
|
|
2721
|
+
async disconnect() {
|
|
2722
|
+
this.auth.clear();
|
|
2723
|
+
this.activated.clear();
|
|
2724
|
+
}
|
|
2725
|
+
async activateAgent(state, chainId, asset) {
|
|
2726
|
+
this.assertChain(chainId);
|
|
2727
|
+
await this.auth.getToken(state, chainId);
|
|
2728
|
+
if (asset === "USDC" || asset === "WETH") {
|
|
2729
|
+
await this.ensureActivated(state, chainId, asset);
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
2733
|
+
this.assertChain(chainId);
|
|
2734
|
+
this.assertAsset(asset);
|
|
2735
|
+
if (BigInt(amount) <= 0n) {
|
|
2736
|
+
throw new OwneyError(
|
|
2737
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
2738
|
+
"Yieldseeker deposits must be greater than zero.",
|
|
2739
|
+
{ amount, minDepositAmount: "1" },
|
|
2740
|
+
this.id
|
|
2741
|
+
);
|
|
2742
|
+
}
|
|
2743
|
+
await this.ensureActivated(state, chainId, asset);
|
|
2744
|
+
const response = await this.walletRequest(
|
|
2745
|
+
state,
|
|
2746
|
+
chainId,
|
|
2747
|
+
"/deposits",
|
|
2748
|
+
{ method: "POST", body: { chainId, asset, amount } }
|
|
2749
|
+
);
|
|
2750
|
+
const deposit = response?.deposit;
|
|
2751
|
+
if (!deposit?.transaction || typeof deposit.amount !== "string" || deposit.amount !== amount || typeof deposit.smartWallet !== "string" || !isAddress(deposit.smartWallet)) {
|
|
2752
|
+
throw this.invalidResponse("deposit");
|
|
2753
|
+
}
|
|
2754
|
+
if (depositCallback) {
|
|
2755
|
+
provideDepositVerificationContext(depositCallback, {
|
|
2756
|
+
agentId: "yieldseeker",
|
|
2757
|
+
// Re-read after /deposits so an authentication retry cannot forward
|
|
2758
|
+
// the stale token that Yieldseeker just rejected.
|
|
2759
|
+
signature: await this.auth.getToken(state, chainId)
|
|
2760
|
+
});
|
|
2761
|
+
}
|
|
2762
|
+
const txHash = depositCallback ? await depositCallback(deposit.smartWallet, chainId, amount) : await this.submitTransaction(state, chainId, deposit.transaction);
|
|
2763
|
+
const confirmation = await this.walletRequest(
|
|
2764
|
+
state,
|
|
2765
|
+
chainId,
|
|
2766
|
+
"/deposits/confirm",
|
|
2767
|
+
{ method: "POST", body: { chainId, asset } }
|
|
2768
|
+
);
|
|
2769
|
+
if (typeof confirmation?.confirmation?.autoseekQueued !== "boolean") {
|
|
2770
|
+
throw this.invalidResponse("deposit confirmation", {
|
|
2771
|
+
transactionHash: txHash,
|
|
2772
|
+
transactionConfirmed: true
|
|
2773
|
+
});
|
|
2774
|
+
}
|
|
2775
|
+
return {
|
|
2776
|
+
txHash,
|
|
2777
|
+
smartWallet: deposit.smartWallet,
|
|
2778
|
+
amount: deposit.amount
|
|
2779
|
+
};
|
|
2780
|
+
}
|
|
2781
|
+
async withdraw(state, chainId, asset, amount) {
|
|
2782
|
+
this.assertChain(chainId);
|
|
2783
|
+
this.assertAsset(asset);
|
|
2784
|
+
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
2785
|
+
throw new OwneyError(
|
|
2786
|
+
"WITHDRAW_FAILED",
|
|
2787
|
+
"Yieldseeker withdrawals must be greater than zero.",
|
|
2788
|
+
{ amount },
|
|
2789
|
+
this.id
|
|
2790
|
+
);
|
|
2791
|
+
}
|
|
2792
|
+
const balances = await this.getBalances(state, chainId);
|
|
2793
|
+
const decimals = asset === "USDC" ? 6 : 18;
|
|
2794
|
+
const targetAsset = asset.toUpperCase();
|
|
2795
|
+
const idle = balances.tokens.filter(
|
|
2796
|
+
(token2) => token2.chainId === chainId && token2.asset.toUpperCase() === targetAsset
|
|
2797
|
+
).reduce((total, token2) => total + parseUnits(token2.amount, decimals), 0n);
|
|
2798
|
+
const positions = (balances.positions ?? []).filter(
|
|
2799
|
+
(position2) => this.positionMatchesChain(position2.chain, chainId) && position2.asset.toUpperCase() === targetAsset
|
|
2800
|
+
).map((position2) => ({
|
|
2801
|
+
position: position2,
|
|
2802
|
+
amount: position2.amountRaw !== void 0 ? BigInt(position2.amountRaw) : parseUnits(position2.amount, decimals)
|
|
2803
|
+
}));
|
|
2804
|
+
const deployed = positions.reduce(
|
|
2805
|
+
(total, position2) => total + position2.amount,
|
|
2806
|
+
0n
|
|
2807
|
+
);
|
|
2808
|
+
const totalAvailable = idle + deployed;
|
|
2809
|
+
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
2810
|
+
if (requested > totalAvailable) {
|
|
2811
|
+
throw new OwneyError(
|
|
2812
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
2813
|
+
`Requested withdrawal "${requested.toString()}" exceeds available Yieldseeker balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
2814
|
+
{
|
|
2815
|
+
asset,
|
|
2816
|
+
requested: requested.toString(),
|
|
2817
|
+
available: totalAvailable.toString()
|
|
2818
|
+
},
|
|
2819
|
+
this.id
|
|
2820
|
+
);
|
|
2821
|
+
}
|
|
2822
|
+
const requiredFromPositions = requested > idle ? requested - idle : 0n;
|
|
2823
|
+
const unwindPlans = [];
|
|
2824
|
+
let remainingToUnwind = requiredFromPositions;
|
|
2825
|
+
for (const { position: position2, amount: positionAmount } of positions) {
|
|
2826
|
+
if (remainingToUnwind === 0n) break;
|
|
2827
|
+
if (positionAmount <= 0n) continue;
|
|
2828
|
+
if (!position2.protocolId || !isAddress(position2.protocolId)) {
|
|
2829
|
+
throw this.invalidResponse("position", {
|
|
2830
|
+
reason: "A deployed position is missing its vault address.",
|
|
2831
|
+
protocol: position2.protocol,
|
|
2832
|
+
pool: position2.pool
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
const unwindAmount = positionAmount < remainingToUnwind ? positionAmount : remainingToUnwind;
|
|
2836
|
+
unwindPlans.push({
|
|
2837
|
+
vaultAddress: getAddress2(position2.protocolId),
|
|
2838
|
+
amount: unwindAmount
|
|
2839
|
+
});
|
|
2840
|
+
remainingToUnwind -= unwindAmount;
|
|
2841
|
+
}
|
|
2842
|
+
if (remainingToUnwind > 0n) {
|
|
2843
|
+
throw this.invalidResponse("balances", {
|
|
2844
|
+
reason: "Deployed position balances could not cover the unwind.",
|
|
2845
|
+
required: requiredFromPositions.toString(),
|
|
2846
|
+
planned: (requiredFromPositions - remainingToUnwind).toString()
|
|
2847
|
+
});
|
|
2848
|
+
}
|
|
2849
|
+
for (const plan of unwindPlans) {
|
|
2850
|
+
const response2 = await this.walletRequest(
|
|
2851
|
+
state,
|
|
2852
|
+
chainId,
|
|
2853
|
+
"/positions/unwind",
|
|
2854
|
+
{
|
|
2855
|
+
method: "POST",
|
|
2856
|
+
body: {
|
|
2857
|
+
chainId,
|
|
2858
|
+
asset,
|
|
2859
|
+
vaultAddress: plan.vaultAddress,
|
|
2860
|
+
amount: plan.amount.toString()
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
);
|
|
2864
|
+
const transactionHash = response2?.unwind?.transactionHash;
|
|
2865
|
+
if (!this.isTransactionHash(transactionHash)) {
|
|
2866
|
+
throw this.invalidResponse("position unwind");
|
|
2867
|
+
}
|
|
2868
|
+
await this.waitForUnwindReceipt(state, chainId, transactionHash);
|
|
2869
|
+
}
|
|
2870
|
+
const response = await this.walletRequest(
|
|
2871
|
+
state,
|
|
2872
|
+
chainId,
|
|
2873
|
+
"/withdrawals",
|
|
2874
|
+
{
|
|
2875
|
+
method: "POST",
|
|
2876
|
+
body: { chainId, asset, ...amount !== void 0 ? { amount } : {} }
|
|
2877
|
+
}
|
|
2878
|
+
);
|
|
2879
|
+
const withdrawal = response?.withdrawal;
|
|
2880
|
+
if (!withdrawal?.transaction || typeof withdrawal.amount !== "string") {
|
|
2881
|
+
throw this.invalidResponse("withdrawal");
|
|
2882
|
+
}
|
|
2883
|
+
const txHash = await this.submitTransaction(
|
|
2884
|
+
state,
|
|
2885
|
+
chainId,
|
|
2886
|
+
withdrawal.transaction
|
|
2887
|
+
);
|
|
2888
|
+
return {
|
|
2889
|
+
txHash,
|
|
2890
|
+
type: amount === void 0 ? "full" : "partial",
|
|
2891
|
+
amount: withdrawal.amount
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2894
|
+
async getBalances(state, chainId) {
|
|
2895
|
+
return mapYieldseekerBalances(
|
|
2896
|
+
await this.walletRequest(
|
|
2897
|
+
state,
|
|
2898
|
+
chainId,
|
|
2899
|
+
`/balances${query({ chainId })}`
|
|
2900
|
+
)
|
|
2901
|
+
);
|
|
2902
|
+
}
|
|
2903
|
+
async getEarnings(state, chainId) {
|
|
2904
|
+
return mapYieldseekerEarnings(
|
|
2905
|
+
await this.walletRequest(
|
|
2906
|
+
state,
|
|
2907
|
+
chainId,
|
|
2908
|
+
`/earnings${query({ chainId })}`
|
|
2909
|
+
)
|
|
2910
|
+
);
|
|
2911
|
+
}
|
|
2912
|
+
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
2913
|
+
return mapYieldseekerApy(
|
|
2914
|
+
await this.walletRequest(
|
|
2915
|
+
state,
|
|
2916
|
+
chainId,
|
|
2917
|
+
`/apy${query({ chainId, days, tokenSymbol })}`
|
|
2918
|
+
)
|
|
2919
|
+
);
|
|
2920
|
+
}
|
|
2921
|
+
async getHistory(state, chainId, options) {
|
|
2922
|
+
return mapYieldseekerHistory(
|
|
2923
|
+
await this.walletRequest(
|
|
2924
|
+
state,
|
|
2925
|
+
chainId,
|
|
2926
|
+
`/history${query({
|
|
2927
|
+
chainId,
|
|
2928
|
+
limit: options?.limit ?? 10,
|
|
2929
|
+
cursor: options?.cursor,
|
|
2930
|
+
fromDate: options?.fromDate,
|
|
2931
|
+
toDate: options?.toDate
|
|
2932
|
+
})}`
|
|
2933
|
+
)
|
|
2934
|
+
);
|
|
2935
|
+
}
|
|
2936
|
+
async getUserProfile(state, chainId) {
|
|
2937
|
+
return mapYieldseekerProfile(
|
|
2938
|
+
await this.walletRequest(state, chainId, `/profile${query({ chainId })}`)
|
|
2939
|
+
);
|
|
2940
|
+
}
|
|
2941
|
+
async getAgentApy(days, options) {
|
|
2942
|
+
this.assertOptionalChain(options?.chainId);
|
|
2943
|
+
return mapYieldseekerAgentApy(
|
|
2944
|
+
await this.api.request(
|
|
2945
|
+
`/agent/apy${query({
|
|
2946
|
+
days,
|
|
2947
|
+
tokenSymbol: options?.tokenSymbol,
|
|
2948
|
+
chainId: options?.chainId
|
|
2949
|
+
})}`
|
|
2950
|
+
)
|
|
2951
|
+
);
|
|
2952
|
+
}
|
|
2953
|
+
async ensureActivated(state, chainId, asset) {
|
|
2954
|
+
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${asset}`;
|
|
2955
|
+
if (this.activated.has(key2)) return;
|
|
2956
|
+
const response = await this.walletRequest(
|
|
2957
|
+
state,
|
|
2958
|
+
chainId,
|
|
2959
|
+
"/activation",
|
|
2960
|
+
{ method: "POST", body: { chainId, asset } }
|
|
2961
|
+
);
|
|
2962
|
+
if (typeof response?.activation?.smartWallet !== "string" || typeof response.activation.deployed !== "boolean" || typeof response.activation.hasActiveSessionKey !== "boolean") {
|
|
2963
|
+
throw this.invalidResponse("activation");
|
|
2964
|
+
}
|
|
2965
|
+
this.activated.add(key2);
|
|
2966
|
+
}
|
|
2967
|
+
async walletRequest(state, chainId, path, options = {}) {
|
|
2968
|
+
this.assertChain(chainId);
|
|
2969
|
+
let signature = await this.auth.getToken(state, chainId);
|
|
2970
|
+
try {
|
|
2971
|
+
return await this.api.request(path, { ...options, signature });
|
|
2972
|
+
} catch (error) {
|
|
2973
|
+
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
2974
|
+
if (error.isAuthenticationError) {
|
|
2975
|
+
this.auth.clear(state, chainId);
|
|
2976
|
+
signature = await this.auth.getToken(state, chainId);
|
|
2977
|
+
try {
|
|
2978
|
+
return await this.api.request(path, { ...options, signature });
|
|
2979
|
+
} catch (retryError) {
|
|
2980
|
+
throw this.mapApiError(retryError);
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
throw this.mapApiError(error);
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
mapApiError(error) {
|
|
2987
|
+
if (!(error instanceof YieldseekerApiError)) {
|
|
2988
|
+
return new OwneyError(
|
|
2989
|
+
"AGENT_API_ERROR",
|
|
2990
|
+
"Yieldseeker request failed.",
|
|
2991
|
+
{ cause: error instanceof Error ? error.message : String(error) },
|
|
2992
|
+
this.id
|
|
2993
|
+
);
|
|
2994
|
+
}
|
|
2995
|
+
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
2996
|
+
return new OwneyError(
|
|
2997
|
+
code,
|
|
2998
|
+
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
2999
|
+
{
|
|
3000
|
+
statusCode: error.status,
|
|
3001
|
+
providerCode: error.providerCode,
|
|
3002
|
+
...error.responseFields ? { fields: error.responseFields } : {}
|
|
3003
|
+
},
|
|
3004
|
+
this.id
|
|
3005
|
+
);
|
|
3006
|
+
}
|
|
3007
|
+
async submitTransaction(state, chainId, transaction) {
|
|
3008
|
+
if (this.transactionExecutor) {
|
|
3009
|
+
return this.transactionExecutor(state, chainId, transaction);
|
|
3010
|
+
}
|
|
3011
|
+
if (typeof transaction.from !== "string" || typeof transaction.to !== "string" || typeof transaction.data !== "string" || typeof transaction.value !== "string" || transaction.chainId !== chainId) {
|
|
3012
|
+
throw this.invalidResponse("transaction");
|
|
3013
|
+
}
|
|
3014
|
+
const account = getAddress2(state.walletAddress);
|
|
3015
|
+
if (getAddress2(transaction.from) !== account) {
|
|
3016
|
+
throw this.invalidResponse("transaction", {
|
|
3017
|
+
reason: "transaction.from does not match the connected wallet"
|
|
3018
|
+
});
|
|
3019
|
+
}
|
|
3020
|
+
const walletClient = createWalletClient2({
|
|
3021
|
+
account,
|
|
3022
|
+
chain: base3,
|
|
3023
|
+
transport: custom2(state.provider)
|
|
3024
|
+
});
|
|
3025
|
+
const publicClient = createPublicClient3({
|
|
3026
|
+
chain: base3,
|
|
3027
|
+
transport: custom2(state.provider)
|
|
3028
|
+
});
|
|
3029
|
+
await ensureWalletOnChain(
|
|
3030
|
+
publicClient,
|
|
3031
|
+
walletClient,
|
|
3032
|
+
8453
|
|
3033
|
+
);
|
|
3034
|
+
const hash = await walletClient.sendTransaction({
|
|
3035
|
+
account,
|
|
3036
|
+
chain: base3,
|
|
3037
|
+
to: getAddress2(transaction.to),
|
|
3038
|
+
data: transaction.data,
|
|
3039
|
+
value: BigInt(transaction.value)
|
|
3040
|
+
});
|
|
3041
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3042
|
+
hash,
|
|
3043
|
+
confirmations: 1
|
|
3044
|
+
});
|
|
3045
|
+
if (receipt.status !== "success") {
|
|
3046
|
+
throw new OwneyError(
|
|
3047
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3048
|
+
`Yieldseeker transaction reverted (${hash}).`,
|
|
3049
|
+
{ transactionHash: hash },
|
|
3050
|
+
this.id
|
|
3051
|
+
);
|
|
3052
|
+
}
|
|
3053
|
+
return hash;
|
|
3054
|
+
}
|
|
3055
|
+
async waitForUnwindReceipt(state, chainId, transactionHash) {
|
|
3056
|
+
if (this.unwindReceiptWaiter) {
|
|
3057
|
+
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3058
|
+
return;
|
|
3059
|
+
}
|
|
3060
|
+
const publicClient = createPublicClient3({
|
|
3061
|
+
chain: base3,
|
|
3062
|
+
transport: custom2(state.provider)
|
|
3063
|
+
});
|
|
3064
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3065
|
+
hash: transactionHash,
|
|
3066
|
+
confirmations: 1
|
|
3067
|
+
});
|
|
3068
|
+
if (receipt.status !== "success") {
|
|
3069
|
+
throw new OwneyError(
|
|
3070
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3071
|
+
`Yieldseeker position unwind reverted (${transactionHash}).`,
|
|
3072
|
+
{ transactionHash },
|
|
3073
|
+
this.id
|
|
3074
|
+
);
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
positionMatchesChain(chain, chainId) {
|
|
3078
|
+
const normalized = chain.trim().toUpperCase();
|
|
3079
|
+
return normalized === String(chainId) || normalized === "BASE";
|
|
3080
|
+
}
|
|
3081
|
+
isTransactionHash(value) {
|
|
3082
|
+
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
3083
|
+
}
|
|
3084
|
+
assertChain(chainId) {
|
|
3085
|
+
if (chainId !== 8453) {
|
|
3086
|
+
throw new OwneyError(
|
|
3087
|
+
"CHAIN_UNSUPPORTED",
|
|
3088
|
+
`Yieldseeker does not support chain ${chainId}.`,
|
|
3089
|
+
{ chainId, supportedChainIds: [8453] },
|
|
3090
|
+
this.id
|
|
3091
|
+
);
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
assertOptionalChain(chainId) {
|
|
3095
|
+
if (chainId !== void 0) this.assertChain(chainId);
|
|
3096
|
+
}
|
|
3097
|
+
assertAsset(asset) {
|
|
3098
|
+
if (asset !== "USDC" && asset !== "WETH") {
|
|
3099
|
+
throw new OwneyError(
|
|
3100
|
+
"ASSET_UNSUPPORTED",
|
|
3101
|
+
`Yieldseeker does not support asset ${asset}.`,
|
|
3102
|
+
{ asset, supportedAssets: ["USDC", "WETH"] },
|
|
3103
|
+
this.id
|
|
3104
|
+
);
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
invalidResponse(operation, details = {}) {
|
|
3108
|
+
return new OwneyError(
|
|
3109
|
+
"AGENT_INVALID_RESPONSE",
|
|
3110
|
+
`Yieldseeker returned an invalid ${operation} response.`,
|
|
3111
|
+
details,
|
|
3112
|
+
this.id
|
|
3113
|
+
);
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
|
|
3117
|
+
// src/lib/routing-api.ts
|
|
3118
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3119
|
+
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3120
|
+
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
3121
|
+
try {
|
|
3122
|
+
const res = await fetch(url, {
|
|
3123
|
+
method: "GET",
|
|
3124
|
+
headers: {
|
|
3125
|
+
"Content-Type": "application/json",
|
|
3126
|
+
"x-owney-api-key": `${apiKey}`
|
|
3127
|
+
}
|
|
3128
|
+
});
|
|
3129
|
+
if (!res.ok) {
|
|
3130
|
+
if (res.status !== 404) {
|
|
3131
|
+
console.warn(
|
|
3132
|
+
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
3133
|
+
);
|
|
3134
|
+
}
|
|
3135
|
+
return null;
|
|
3136
|
+
}
|
|
3137
|
+
const json = await res.json();
|
|
3138
|
+
const policy = json.success ? json.data ?? null : null;
|
|
3139
|
+
debugLog(
|
|
3140
|
+
"owney-sdk",
|
|
3141
|
+
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
3142
|
+
policy ?? void 0
|
|
3143
|
+
);
|
|
3144
|
+
return policy;
|
|
3145
|
+
} catch (error) {
|
|
3146
|
+
console.warn(
|
|
3147
|
+
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
3148
|
+
error instanceof Error ? error.message : String(error)
|
|
3149
|
+
);
|
|
3150
|
+
return null;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3154
|
+
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
3155
|
+
const res = await fetch(url, {
|
|
3156
|
+
method: "GET",
|
|
3157
|
+
headers: {
|
|
3158
|
+
"Content-Type": "application/json",
|
|
3159
|
+
"x-owney-api-key": `${apiKey}`
|
|
3160
|
+
}
|
|
3161
|
+
});
|
|
3162
|
+
if (!res.ok) {
|
|
3163
|
+
const text = await res.text().catch(() => "");
|
|
3164
|
+
throw new OwneyError(
|
|
3165
|
+
"API_ROUTING_ERROR",
|
|
3166
|
+
`Routing API error ${res.status}: ${text}`,
|
|
3167
|
+
{ statusCode: res.status, responseBody: text }
|
|
3168
|
+
);
|
|
3169
|
+
}
|
|
3170
|
+
const json = await res.json();
|
|
3171
|
+
if (!json.success) {
|
|
3172
|
+
throw new OwneyError(
|
|
3173
|
+
"API_ROUTING_FAILED",
|
|
3174
|
+
`Routing API request failed: ${json.message}`,
|
|
3175
|
+
{ message: json.message }
|
|
3176
|
+
);
|
|
3177
|
+
}
|
|
3178
|
+
return json.data;
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
// src/lib/health-report.ts
|
|
3182
|
+
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3183
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
3184
|
+
try {
|
|
3185
|
+
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
3186
|
+
method: "POST",
|
|
3187
|
+
headers: {
|
|
3188
|
+
"Content-Type": "application/json",
|
|
3189
|
+
"x-owney-api-key": apiKey
|
|
3190
|
+
},
|
|
3191
|
+
body: JSON.stringify({
|
|
3192
|
+
agent_type: agentType,
|
|
3193
|
+
error_code: errorCode,
|
|
3194
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3195
|
+
})
|
|
3196
|
+
});
|
|
3197
|
+
} catch (err) {
|
|
3198
|
+
console.warn(
|
|
3199
|
+
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
3200
|
+
err instanceof Error ? err.message : err
|
|
3201
|
+
);
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
3205
|
+
try {
|
|
3206
|
+
return await fn();
|
|
3207
|
+
} catch (err) {
|
|
3208
|
+
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3209
|
+
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
3210
|
+
throw err;
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
3215
|
+
import { parseUnits as parseUnits2 } from "viem";
|
|
3216
|
+
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
3217
|
+
const target = asset.toUpperCase();
|
|
3218
|
+
return agents.map((agent) => {
|
|
3219
|
+
const agentBalance = aggregated[agent.id];
|
|
3220
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
3221
|
+
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
3222
|
+
);
|
|
3223
|
+
let balance = tokenBalance ? parseUnits2(tokenBalance.amount, decimals) : 0n;
|
|
3224
|
+
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
3225
|
+
const chainNameById = {
|
|
3226
|
+
1: "ETHEREUM",
|
|
3227
|
+
8453: "BASE",
|
|
3228
|
+
42161: "ARBITRUM"
|
|
3229
|
+
};
|
|
3230
|
+
const targetChain = chainNameById[chainId];
|
|
3231
|
+
for (const position2 of agentBalance?.positions ?? []) {
|
|
3232
|
+
const positionChain = position2.chain.trim().toUpperCase();
|
|
3233
|
+
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
3234
|
+
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
3235
|
+
if (position2.amountRaw !== void 0) {
|
|
3236
|
+
try {
|
|
3237
|
+
balance += BigInt(position2.amountRaw);
|
|
3238
|
+
continue;
|
|
3239
|
+
} catch {
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
balance += parseUnits2(position2.amount, decimals);
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
return { agent, balance };
|
|
3246
|
+
});
|
|
3247
|
+
}
|
|
3248
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
3249
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
3250
|
+
agent,
|
|
3251
|
+
balance,
|
|
3252
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
3253
|
+
}));
|
|
3254
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
3255
|
+
let remainder = requested - assigned;
|
|
3256
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
3257
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
3258
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2303
3259
|
});
|
|
3260
|
+
for (const p of byHeadroom) {
|
|
3261
|
+
if (remainder === 0n) break;
|
|
3262
|
+
const headroom = p.balance - p.planned;
|
|
3263
|
+
if (headroom <= 0n) continue;
|
|
3264
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
3265
|
+
p.planned += take;
|
|
3266
|
+
remainder -= take;
|
|
3267
|
+
}
|
|
3268
|
+
return plans;
|
|
2304
3269
|
}
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
}
|
|
2312
|
-
|
|
2313
|
-
|
|
3270
|
+
function planDisabledDrain(disabled, requested) {
|
|
3271
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
3272
|
+
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
3273
|
+
);
|
|
3274
|
+
const plans = [];
|
|
3275
|
+
let remaining = requested;
|
|
3276
|
+
for (const { agent, balance } of sorted) {
|
|
3277
|
+
if (remaining === 0n) {
|
|
3278
|
+
plans.push({ agent, balance, planned: 0n });
|
|
3279
|
+
continue;
|
|
3280
|
+
}
|
|
3281
|
+
const take = balance < remaining ? balance : remaining;
|
|
3282
|
+
plans.push({ agent, balance, planned: take });
|
|
3283
|
+
remaining -= take;
|
|
3284
|
+
}
|
|
3285
|
+
return { plans, remaining };
|
|
2314
3286
|
}
|
|
2315
|
-
|
|
2316
|
-
const
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
3287
|
+
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
3288
|
+
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
3289
|
+
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
3290
|
+
const totalHeadroom = candidates.reduce(
|
|
3291
|
+
(s, c) => s + (c.balance - c.planned),
|
|
3292
|
+
0n
|
|
3293
|
+
);
|
|
3294
|
+
if (totalHeadroom === 0n) return;
|
|
3295
|
+
let distributed = 0n;
|
|
3296
|
+
for (const c of candidates) {
|
|
3297
|
+
const headroom = c.balance - c.planned;
|
|
3298
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
3299
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
3300
|
+
c.planned += give;
|
|
3301
|
+
distributed += give;
|
|
2330
3302
|
}
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
3303
|
+
let leftover = amount - distributed;
|
|
3304
|
+
for (const c of candidates) {
|
|
3305
|
+
if (leftover === 0n) break;
|
|
3306
|
+
const headroom = c.balance - c.planned;
|
|
3307
|
+
if (headroom <= 0n) continue;
|
|
3308
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
3309
|
+
c.planned += take;
|
|
3310
|
+
leftover -= take;
|
|
2338
3311
|
}
|
|
2339
3312
|
}
|
|
3313
|
+
function sumWithdrawnAmount(results) {
|
|
3314
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
3315
|
+
}
|
|
2340
3316
|
|
|
2341
|
-
// src/lib/
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
`No sponsored token configured for chain ${chainId}`
|
|
2352
|
-
);
|
|
3317
|
+
// src/lib/helpers/account-apy-helper.ts
|
|
3318
|
+
function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
3319
|
+
if (!tokenSymbol) {
|
|
3320
|
+
const total = Number(balance.totalBalance);
|
|
3321
|
+
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
3322
|
+
}
|
|
3323
|
+
const normalizedToken = tokenSymbol.toUpperCase();
|
|
3324
|
+
return balance.tokens.reduce((total, token2) => {
|
|
3325
|
+
if (Number(token2.chainId) !== chainId || String(token2.asset).toUpperCase() !== normalizedToken) {
|
|
3326
|
+
return total;
|
|
2353
3327
|
}
|
|
2354
|
-
const
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
account: deps.ownerAddress,
|
|
2395
|
-
...typedData
|
|
2396
|
-
});
|
|
2397
|
-
deps.onApproved?.();
|
|
2398
|
-
const result = await post({
|
|
2399
|
-
baseUrl: deps.baseUrl,
|
|
2400
|
-
apiKey: deps.apiKey,
|
|
2401
|
-
body: {
|
|
2402
|
-
chainId: cid,
|
|
2403
|
-
token,
|
|
2404
|
-
from: deps.ownerAddress,
|
|
2405
|
-
to: smartWallet,
|
|
2406
|
-
value: amount,
|
|
2407
|
-
validAfter: validAfter.toString(),
|
|
2408
|
-
validBefore: validBefore.toString(),
|
|
2409
|
-
nonce,
|
|
2410
|
-
authSignature,
|
|
2411
|
-
tokenName,
|
|
2412
|
-
tokenVersion
|
|
3328
|
+
const amount = Number(token2.amount);
|
|
3329
|
+
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
3330
|
+
}, 0);
|
|
3331
|
+
}
|
|
3332
|
+
function aggregateApyHistory(agentApys, agentBalances) {
|
|
3333
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
3334
|
+
for (const [id, accountApy] of Object.entries(agentApys)) {
|
|
3335
|
+
const balance = agentBalances[id] ?? 0;
|
|
3336
|
+
if (!Number.isFinite(balance) || balance <= 0) continue;
|
|
3337
|
+
for (const point of accountApy.history ?? []) {
|
|
3338
|
+
const apy = Number(point.apy);
|
|
3339
|
+
if (!point.date || !Number.isFinite(apy)) continue;
|
|
3340
|
+
const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
|
|
3341
|
+
current.weightedSum += apy * balance;
|
|
3342
|
+
current.weight += balance;
|
|
3343
|
+
byDate.set(point.date, current);
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
3347
|
+
date,
|
|
3348
|
+
apy: value.weightedSum / value.weight
|
|
3349
|
+
}));
|
|
3350
|
+
}
|
|
3351
|
+
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
3352
|
+
const sums = {};
|
|
3353
|
+
const weights = {};
|
|
3354
|
+
for (const id of Object.keys(agentApys)) {
|
|
3355
|
+
const cells = agentApys[id].apyByChainAndAsset;
|
|
3356
|
+
const balance = agentBalances[id] ?? 0;
|
|
3357
|
+
if (!cells || balance <= 0) continue;
|
|
3358
|
+
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
3359
|
+
if (!perAsset) continue;
|
|
3360
|
+
const chainId = Number(chainKey);
|
|
3361
|
+
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
3362
|
+
const apy = Number(apyValue ?? 0);
|
|
3363
|
+
if (apy === 0) continue;
|
|
3364
|
+
sums[chainId] ??= {};
|
|
3365
|
+
weights[chainId] ??= {};
|
|
3366
|
+
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
3367
|
+
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2413
3368
|
}
|
|
2414
|
-
}
|
|
2415
|
-
|
|
2416
|
-
};
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
const out = {};
|
|
3372
|
+
for (const chainKey of Object.keys(sums)) {
|
|
3373
|
+
const chainId = Number(chainKey);
|
|
3374
|
+
const perAssetOut = {};
|
|
3375
|
+
for (const asset of Object.keys(sums[chainId])) {
|
|
3376
|
+
const w = weights[chainId][asset];
|
|
3377
|
+
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
3378
|
+
}
|
|
3379
|
+
if (Object.keys(perAssetOut).length > 0) {
|
|
3380
|
+
out[chainId] = perAssetOut;
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
return out;
|
|
2417
3384
|
}
|
|
2418
3385
|
|
|
3386
|
+
// src/client.ts
|
|
3387
|
+
import {
|
|
3388
|
+
createPublicClient as createPublicClient4,
|
|
3389
|
+
createWalletClient as createWalletClient3,
|
|
3390
|
+
custom as custom3
|
|
3391
|
+
} from "viem";
|
|
3392
|
+
import { base as base4, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
3393
|
+
|
|
2419
3394
|
// src/lib/sponsored-weth-deposit.ts
|
|
2420
3395
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
2421
3396
|
function makeSponsoredWethCallback(deps) {
|
|
2422
3397
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
2423
3398
|
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
2424
|
-
return
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
"CHAIN_UNSUPPORTED",
|
|
2430
|
-
`No sponsored WETH configured for chain ${chainId}`
|
|
2431
|
-
);
|
|
2432
|
-
}
|
|
2433
|
-
const amountWei = BigInt(amount);
|
|
2434
|
-
const pub = deps.getPublicClient(cid);
|
|
2435
|
-
const wallet = deps.getWalletClient(cid);
|
|
2436
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
2437
|
-
try {
|
|
2438
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2439
|
-
if (balance < amountWei) {
|
|
3399
|
+
return makeVerificationAwareDepositCallback(
|
|
3400
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
3401
|
+
const cid = chainId;
|
|
3402
|
+
const token2 = deps.tokenAddressByChain[cid];
|
|
3403
|
+
if (!token2) {
|
|
2440
3404
|
throw new OwneyError(
|
|
2441
|
-
"
|
|
2442
|
-
|
|
2443
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
3405
|
+
"CHAIN_UNSUPPORTED",
|
|
3406
|
+
`No sponsored WETH configured for chain ${chainId}`
|
|
2444
3407
|
);
|
|
2445
3408
|
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
3409
|
+
const amountWei = BigInt(amount);
|
|
3410
|
+
const pub = deps.getPublicClient(cid);
|
|
3411
|
+
const wallet = deps.getWalletClient(cid);
|
|
3412
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
3413
|
+
try {
|
|
3414
|
+
const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
|
|
3415
|
+
if (balance < amountWei) {
|
|
3416
|
+
throw new OwneyError(
|
|
3417
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
3418
|
+
"Insufficient WETH balance for this deposit.",
|
|
3419
|
+
{ token: token2, chainId: cid, balance: balance.toString(), amount }
|
|
3420
|
+
);
|
|
3421
|
+
}
|
|
3422
|
+
} catch (err) {
|
|
3423
|
+
if (err instanceof OwneyError) throw err;
|
|
3424
|
+
console.warn(
|
|
3425
|
+
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
3426
|
+
err instanceof Error ? err.message : String(err)
|
|
3427
|
+
);
|
|
3428
|
+
}
|
|
3429
|
+
const allowance = await readPermit2Allowance(
|
|
3430
|
+
pub,
|
|
3431
|
+
token2,
|
|
3432
|
+
deps.ownerAddress
|
|
2459
3433
|
);
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
const nonce = randomPermit2Nonce();
|
|
2467
|
-
const deadline = BigInt(
|
|
2468
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2469
|
-
);
|
|
2470
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
2471
|
-
chainId: cid,
|
|
2472
|
-
message: {
|
|
2473
|
-
permitted: { token, amount: amountWei },
|
|
2474
|
-
spender: relayer,
|
|
2475
|
-
nonce,
|
|
2476
|
-
deadline
|
|
3434
|
+
if (allowance < amountWei) {
|
|
3435
|
+
throw new OwneyError(
|
|
3436
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
3437
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
3438
|
+
{ token: token2, chainId: cid, allowance: allowance.toString(), amount }
|
|
3439
|
+
);
|
|
2477
3440
|
}
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
3441
|
+
const relayer = await get({
|
|
3442
|
+
baseUrl: deps.baseUrl,
|
|
3443
|
+
apiKey: deps.apiKey,
|
|
3444
|
+
chainId: cid
|
|
3445
|
+
});
|
|
3446
|
+
const nonce = randomPermit2Nonce();
|
|
3447
|
+
const deadline = BigInt(
|
|
3448
|
+
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
3449
|
+
);
|
|
3450
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
2488
3451
|
chainId: cid,
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
3452
|
+
message: {
|
|
3453
|
+
permitted: { token: token2, amount: amountWei },
|
|
3454
|
+
spender: relayer,
|
|
3455
|
+
nonce,
|
|
3456
|
+
deadline
|
|
3457
|
+
}
|
|
3458
|
+
});
|
|
3459
|
+
const signature = await wallet.signTypedData({
|
|
3460
|
+
account: deps.ownerAddress,
|
|
3461
|
+
...typedData
|
|
3462
|
+
});
|
|
3463
|
+
deps.onApproved?.();
|
|
3464
|
+
const result = await post({
|
|
3465
|
+
baseUrl: deps.baseUrl,
|
|
3466
|
+
apiKey: deps.apiKey,
|
|
3467
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
3468
|
+
body: {
|
|
3469
|
+
chainId: cid,
|
|
3470
|
+
token: token2,
|
|
3471
|
+
from: deps.ownerAddress,
|
|
3472
|
+
to: smartWallet,
|
|
3473
|
+
amount,
|
|
3474
|
+
nonce: nonce.toString(),
|
|
3475
|
+
deadline: deadline.toString(),
|
|
3476
|
+
signature
|
|
3477
|
+
}
|
|
3478
|
+
});
|
|
3479
|
+
return result.txHash;
|
|
3480
|
+
}
|
|
3481
|
+
);
|
|
2500
3482
|
}
|
|
2501
3483
|
|
|
2502
3484
|
// src/lib/sponsored-calls-deposit.ts
|
|
@@ -2528,8 +3510,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2528
3510
|
};
|
|
2529
3511
|
return async (smartWallet, chainId, amount) => {
|
|
2530
3512
|
const cid = chainId;
|
|
2531
|
-
const
|
|
2532
|
-
if (!
|
|
3513
|
+
const token2 = deps.tokenAddressByChain[cid];
|
|
3514
|
+
if (!token2) {
|
|
2533
3515
|
throw new OwneyError(
|
|
2534
3516
|
"CHAIN_UNSUPPORTED",
|
|
2535
3517
|
`No sponsored token configured for chain ${chainId}`
|
|
@@ -2555,7 +3537,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2555
3537
|
from: deps.ownerAddress,
|
|
2556
3538
|
chainId: toHex(chainId),
|
|
2557
3539
|
atomicRequired: false,
|
|
2558
|
-
calls: [{ to:
|
|
3540
|
+
calls: [{ to: token2, value: "0x0", data }],
|
|
2559
3541
|
capabilities: {
|
|
2560
3542
|
paymasterService: { url: absolutePaymasterUrl() }
|
|
2561
3543
|
}
|
|
@@ -2594,10 +3576,10 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2594
3576
|
function encodeMultiAgentCursor(map) {
|
|
2595
3577
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
2596
3578
|
}
|
|
2597
|
-
function decodeMultiAgentCursor(
|
|
3579
|
+
function decodeMultiAgentCursor(token2) {
|
|
2598
3580
|
let parsed;
|
|
2599
3581
|
try {
|
|
2600
|
-
const json = Buffer.from(
|
|
3582
|
+
const json = Buffer.from(token2, "base64").toString("utf8");
|
|
2601
3583
|
parsed = JSON.parse(json);
|
|
2602
3584
|
} catch {
|
|
2603
3585
|
throw new InvalidHistoryCursorError(
|
|
@@ -2617,7 +3599,7 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
2617
3599
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
2618
3600
|
};
|
|
2619
3601
|
var VIEM_CHAIN2 = {
|
|
2620
|
-
8453:
|
|
3602
|
+
8453: base4,
|
|
2621
3603
|
42161: arbitrum2,
|
|
2622
3604
|
1: mainnet2
|
|
2623
3605
|
};
|
|
@@ -2647,6 +3629,7 @@ var OwneySDK = class {
|
|
|
2647
3629
|
orgAgentConfig;
|
|
2648
3630
|
orgAgentConfigPromise = null;
|
|
2649
3631
|
zyfaiRpcUrls;
|
|
3632
|
+
yieldseekerApiBaseUrl;
|
|
2650
3633
|
routingApiBaseUrl;
|
|
2651
3634
|
referralSource;
|
|
2652
3635
|
cachedSponsoredCallback = null;
|
|
@@ -2658,6 +3641,7 @@ var OwneySDK = class {
|
|
|
2658
3641
|
this.apiKey = config.apiKey;
|
|
2659
3642
|
if (config.debug) setOwneyDebug(true);
|
|
2660
3643
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
3644
|
+
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
2661
3645
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2662
3646
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
2663
3647
|
this.referralSource = config.referralSource;
|
|
@@ -2760,14 +3744,14 @@ var OwneySDK = class {
|
|
|
2760
3744
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2761
3745
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2762
3746
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2763
|
-
getPublicClient: (cid) =>
|
|
3747
|
+
getPublicClient: (cid) => createPublicClient4({
|
|
2764
3748
|
chain: VIEM_CHAIN2[cid],
|
|
2765
|
-
transport:
|
|
3749
|
+
transport: custom3(provider)
|
|
2766
3750
|
}),
|
|
2767
|
-
getWalletClient: (cid) =>
|
|
3751
|
+
getWalletClient: (cid) => createWalletClient3({
|
|
2768
3752
|
account: owner,
|
|
2769
3753
|
chain: VIEM_CHAIN2[cid],
|
|
2770
|
-
transport:
|
|
3754
|
+
transport: custom3(provider)
|
|
2771
3755
|
})
|
|
2772
3756
|
});
|
|
2773
3757
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -2813,14 +3797,14 @@ var OwneySDK = class {
|
|
|
2813
3797
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2814
3798
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2815
3799
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2816
|
-
getPublicClient: (cid) =>
|
|
3800
|
+
getPublicClient: (cid) => createPublicClient4({
|
|
2817
3801
|
chain: VIEM_CHAIN2[cid],
|
|
2818
|
-
transport:
|
|
3802
|
+
transport: custom3(provider)
|
|
2819
3803
|
}),
|
|
2820
|
-
getWalletClient: (cid) =>
|
|
3804
|
+
getWalletClient: (cid) => createWalletClient3({
|
|
2821
3805
|
account: owner,
|
|
2822
3806
|
chain: VIEM_CHAIN2[cid],
|
|
2823
|
-
transport:
|
|
3807
|
+
transport: custom3(provider)
|
|
2824
3808
|
})
|
|
2825
3809
|
});
|
|
2826
3810
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -2848,12 +3832,13 @@ var OwneySDK = class {
|
|
|
2848
3832
|
async getOrgAgentConfig() {
|
|
2849
3833
|
if (this.orgAgentConfig !== void 0) return this.orgAgentConfig;
|
|
2850
3834
|
if (!this.orgAgentConfigPromise) {
|
|
2851
|
-
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
3835
|
+
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
3836
|
+
this.apiKey,
|
|
3837
|
+
this.routingApiBaseUrl
|
|
3838
|
+
).then((config) => {
|
|
3839
|
+
this.orgAgentConfig = config;
|
|
3840
|
+
return config;
|
|
3841
|
+
});
|
|
2857
3842
|
}
|
|
2858
3843
|
return this.orgAgentConfigPromise;
|
|
2859
3844
|
}
|
|
@@ -2888,9 +3873,19 @@ var OwneySDK = class {
|
|
|
2888
3873
|
if (this.agents.size > 0) return;
|
|
2889
3874
|
if (!this.initializingAgentsPromise) {
|
|
2890
3875
|
this.initializingAgentsPromise = (async () => {
|
|
2891
|
-
const agentKeys = await fetchAgentKeys(
|
|
3876
|
+
const agentKeys = await fetchAgentKeys(
|
|
3877
|
+
this.apiKey,
|
|
3878
|
+
this.routingApiBaseUrl
|
|
3879
|
+
);
|
|
2892
3880
|
this.disabledAgents.clear();
|
|
2893
|
-
for (const {
|
|
3881
|
+
for (const {
|
|
3882
|
+
key: key2,
|
|
3883
|
+
agent_type,
|
|
3884
|
+
is_enabled,
|
|
3885
|
+
is_configured
|
|
3886
|
+
} of agentKeys) {
|
|
3887
|
+
const configured = is_configured ?? Boolean(key2);
|
|
3888
|
+
if (!configured) continue;
|
|
2894
3889
|
const agent = this.createAgent(agent_type, key2);
|
|
2895
3890
|
if (!agent) continue;
|
|
2896
3891
|
this.agents.set(agent_type, agent);
|
|
@@ -2914,10 +3909,47 @@ var OwneySDK = class {
|
|
|
2914
3909
|
}
|
|
2915
3910
|
createAgent(agentId, key2) {
|
|
2916
3911
|
if (agentId === "zyfai") {
|
|
3912
|
+
if (!key2) return null;
|
|
2917
3913
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
2918
3914
|
}
|
|
3915
|
+
if (agentId === "yieldseeker") {
|
|
3916
|
+
return new YieldseekerAgent(this.apiKey, {
|
|
3917
|
+
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
3918
|
+
});
|
|
3919
|
+
}
|
|
2919
3920
|
return null;
|
|
2920
3921
|
}
|
|
3922
|
+
/**
|
|
3923
|
+
* Discover the agents actually returned by routing for this organization.
|
|
3924
|
+
* Consumers should use this instead of hardcoding a global agent roster.
|
|
3925
|
+
*/
|
|
3926
|
+
async getAvailableAgents(options = {}) {
|
|
3927
|
+
await this.ensureAgentsInitialized();
|
|
3928
|
+
const { chainId, asset, includeDisabled = false } = options;
|
|
3929
|
+
const available = [];
|
|
3930
|
+
for (const [id, agent] of this.agents) {
|
|
3931
|
+
const isEnabled = !this.isAgentDisabled(id);
|
|
3932
|
+
if (!includeDisabled && !isEnabled) continue;
|
|
3933
|
+
if (chainId !== void 0 && !agent.supportedChainIds.includes(chainId)) {
|
|
3934
|
+
continue;
|
|
3935
|
+
}
|
|
3936
|
+
if (asset !== void 0) {
|
|
3937
|
+
const supportsAsset = agent.supportedAssets.some(
|
|
3938
|
+
(entry) => (chainId === void 0 || entry.chainId === chainId) && entry.assets.some((candidate) => candidate.symbol === asset)
|
|
3939
|
+
);
|
|
3940
|
+
if (!supportsAsset) {
|
|
3941
|
+
continue;
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
available.push({
|
|
3945
|
+
id,
|
|
3946
|
+
isEnabled,
|
|
3947
|
+
supportedChainIds: agent.supportedChainIds,
|
|
3948
|
+
supportedAssets: agent.supportedAssets
|
|
3949
|
+
});
|
|
3950
|
+
}
|
|
3951
|
+
return available;
|
|
3952
|
+
}
|
|
2921
3953
|
// --- Account lifecycle ---
|
|
2922
3954
|
/**
|
|
2923
3955
|
* Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
|
|
@@ -2929,7 +3961,7 @@ var OwneySDK = class {
|
|
|
2929
3961
|
* If provided, ALL specified agents must support the chainId or the call
|
|
2930
3962
|
* throws before activating any agent.
|
|
2931
3963
|
*/
|
|
2932
|
-
async activateAgent(chainId, agentId) {
|
|
3964
|
+
async activateAgent(chainId, agentId, asset) {
|
|
2933
3965
|
const state = this.requireState();
|
|
2934
3966
|
await this.ensureAgentsInitialized();
|
|
2935
3967
|
if (agentId !== void 0) {
|
|
@@ -2965,9 +3997,7 @@ var OwneySDK = class {
|
|
|
2965
3997
|
this.activeAgents.add(id);
|
|
2966
3998
|
}
|
|
2967
3999
|
state.chainId = chainId;
|
|
2968
|
-
this.activateAgentsInTurn(agents, state, chainId)
|
|
2969
|
-
console.error("activateAgent background init failed:", error);
|
|
2970
|
-
});
|
|
4000
|
+
await this.activateAgentsInTurn(agents, state, chainId, asset);
|
|
2971
4001
|
return;
|
|
2972
4002
|
}
|
|
2973
4003
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -2988,11 +4018,7 @@ var OwneySDK = class {
|
|
|
2988
4018
|
const enabledCompatible = compatible.filter(
|
|
2989
4019
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
2990
4020
|
);
|
|
2991
|
-
this.activateAgentsInTurn(enabledCompatible, state, chainId)
|
|
2992
|
-
(error) => {
|
|
2993
|
-
console.error("activateAgent background init failed:", error);
|
|
2994
|
-
}
|
|
2995
|
-
);
|
|
4021
|
+
await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
|
|
2996
4022
|
}
|
|
2997
4023
|
/**
|
|
2998
4024
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -3012,11 +4038,11 @@ var OwneySDK = class {
|
|
|
3012
4038
|
* rethrown (matching the previous `Promise.all` rejection) once all agents
|
|
3013
4039
|
* have had a chance to activate.
|
|
3014
4040
|
*/
|
|
3015
|
-
async activateAgentsInTurn(agents, state, chainId) {
|
|
4041
|
+
async activateAgentsInTurn(agents, state, chainId, asset) {
|
|
3016
4042
|
let firstError = null;
|
|
3017
4043
|
for (const agent of agents) {
|
|
3018
4044
|
try {
|
|
3019
|
-
await agent.activateAgent(state, chainId);
|
|
4045
|
+
await agent.activateAgent(state, chainId, asset);
|
|
3020
4046
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
3021
4047
|
} catch (error) {
|
|
3022
4048
|
if (firstError === null) {
|
|
@@ -3144,7 +4170,8 @@ var OwneySDK = class {
|
|
|
3144
4170
|
amount,
|
|
3145
4171
|
asset,
|
|
3146
4172
|
callback
|
|
3147
|
-
)
|
|
4173
|
+
),
|
|
4174
|
+
this.routingApiBaseUrl
|
|
3148
4175
|
);
|
|
3149
4176
|
let approvalAttempted = false;
|
|
3150
4177
|
for (; ; ) {
|
|
@@ -3224,10 +4251,10 @@ var OwneySDK = class {
|
|
|
3224
4251
|
try {
|
|
3225
4252
|
const balance = await agent.getBalances(state, chainId);
|
|
3226
4253
|
const target = asset.toLowerCase();
|
|
3227
|
-
const
|
|
4254
|
+
const token2 = balance.tokens.find(
|
|
3228
4255
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
3229
4256
|
);
|
|
3230
|
-
return !!
|
|
4257
|
+
return !!token2 && Number(token2.amount) > 0;
|
|
3231
4258
|
} catch {
|
|
3232
4259
|
return false;
|
|
3233
4260
|
}
|
|
@@ -3278,9 +4305,9 @@ var OwneySDK = class {
|
|
|
3278
4305
|
const { asset, amount, agentId } = options;
|
|
3279
4306
|
const state = this.requireState();
|
|
3280
4307
|
const chainId = this.requireChainId();
|
|
3281
|
-
const
|
|
4308
|
+
const token2 = asset;
|
|
3282
4309
|
const assetInfo = SupportedAssets.find(
|
|
3283
|
-
(a) => a.chainId === chainId && a.symbol ===
|
|
4310
|
+
(a) => a.chainId === chainId && a.symbol === token2
|
|
3284
4311
|
);
|
|
3285
4312
|
if (!assetInfo) {
|
|
3286
4313
|
throw new OwneyError(
|
|
@@ -3294,7 +4321,8 @@ var OwneySDK = class {
|
|
|
3294
4321
|
return withFailureReporting(
|
|
3295
4322
|
this.apiKey,
|
|
3296
4323
|
agent.id,
|
|
3297
|
-
() => agent.withdraw(state, chainId,
|
|
4324
|
+
() => agent.withdraw(state, chainId, token2, amount),
|
|
4325
|
+
this.routingApiBaseUrl
|
|
3298
4326
|
);
|
|
3299
4327
|
}
|
|
3300
4328
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
@@ -3303,12 +4331,17 @@ var OwneySDK = class {
|
|
|
3303
4331
|
const agentErrors2 = {};
|
|
3304
4332
|
for (const agent of eligibleAgents) {
|
|
3305
4333
|
try {
|
|
3306
|
-
results2[agent.id] = await agent.withdraw(state, chainId,
|
|
4334
|
+
results2[agent.id] = await agent.withdraw(state, chainId, token2);
|
|
3307
4335
|
} catch (err) {
|
|
3308
4336
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
3309
4337
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
3310
4338
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3311
|
-
void reportAgentFailure(
|
|
4339
|
+
void reportAgentFailure(
|
|
4340
|
+
this.apiKey,
|
|
4341
|
+
agent.id,
|
|
4342
|
+
code,
|
|
4343
|
+
this.routingApiBaseUrl
|
|
4344
|
+
);
|
|
3312
4345
|
}
|
|
3313
4346
|
}
|
|
3314
4347
|
if (Object.keys(results2).length === 0) {
|
|
@@ -3325,6 +4358,10 @@ var OwneySDK = class {
|
|
|
3325
4358
|
}
|
|
3326
4359
|
const requested = BigInt(amount);
|
|
3327
4360
|
const aggregated = await this.getBalances();
|
|
4361
|
+
const unavailableAgents = eligibleAgents.filter(
|
|
4362
|
+
(agent) => !(agent.id in aggregated.agentBalances)
|
|
4363
|
+
);
|
|
4364
|
+
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
3328
4365
|
const balances = projectAgentBalancesForAsset(
|
|
3329
4366
|
eligibleAgents,
|
|
3330
4367
|
aggregated.agentBalances,
|
|
@@ -3333,7 +4370,18 @@ var OwneySDK = class {
|
|
|
3333
4370
|
assetInfo.decimals
|
|
3334
4371
|
);
|
|
3335
4372
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
3336
|
-
if (totalAvailable
|
|
4373
|
+
if (totalAvailable === 0n && unavailableAgents.length > 0) {
|
|
4374
|
+
throw new OwneyError(
|
|
4375
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
4376
|
+
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
4377
|
+
{
|
|
4378
|
+
asset,
|
|
4379
|
+
unavailableAgents: unavailableAgentIds,
|
|
4380
|
+
agentErrors: aggregated.agentErrors
|
|
4381
|
+
}
|
|
4382
|
+
);
|
|
4383
|
+
}
|
|
4384
|
+
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
3337
4385
|
throw new OwneyError(
|
|
3338
4386
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3339
4387
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -3344,6 +4392,7 @@ var OwneySDK = class {
|
|
|
3344
4392
|
}
|
|
3345
4393
|
);
|
|
3346
4394
|
}
|
|
4395
|
+
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
3347
4396
|
const disabledBalances = balances.filter(
|
|
3348
4397
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
3349
4398
|
);
|
|
@@ -3352,7 +4401,7 @@ var OwneySDK = class {
|
|
|
3352
4401
|
);
|
|
3353
4402
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
3354
4403
|
disabledBalances,
|
|
3355
|
-
|
|
4404
|
+
plannedTarget
|
|
3356
4405
|
);
|
|
3357
4406
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
3358
4407
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -3362,7 +4411,9 @@ var OwneySDK = class {
|
|
|
3362
4411
|
}));
|
|
3363
4412
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
3364
4413
|
const results = {};
|
|
3365
|
-
const agentErrors = {
|
|
4414
|
+
const agentErrors = {
|
|
4415
|
+
...aggregated.agentErrors ?? {}
|
|
4416
|
+
};
|
|
3366
4417
|
for (let i = 0; i < plans.length; i++) {
|
|
3367
4418
|
const p = plans[i];
|
|
3368
4419
|
if (p.planned === 0n) continue;
|
|
@@ -3370,14 +4421,19 @@ var OwneySDK = class {
|
|
|
3370
4421
|
results[p.agent.id] = await p.agent.withdraw(
|
|
3371
4422
|
state,
|
|
3372
4423
|
chainId,
|
|
3373
|
-
|
|
4424
|
+
token2,
|
|
3374
4425
|
p.planned.toString()
|
|
3375
4426
|
);
|
|
3376
4427
|
} catch (err) {
|
|
3377
4428
|
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
3378
4429
|
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
3379
4430
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3380
|
-
void reportAgentFailure(
|
|
4431
|
+
void reportAgentFailure(
|
|
4432
|
+
this.apiKey,
|
|
4433
|
+
p.agent.id,
|
|
4434
|
+
code,
|
|
4435
|
+
this.routingApiBaseUrl
|
|
4436
|
+
);
|
|
3381
4437
|
const failedAmount = p.planned;
|
|
3382
4438
|
p.planned = 0n;
|
|
3383
4439
|
redistributeShare(plans, i, failedAmount);
|
|
@@ -3404,7 +4460,8 @@ var OwneySDK = class {
|
|
|
3404
4460
|
requested: amount,
|
|
3405
4461
|
withdrawn: withdrawn.toString(),
|
|
3406
4462
|
partialResults: results,
|
|
3407
|
-
agentErrors
|
|
4463
|
+
agentErrors,
|
|
4464
|
+
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
3408
4465
|
}
|
|
3409
4466
|
);
|
|
3410
4467
|
}
|
|
@@ -3445,7 +4502,12 @@ var OwneySDK = class {
|
|
|
3445
4502
|
continue;
|
|
3446
4503
|
}
|
|
3447
4504
|
const reason = settledResult.reason;
|
|
3448
|
-
|
|
4505
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
4506
|
+
agentErrors[agentId2] = message;
|
|
4507
|
+
console.error(
|
|
4508
|
+
`[owney-sdk] Balance fetch failed for agent "${agentId2}":`,
|
|
4509
|
+
reason
|
|
4510
|
+
);
|
|
3449
4511
|
}
|
|
3450
4512
|
if (successCount === 0) {
|
|
3451
4513
|
throw new OwneyError(
|
|
@@ -3457,7 +4519,8 @@ var OwneySDK = class {
|
|
|
3457
4519
|
return {
|
|
3458
4520
|
totalBalance: String(totalBalance),
|
|
3459
4521
|
totalBalanceAsset: "usdc",
|
|
3460
|
-
agentBalances: results
|
|
4522
|
+
agentBalances: results,
|
|
4523
|
+
...Object.keys(agentErrors).length > 0 ? { agentErrors } : {}
|
|
3461
4524
|
};
|
|
3462
4525
|
}
|
|
3463
4526
|
/**
|
|
@@ -3551,7 +4614,10 @@ var OwneySDK = class {
|
|
|
3551
4614
|
Promise.all(
|
|
3552
4615
|
entries.map(async ([id, agent]) => {
|
|
3553
4616
|
const b = await agent.getBalances(state, chainId);
|
|
3554
|
-
return [
|
|
4617
|
+
return [
|
|
4618
|
+
id,
|
|
4619
|
+
balanceForApyScope(b, chainId, tokenSymbol)
|
|
4620
|
+
];
|
|
3555
4621
|
})
|
|
3556
4622
|
)
|
|
3557
4623
|
]);
|
|
@@ -3579,10 +4645,12 @@ var OwneySDK = class {
|
|
|
3579
4645
|
}
|
|
3580
4646
|
}
|
|
3581
4647
|
const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
|
|
4648
|
+
const history = aggregateApyHistory(results, balances);
|
|
3582
4649
|
return {
|
|
3583
4650
|
totalApy: String(totalApy),
|
|
3584
4651
|
agentApy: results,
|
|
3585
|
-
apyByChainAndAsset
|
|
4652
|
+
apyByChainAndAsset,
|
|
4653
|
+
history
|
|
3586
4654
|
};
|
|
3587
4655
|
}
|
|
3588
4656
|
/**
|
|
@@ -3704,30 +4772,30 @@ var OwneySDK = class {
|
|
|
3704
4772
|
const state = this.requireState();
|
|
3705
4773
|
const chainId = this.requireChainId();
|
|
3706
4774
|
this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
|
|
3707
|
-
const
|
|
3708
|
-
if (!
|
|
4775
|
+
const token2 = SPONSORED_WETH_BY_CHAIN[chainId];
|
|
4776
|
+
if (!token2) {
|
|
3709
4777
|
throw new OwneyError(
|
|
3710
4778
|
"CHAIN_UNSUPPORTED",
|
|
3711
4779
|
`No sponsored WETH on chain ${chainId}`
|
|
3712
4780
|
);
|
|
3713
4781
|
}
|
|
3714
4782
|
const provider = this.requireConnectedProvider();
|
|
3715
|
-
const wallet =
|
|
4783
|
+
const wallet = createWalletClient3({
|
|
3716
4784
|
account: state.walletAddress,
|
|
3717
4785
|
chain: VIEM_CHAIN2[chainId],
|
|
3718
|
-
transport:
|
|
4786
|
+
transport: custom3(provider)
|
|
3719
4787
|
});
|
|
3720
4788
|
const hash = await wallet.writeContract({
|
|
3721
|
-
address:
|
|
4789
|
+
address: token2,
|
|
3722
4790
|
abi: ERC20_ALLOWANCE_ABI,
|
|
3723
4791
|
functionName: "approve",
|
|
3724
4792
|
args: [PERMIT2_ADDRESS, MAX_UINT256],
|
|
3725
4793
|
account: state.walletAddress,
|
|
3726
4794
|
chain: VIEM_CHAIN2[chainId]
|
|
3727
4795
|
});
|
|
3728
|
-
const publicClient =
|
|
4796
|
+
const publicClient = createPublicClient4({
|
|
3729
4797
|
chain: VIEM_CHAIN2[chainId],
|
|
3730
|
-
transport:
|
|
4798
|
+
transport: custom3(provider)
|
|
3731
4799
|
});
|
|
3732
4800
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3733
4801
|
hash,
|
|
@@ -3763,7 +4831,9 @@ var OwneySDK = class {
|
|
|
3763
4831
|
return this.getAgent(agentId).getAgentApy(days, agentOptions);
|
|
3764
4832
|
}
|
|
3765
4833
|
const results = {};
|
|
3766
|
-
const agentEntries = [...this.agents.entries()]
|
|
4834
|
+
const agentEntries = [...this.agents.entries()].filter(
|
|
4835
|
+
([id]) => !this.isAgentDisabled(id)
|
|
4836
|
+
);
|
|
3767
4837
|
const apyResults = await Promise.all(
|
|
3768
4838
|
agentEntries.map(async ([id, agent]) => {
|
|
3769
4839
|
const apy = await agent.getAgentApy(days, agentOptions);
|
|
@@ -3836,13 +4906,13 @@ var OwneySDK = class {
|
|
|
3836
4906
|
};
|
|
3837
4907
|
|
|
3838
4908
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
3839
|
-
import { getAddress } from "viem";
|
|
3840
|
-
import { SiweMessage } from "siwe";
|
|
4909
|
+
import { getAddress as getAddress3 } from "viem";
|
|
4910
|
+
import { SiweMessage as SiweMessage2 } from "siwe";
|
|
3841
4911
|
import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
|
|
3842
4912
|
|
|
3843
4913
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
3844
|
-
var
|
|
3845
|
-
var
|
|
4914
|
+
var KEY_PREFIX3 = "owney.siwx.session";
|
|
4915
|
+
var storage3 = () => {
|
|
3846
4916
|
if (typeof window === "undefined") return null;
|
|
3847
4917
|
try {
|
|
3848
4918
|
return window.localStorage;
|
|
@@ -3850,8 +4920,8 @@ var storage2 = () => {
|
|
|
3850
4920
|
return null;
|
|
3851
4921
|
}
|
|
3852
4922
|
};
|
|
3853
|
-
var
|
|
3854
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
4923
|
+
var buildKey3 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
|
|
4924
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
|
|
3855
4925
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
3856
4926
|
var readLegacySiwxSession = (store, address) => {
|
|
3857
4927
|
if (!store) return null;
|
|
@@ -3882,8 +4952,8 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
3882
4952
|
};
|
|
3883
4953
|
var readSiwxSession = (address, chainId) => {
|
|
3884
4954
|
if (typeof window === "undefined") return null;
|
|
3885
|
-
const key2 =
|
|
3886
|
-
const store =
|
|
4955
|
+
const key2 = buildKey3(address);
|
|
4956
|
+
const store = storage3();
|
|
3887
4957
|
let raw = null;
|
|
3888
4958
|
try {
|
|
3889
4959
|
raw = store?.getItem(key2) ?? null;
|
|
@@ -3911,18 +4981,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
3911
4981
|
};
|
|
3912
4982
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
3913
4983
|
if (typeof window === "undefined") return;
|
|
3914
|
-
const key2 =
|
|
4984
|
+
const key2 = buildKey3(address);
|
|
3915
4985
|
memorySiwxSessions.set(key2, session);
|
|
3916
|
-
const store =
|
|
4986
|
+
const store = storage3();
|
|
3917
4987
|
try {
|
|
3918
4988
|
store?.setItem(key2, JSON.stringify(session));
|
|
3919
4989
|
} catch {
|
|
3920
4990
|
}
|
|
3921
4991
|
};
|
|
3922
4992
|
var clearSiwxSession = (address, _chainId) => {
|
|
3923
|
-
const key2 =
|
|
4993
|
+
const key2 = buildKey3(address);
|
|
3924
4994
|
memorySiwxSessions.delete(key2);
|
|
3925
|
-
const store =
|
|
4995
|
+
const store = storage3();
|
|
3926
4996
|
try {
|
|
3927
4997
|
store?.removeItem(key2);
|
|
3928
4998
|
} catch {
|
|
@@ -3962,8 +5032,8 @@ function buildSIWXConfig(deps) {
|
|
|
3962
5032
|
statement: STATEMENT,
|
|
3963
5033
|
issuedAt,
|
|
3964
5034
|
toString() {
|
|
3965
|
-
return new
|
|
3966
|
-
address:
|
|
5035
|
+
return new SiweMessage2({
|
|
5036
|
+
address: getAddress3(accountAddress),
|
|
3967
5037
|
chainId: numericChainId(chainId),
|
|
3968
5038
|
domain,
|
|
3969
5039
|
uri,
|
|
@@ -4005,7 +5075,7 @@ function buildSIWXConfig(deps) {
|
|
|
4005
5075
|
const persistSession = async (session) => {
|
|
4006
5076
|
const address = session.data.accountAddress;
|
|
4007
5077
|
const id = numericChainId(session.data.chainId);
|
|
4008
|
-
const message = new
|
|
5078
|
+
const message = new SiweMessage2(session.message);
|
|
4009
5079
|
const login = await post("/auth/login", {
|
|
4010
5080
|
message,
|
|
4011
5081
|
signature: session.signature,
|
|
@@ -4053,6 +5123,7 @@ export {
|
|
|
4053
5123
|
NotConnectedError,
|
|
4054
5124
|
OwneyError,
|
|
4055
5125
|
OwneySDK,
|
|
5126
|
+
YieldseekerAgent,
|
|
4056
5127
|
createOwneySIWX,
|
|
4057
5128
|
setOwneyDebug
|
|
4058
5129
|
};
|