@owney/sdk 0.7.16-beta.1 → 0.7.16
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 +0 -2
- package/dist/index.cjs +666 -1708
- package/dist/index.d.cts +5 -115
- package/dist/index.d.ts +5 -115
- package/dist/index.js +685 -1738
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -26,7 +26,6 @@ __export(index_exports, {
|
|
|
26
26
|
NotConnectedError: () => NotConnectedError,
|
|
27
27
|
OwneyError: () => OwneyError,
|
|
28
28
|
OwneySDK: () => OwneySDK,
|
|
29
|
-
YieldseekerAgent: () => YieldseekerAgent,
|
|
30
29
|
createOwneySIWX: () => createOwneySIWX,
|
|
31
30
|
setOwneyDebug: () => setOwneyDebug
|
|
32
31
|
});
|
|
@@ -84,6 +83,23 @@ var SupportedAssets = [
|
|
|
84
83
|
}
|
|
85
84
|
];
|
|
86
85
|
|
|
86
|
+
// src/lib/debug.ts
|
|
87
|
+
var configuredDebug = false;
|
|
88
|
+
function setOwneyDebug(enabled) {
|
|
89
|
+
configuredDebug = enabled;
|
|
90
|
+
}
|
|
91
|
+
function isOwneyDebug() {
|
|
92
|
+
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
93
|
+
}
|
|
94
|
+
function debugLog(scope, message, data) {
|
|
95
|
+
if (!isOwneyDebug()) return;
|
|
96
|
+
if (data === void 0) {
|
|
97
|
+
console.log(`[${scope}] ${message}`);
|
|
98
|
+
} else {
|
|
99
|
+
console.log(`[${scope}] ${message}`, data);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
87
103
|
// src/errors.ts
|
|
88
104
|
var OwneyError = class extends Error {
|
|
89
105
|
code;
|
|
@@ -283,7 +299,7 @@ function mapBalances(raw, _chainId, smartWallet) {
|
|
|
283
299
|
)
|
|
284
300
|
)
|
|
285
301
|
),
|
|
286
|
-
apy: p.pool_apy,
|
|
302
|
+
apy: netApy(p.pool_apy_withFee, p.pool_apy, "pool_apy_withFee"),
|
|
287
303
|
tvl: p.pool_tvl,
|
|
288
304
|
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
289
305
|
// structural read keeps it undefined today and auto-populates later.
|
|
@@ -340,14 +356,27 @@ function mapWeightedApyByChain(raw) {
|
|
|
340
356
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
341
357
|
const perAsset = {};
|
|
342
358
|
for (const [symbol, apy] of Object.entries(tokenApy)) {
|
|
343
|
-
const
|
|
344
|
-
if (!SUPPORTED_TOKENS.includes(
|
|
345
|
-
perAsset[
|
|
359
|
+
const token = symbol;
|
|
360
|
+
if (!SUPPORTED_TOKENS.includes(token)) continue;
|
|
361
|
+
perAsset[token] = apy;
|
|
346
362
|
}
|
|
347
363
|
if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
|
|
348
364
|
}
|
|
349
365
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
350
366
|
}
|
|
367
|
+
var warnedGrossApyFallbacks = /* @__PURE__ */ new Set();
|
|
368
|
+
function netApy(net, gross, field) {
|
|
369
|
+
if (net !== void 0 && net !== null) return net;
|
|
370
|
+
if (gross === void 0 || gross === null) return void 0;
|
|
371
|
+
if (!warnedGrossApyFallbacks.has(field)) {
|
|
372
|
+
warnedGrossApyFallbacks.add(field);
|
|
373
|
+
console.warn(
|
|
374
|
+
`[owney] @zyfai/sdk omitted "${field}"; falling back to the gross APY, which does not deduct Zyfai's performance fee and so reads high.`
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
debugLog("zyfai:apy", `gross fallback for ${field}`, { gross });
|
|
378
|
+
return gross;
|
|
379
|
+
}
|
|
351
380
|
function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
352
381
|
let weightedSum = 0;
|
|
353
382
|
let totalBalance = 0;
|
|
@@ -355,8 +384,9 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
355
384
|
if (p.chainId !== chainId) continue;
|
|
356
385
|
if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
|
|
357
386
|
const balance = p.balance ?? 0;
|
|
358
|
-
|
|
359
|
-
|
|
387
|
+
const apy = netApy(p.apy_withFee, p.apy, "apy_withFee");
|
|
388
|
+
if (balance <= 0 || typeof apy !== "number") continue;
|
|
389
|
+
weightedSum += apy * balance;
|
|
360
390
|
totalBalance += balance;
|
|
361
391
|
}
|
|
362
392
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
@@ -404,8 +434,12 @@ function mapEntries(rawEntries, chainId) {
|
|
|
404
434
|
const matched = positions.find(
|
|
405
435
|
(p) => p.protocol_name ? rawLog.newOpportunity.includes(`${p.protocol_name} (${p.pool})`) : false
|
|
406
436
|
);
|
|
407
|
-
oldApy = Number(
|
|
408
|
-
|
|
437
|
+
oldApy = Number(
|
|
438
|
+
netApy(rawLog.oldApy_withFee, rawLog.oldApy, "oldApy_withFee")
|
|
439
|
+
);
|
|
440
|
+
newApy = Number(
|
|
441
|
+
netApy(rawLog.newApy_withFee, rawLog.newApy, "newApy_withFee")
|
|
442
|
+
);
|
|
409
443
|
const from = splitOpportunity(rawLog.oldOpportunity);
|
|
410
444
|
const to = splitOpportunity(rawLog.newOpportunity);
|
|
411
445
|
rebalanceLog = [
|
|
@@ -485,7 +519,11 @@ function mapApyByStrategy(raw) {
|
|
|
485
519
|
const chainKey = supported.chainId;
|
|
486
520
|
const symbolKey = supported.symbol;
|
|
487
521
|
const bucket = apyPerAsset[chainKey] ?? {};
|
|
488
|
-
const apy =
|
|
522
|
+
const apy = netApy(
|
|
523
|
+
entry.average_apy_withFee,
|
|
524
|
+
entry.average_apy,
|
|
525
|
+
"average_apy_withFee"
|
|
526
|
+
) ?? entry.average_apy;
|
|
489
527
|
bucket[symbolKey] = apy;
|
|
490
528
|
apyPerAsset[chainKey] = bucket;
|
|
491
529
|
apySum += apy;
|
|
@@ -511,23 +549,23 @@ function computeAllocationApy(positions) {
|
|
|
511
549
|
weightedSum += apy * value;
|
|
512
550
|
totalValue += value;
|
|
513
551
|
const chainId = resolveChainId(p.chain);
|
|
514
|
-
const
|
|
552
|
+
const token = p.asset;
|
|
515
553
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
516
|
-
if (!SUPPORTED_TOKENS.includes(
|
|
554
|
+
if (!SUPPORTED_TOKENS.includes(token)) continue;
|
|
517
555
|
const perAsset = buckets[chainId] ?? {};
|
|
518
|
-
const cell = perAsset[
|
|
556
|
+
const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
|
|
519
557
|
cell.weightedSum += apy * value;
|
|
520
558
|
cell.value += value;
|
|
521
|
-
perAsset[
|
|
559
|
+
perAsset[token] = cell;
|
|
522
560
|
buckets[chainId] = perAsset;
|
|
523
561
|
}
|
|
524
562
|
const apyByChainAndAsset = {};
|
|
525
563
|
for (const [chainKey, perAsset] of Object.entries(buckets)) {
|
|
526
564
|
const chainId = Number(chainKey);
|
|
527
565
|
const out = {};
|
|
528
|
-
for (const [
|
|
566
|
+
for (const [token, cell] of Object.entries(perAsset ?? {})) {
|
|
529
567
|
if (cell && cell.value > 0) {
|
|
530
|
-
out[
|
|
568
|
+
out[token] = cell.weightedSum / cell.value;
|
|
531
569
|
}
|
|
532
570
|
}
|
|
533
571
|
if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
|
|
@@ -541,9 +579,9 @@ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4d
|
|
|
541
579
|
function topicToAddress(topic) {
|
|
542
580
|
return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
|
|
543
581
|
}
|
|
544
|
-
function extractWithdrawnAmount(logs, recipient,
|
|
582
|
+
function extractWithdrawnAmount(logs, recipient, token, decimals) {
|
|
545
583
|
const wantRecipient = recipient.toLowerCase();
|
|
546
|
-
const wantToken =
|
|
584
|
+
const wantToken = token.toLowerCase();
|
|
547
585
|
let total = 0n;
|
|
548
586
|
let matched = false;
|
|
549
587
|
for (const log of logs) {
|
|
@@ -569,11 +607,11 @@ var InvalidHistoryCursorError = class extends Error {
|
|
|
569
607
|
function encodeHistoryCursor(payload) {
|
|
570
608
|
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
|
|
571
609
|
}
|
|
572
|
-
function decodeHistoryCursor(
|
|
573
|
-
if (!
|
|
610
|
+
function decodeHistoryCursor(token) {
|
|
611
|
+
if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
|
|
574
612
|
let parsed;
|
|
575
613
|
try {
|
|
576
|
-
const json = Buffer.from(
|
|
614
|
+
const json = Buffer.from(token, "base64").toString("utf8");
|
|
577
615
|
parsed = JSON.parse(json);
|
|
578
616
|
} catch {
|
|
579
617
|
throw new InvalidHistoryCursorError("Cursor is not valid base64 JSON.");
|
|
@@ -596,8 +634,8 @@ var storage = () => {
|
|
|
596
634
|
};
|
|
597
635
|
var buildKey = (address) => `${KEY_PREFIX}:${address.toLowerCase()}`;
|
|
598
636
|
var legacyKeyPrefix = (address) => `${KEY_PREFIX}:${address.toLowerCase()}:`;
|
|
599
|
-
var isJwtExpired = (
|
|
600
|
-
const parts =
|
|
637
|
+
var isJwtExpired = (token) => {
|
|
638
|
+
const parts = token.split(".");
|
|
601
639
|
if (parts.length !== 3) return false;
|
|
602
640
|
try {
|
|
603
641
|
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -845,23 +883,6 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
845
883
|
return !protocolListsEqual(current.protocols, desiredProtocols);
|
|
846
884
|
}
|
|
847
885
|
|
|
848
|
-
// src/lib/debug.ts
|
|
849
|
-
var configuredDebug = false;
|
|
850
|
-
function setOwneyDebug(enabled) {
|
|
851
|
-
configuredDebug = enabled;
|
|
852
|
-
}
|
|
853
|
-
function isOwneyDebug() {
|
|
854
|
-
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
855
|
-
}
|
|
856
|
-
function debugLog(scope, message, data) {
|
|
857
|
-
if (!isOwneyDebug()) return;
|
|
858
|
-
if (data === void 0) {
|
|
859
|
-
console.log(`[${scope}] ${message}`);
|
|
860
|
-
} else {
|
|
861
|
-
console.log(`[${scope}] ${message}`, data);
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
|
|
865
886
|
// src/agents/zyfai/zyfai.agent.ts
|
|
866
887
|
var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem.parseAbi)([
|
|
867
888
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
@@ -1699,20 +1720,20 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1699
1720
|
}
|
|
1700
1721
|
}
|
|
1701
1722
|
// --- IAgent: Fund operations ---
|
|
1702
|
-
async withdraw(state, chainId,
|
|
1723
|
+
async withdraw(state, chainId, token, amount) {
|
|
1703
1724
|
const validChainId = isValidChainId(chainId);
|
|
1704
1725
|
await this.ensureConnected(state, validChainId);
|
|
1705
1726
|
const raw = await this.sdk.withdrawFunds(
|
|
1706
1727
|
this.getAddress(),
|
|
1707
1728
|
validChainId,
|
|
1708
1729
|
amount,
|
|
1709
|
-
|
|
1730
|
+
token
|
|
1710
1731
|
);
|
|
1711
1732
|
if (!raw.success) {
|
|
1712
1733
|
throw new OwneyError(
|
|
1713
1734
|
"WITHDRAW_FAILED",
|
|
1714
1735
|
raw.message || "Zyfai withdraw failed.",
|
|
1715
|
-
{ chainId: validChainId, token
|
|
1736
|
+
{ chainId: validChainId, token, amount, response: raw },
|
|
1716
1737
|
this.id
|
|
1717
1738
|
);
|
|
1718
1739
|
}
|
|
@@ -1863,183 +1884,359 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1863
1884
|
}
|
|
1864
1885
|
};
|
|
1865
1886
|
|
|
1866
|
-
// src/
|
|
1867
|
-
var
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
// src/lib/chain-guard.ts
|
|
1871
|
-
var CHAIN_NAMES = {
|
|
1872
|
-
1: "Ethereum",
|
|
1873
|
-
8453: "Base",
|
|
1874
|
-
42161: "Arbitrum"
|
|
1875
|
-
};
|
|
1876
|
-
function chainName(chainId) {
|
|
1877
|
-
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
1878
|
-
}
|
|
1879
|
-
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
1880
|
-
const actual = await pub.getChainId();
|
|
1881
|
-
if (actual === expected) return;
|
|
1887
|
+
// src/lib/routing-api.ts
|
|
1888
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1889
|
+
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL) {
|
|
1890
|
+
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
1882
1891
|
try {
|
|
1883
|
-
await
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
+
const res = await fetch(url, {
|
|
1893
|
+
method: "GET",
|
|
1894
|
+
headers: {
|
|
1895
|
+
"Content-Type": "application/json",
|
|
1896
|
+
"x-owney-api-key": `${apiKey}`
|
|
1897
|
+
}
|
|
1898
|
+
});
|
|
1899
|
+
if (!res.ok) {
|
|
1900
|
+
if (res.status !== 404) {
|
|
1901
|
+
console.warn(
|
|
1902
|
+
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
1903
|
+
);
|
|
1892
1904
|
}
|
|
1905
|
+
return null;
|
|
1906
|
+
}
|
|
1907
|
+
const json = await res.json();
|
|
1908
|
+
const policy = json.success ? json.data ?? null : null;
|
|
1909
|
+
debugLog(
|
|
1910
|
+
"owney-sdk",
|
|
1911
|
+
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
1912
|
+
policy ?? void 0
|
|
1893
1913
|
);
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
1900
|
-
{ expectedChainId: expected, actualChainId: after }
|
|
1914
|
+
return policy;
|
|
1915
|
+
} catch (error) {
|
|
1916
|
+
console.warn(
|
|
1917
|
+
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
1918
|
+
error instanceof Error ? error.message : String(error)
|
|
1901
1919
|
);
|
|
1920
|
+
return null;
|
|
1902
1921
|
}
|
|
1903
1922
|
}
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
TransferWithAuthorization: [
|
|
1916
|
-
{ name: "from", type: "address" },
|
|
1917
|
-
{ name: "to", type: "address" },
|
|
1918
|
-
{ name: "value", type: "uint256" },
|
|
1919
|
-
{ name: "validAfter", type: "uint256" },
|
|
1920
|
-
{ name: "validBefore", type: "uint256" },
|
|
1921
|
-
{ name: "nonce", type: "bytes32" }
|
|
1922
|
-
]
|
|
1923
|
-
},
|
|
1924
|
-
primaryType: "TransferWithAuthorization",
|
|
1925
|
-
message: input.message
|
|
1926
|
-
};
|
|
1927
|
-
}
|
|
1928
|
-
async function readTokenMeta(publicClient, token2) {
|
|
1929
|
-
const [tokenName, tokenVersion] = await Promise.all([
|
|
1930
|
-
publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "name" }),
|
|
1931
|
-
publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
1932
|
-
]);
|
|
1933
|
-
return { tokenName, tokenVersion };
|
|
1934
|
-
}
|
|
1935
|
-
function randomAuthNonce() {
|
|
1936
|
-
const bytes = new Uint8Array(32);
|
|
1937
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
1938
|
-
return (0, import_viem2.bytesToHex)(bytes);
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
// src/lib/sponsor-client.ts
|
|
1942
|
-
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1943
|
-
async function postSponsorTransferAuth(input) {
|
|
1944
|
-
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
1945
|
-
let res;
|
|
1946
|
-
try {
|
|
1947
|
-
res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
1948
|
-
method: "POST",
|
|
1949
|
-
headers: {
|
|
1950
|
-
"content-type": "application/json",
|
|
1951
|
-
"x-owney-api-key": input.apiKey,
|
|
1952
|
-
...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
|
|
1953
|
-
},
|
|
1954
|
-
body: JSON.stringify(input.body)
|
|
1955
|
-
});
|
|
1956
|
-
} catch (networkError) {
|
|
1923
|
+
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
|
|
1924
|
+
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
1925
|
+
const res = await fetch(url, {
|
|
1926
|
+
method: "GET",
|
|
1927
|
+
headers: {
|
|
1928
|
+
"Content-Type": "application/json",
|
|
1929
|
+
"x-owney-api-key": `${apiKey}`
|
|
1930
|
+
}
|
|
1931
|
+
});
|
|
1932
|
+
if (!res.ok) {
|
|
1933
|
+
const text = await res.text().catch(() => "");
|
|
1957
1934
|
throw new OwneyError(
|
|
1958
|
-
"
|
|
1959
|
-
`
|
|
1960
|
-
{
|
|
1935
|
+
"API_ROUTING_ERROR",
|
|
1936
|
+
`Routing API error ${res.status}: ${text}`,
|
|
1937
|
+
{ statusCode: res.status, responseBody: text }
|
|
1961
1938
|
);
|
|
1962
1939
|
}
|
|
1963
|
-
const
|
|
1964
|
-
|
|
1965
|
-
try {
|
|
1966
|
-
parsed = JSON.parse(text);
|
|
1967
|
-
} catch {
|
|
1968
|
-
}
|
|
1969
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
1940
|
+
const json = await res.json();
|
|
1941
|
+
if (!json.success) {
|
|
1970
1942
|
throw new OwneyError(
|
|
1971
|
-
"
|
|
1972
|
-
`
|
|
1973
|
-
{
|
|
1974
|
-
statusCode: res.status,
|
|
1975
|
-
responseBody: text.slice(0, 500),
|
|
1976
|
-
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
1977
|
-
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
1978
|
-
safeToFallback: res.status === 503
|
|
1979
|
-
}
|
|
1943
|
+
"API_ROUTING_FAILED",
|
|
1944
|
+
`Routing API request failed: ${json.message}`,
|
|
1945
|
+
{ message: json.message }
|
|
1980
1946
|
);
|
|
1981
1947
|
}
|
|
1982
|
-
return
|
|
1948
|
+
return json.data;
|
|
1983
1949
|
}
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1950
|
+
|
|
1951
|
+
// src/lib/health-report.ts
|
|
1952
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1953
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL2) {
|
|
1987
1954
|
try {
|
|
1988
|
-
|
|
1955
|
+
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
1989
1956
|
method: "POST",
|
|
1990
1957
|
headers: {
|
|
1991
|
-
"
|
|
1992
|
-
"x-owney-api-key":
|
|
1993
|
-
...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
|
|
1958
|
+
"Content-Type": "application/json",
|
|
1959
|
+
"x-owney-api-key": apiKey
|
|
1994
1960
|
},
|
|
1995
|
-
body: JSON.stringify(
|
|
1961
|
+
body: JSON.stringify({
|
|
1962
|
+
agent_type: agentType,
|
|
1963
|
+
error_code: errorCode,
|
|
1964
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1965
|
+
})
|
|
1996
1966
|
});
|
|
1997
|
-
} catch (
|
|
1998
|
-
|
|
1999
|
-
"
|
|
2000
|
-
|
|
2001
|
-
{ cause: String(networkError), safeToFallback: false }
|
|
1967
|
+
} catch (err) {
|
|
1968
|
+
console.warn(
|
|
1969
|
+
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
1970
|
+
err instanceof Error ? err.message : err
|
|
2002
1971
|
);
|
|
2003
1972
|
}
|
|
2004
|
-
|
|
2005
|
-
|
|
1973
|
+
}
|
|
1974
|
+
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
2006
1975
|
try {
|
|
2007
|
-
|
|
2008
|
-
} catch {
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
throw
|
|
2012
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2013
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2014
|
-
{
|
|
2015
|
-
statusCode: res.status,
|
|
2016
|
-
responseBody: text.slice(0, 500),
|
|
2017
|
-
safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
|
|
2018
|
-
}
|
|
2019
|
-
);
|
|
1976
|
+
return await fn();
|
|
1977
|
+
} catch (err) {
|
|
1978
|
+
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
1979
|
+
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
1980
|
+
throw err;
|
|
2020
1981
|
}
|
|
2021
|
-
return parsed.data;
|
|
2022
1982
|
}
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
);
|
|
2033
|
-
} catch (networkError) {
|
|
2034
|
-
throw new OwneyError(
|
|
2035
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2036
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2037
|
-
{ cause: String(networkError), safeToFallback: true }
|
|
1983
|
+
|
|
1984
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
1985
|
+
var import_viem2 = require("viem");
|
|
1986
|
+
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
1987
|
+
const target = asset.toUpperCase();
|
|
1988
|
+
return agents.map((agent) => {
|
|
1989
|
+
const agentBalance = aggregated[agent.id];
|
|
1990
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
1991
|
+
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
2038
1992
|
);
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
1993
|
+
if (!tokenBalance) return { agent, balance: 0n };
|
|
1994
|
+
return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
1998
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
1999
|
+
agent,
|
|
2000
|
+
balance,
|
|
2001
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
2002
|
+
}));
|
|
2003
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
2004
|
+
let remainder = requested - assigned;
|
|
2005
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
2006
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
2007
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2008
|
+
});
|
|
2009
|
+
for (const p of byHeadroom) {
|
|
2010
|
+
if (remainder === 0n) break;
|
|
2011
|
+
const headroom = p.balance - p.planned;
|
|
2012
|
+
if (headroom <= 0n) continue;
|
|
2013
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
2014
|
+
p.planned += take;
|
|
2015
|
+
remainder -= take;
|
|
2016
|
+
}
|
|
2017
|
+
return plans;
|
|
2018
|
+
}
|
|
2019
|
+
function planDisabledDrain(disabled, requested) {
|
|
2020
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
2021
|
+
const plans = [];
|
|
2022
|
+
let remaining = requested;
|
|
2023
|
+
for (const { agent, balance } of sorted) {
|
|
2024
|
+
if (remaining === 0n) {
|
|
2025
|
+
plans.push({ agent, balance, planned: 0n });
|
|
2026
|
+
continue;
|
|
2027
|
+
}
|
|
2028
|
+
const take = balance < remaining ? balance : remaining;
|
|
2029
|
+
plans.push({ agent, balance, planned: take });
|
|
2030
|
+
remaining -= take;
|
|
2031
|
+
}
|
|
2032
|
+
return { plans, remaining };
|
|
2033
|
+
}
|
|
2034
|
+
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
2035
|
+
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
2036
|
+
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
2037
|
+
const totalHeadroom = candidates.reduce(
|
|
2038
|
+
(s, c) => s + (c.balance - c.planned),
|
|
2039
|
+
0n
|
|
2040
|
+
);
|
|
2041
|
+
if (totalHeadroom === 0n) return;
|
|
2042
|
+
let distributed = 0n;
|
|
2043
|
+
for (const c of candidates) {
|
|
2044
|
+
const headroom = c.balance - c.planned;
|
|
2045
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
2046
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
2047
|
+
c.planned += give;
|
|
2048
|
+
distributed += give;
|
|
2049
|
+
}
|
|
2050
|
+
let leftover = amount - distributed;
|
|
2051
|
+
for (const c of candidates) {
|
|
2052
|
+
if (leftover === 0n) break;
|
|
2053
|
+
const headroom = c.balance - c.planned;
|
|
2054
|
+
if (headroom <= 0n) continue;
|
|
2055
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
2056
|
+
c.planned += take;
|
|
2057
|
+
leftover -= take;
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
function sumWithdrawnAmount(results) {
|
|
2061
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
// src/lib/helpers/account-apy-helper.ts
|
|
2065
|
+
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
2066
|
+
const sums = {};
|
|
2067
|
+
const weights = {};
|
|
2068
|
+
for (const id of Object.keys(agentApys)) {
|
|
2069
|
+
const cells = agentApys[id].apyByChainAndAsset;
|
|
2070
|
+
const balance = agentBalances[id] ?? 0;
|
|
2071
|
+
if (!cells || balance <= 0) continue;
|
|
2072
|
+
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
2073
|
+
if (!perAsset) continue;
|
|
2074
|
+
const chainId = Number(chainKey);
|
|
2075
|
+
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
2076
|
+
const apy = Number(apyValue ?? 0);
|
|
2077
|
+
if (apy === 0) continue;
|
|
2078
|
+
sums[chainId] ??= {};
|
|
2079
|
+
weights[chainId] ??= {};
|
|
2080
|
+
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
2081
|
+
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
const out = {};
|
|
2086
|
+
for (const chainKey of Object.keys(sums)) {
|
|
2087
|
+
const chainId = Number(chainKey);
|
|
2088
|
+
const perAssetOut = {};
|
|
2089
|
+
for (const asset of Object.keys(sums[chainId])) {
|
|
2090
|
+
const w = weights[chainId][asset];
|
|
2091
|
+
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
2092
|
+
}
|
|
2093
|
+
if (Object.keys(perAssetOut).length > 0) {
|
|
2094
|
+
out[chainId] = perAssetOut;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
return out;
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
// src/client.ts
|
|
2101
|
+
var import_viem6 = require("viem");
|
|
2102
|
+
var import_chains2 = require("viem/chains");
|
|
2103
|
+
|
|
2104
|
+
// src/lib/transfer-auth.ts
|
|
2105
|
+
var import_viem3 = require("viem");
|
|
2106
|
+
var ERC20_META_ABI = [
|
|
2107
|
+
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
2108
|
+
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
2109
|
+
];
|
|
2110
|
+
function buildTransferWithAuthorizationTypedData(input) {
|
|
2111
|
+
return {
|
|
2112
|
+
domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
|
|
2113
|
+
types: {
|
|
2114
|
+
TransferWithAuthorization: [
|
|
2115
|
+
{ name: "from", type: "address" },
|
|
2116
|
+
{ name: "to", type: "address" },
|
|
2117
|
+
{ name: "value", type: "uint256" },
|
|
2118
|
+
{ name: "validAfter", type: "uint256" },
|
|
2119
|
+
{ name: "validBefore", type: "uint256" },
|
|
2120
|
+
{ name: "nonce", type: "bytes32" }
|
|
2121
|
+
]
|
|
2122
|
+
},
|
|
2123
|
+
primaryType: "TransferWithAuthorization",
|
|
2124
|
+
message: input.message
|
|
2125
|
+
};
|
|
2126
|
+
}
|
|
2127
|
+
async function readTokenMeta(publicClient, token) {
|
|
2128
|
+
const [tokenName, tokenVersion] = await Promise.all([
|
|
2129
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
2130
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
2131
|
+
]);
|
|
2132
|
+
return { tokenName, tokenVersion };
|
|
2133
|
+
}
|
|
2134
|
+
function randomAuthNonce() {
|
|
2135
|
+
const bytes = new Uint8Array(32);
|
|
2136
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
2137
|
+
return (0, import_viem3.bytesToHex)(bytes);
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
// src/lib/sponsor-client.ts
|
|
2141
|
+
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2142
|
+
async function postSponsorTransferAuth(input) {
|
|
2143
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2144
|
+
let res;
|
|
2145
|
+
try {
|
|
2146
|
+
res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2147
|
+
method: "POST",
|
|
2148
|
+
headers: {
|
|
2149
|
+
"content-type": "application/json",
|
|
2150
|
+
"x-owney-api-key": input.apiKey
|
|
2151
|
+
},
|
|
2152
|
+
body: JSON.stringify(input.body)
|
|
2153
|
+
});
|
|
2154
|
+
} catch (networkError) {
|
|
2155
|
+
throw new OwneyError(
|
|
2156
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2157
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2158
|
+
{ cause: String(networkError) }
|
|
2159
|
+
);
|
|
2160
|
+
}
|
|
2161
|
+
const text = await res.text();
|
|
2162
|
+
let parsed = null;
|
|
2163
|
+
try {
|
|
2164
|
+
parsed = JSON.parse(text);
|
|
2165
|
+
} catch {
|
|
2166
|
+
}
|
|
2167
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2168
|
+
throw new OwneyError(
|
|
2169
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2170
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2171
|
+
{
|
|
2172
|
+
statusCode: res.status,
|
|
2173
|
+
responseBody: text.slice(0, 500),
|
|
2174
|
+
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
2175
|
+
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2176
|
+
safeToFallback: res.status === 503
|
|
2177
|
+
}
|
|
2178
|
+
);
|
|
2179
|
+
}
|
|
2180
|
+
return parsed.data;
|
|
2181
|
+
}
|
|
2182
|
+
async function postSponsorPermit2Transfer(input) {
|
|
2183
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2184
|
+
let res;
|
|
2185
|
+
try {
|
|
2186
|
+
res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
|
|
2187
|
+
method: "POST",
|
|
2188
|
+
headers: {
|
|
2189
|
+
"content-type": "application/json",
|
|
2190
|
+
"x-owney-api-key": input.apiKey
|
|
2191
|
+
},
|
|
2192
|
+
body: JSON.stringify(input.body)
|
|
2193
|
+
});
|
|
2194
|
+
} catch (networkError) {
|
|
2195
|
+
throw new OwneyError(
|
|
2196
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2197
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2198
|
+
{ cause: String(networkError), safeToFallback: false }
|
|
2199
|
+
);
|
|
2200
|
+
}
|
|
2201
|
+
const text = await res.text();
|
|
2202
|
+
let parsed = null;
|
|
2203
|
+
try {
|
|
2204
|
+
parsed = JSON.parse(text);
|
|
2205
|
+
} catch {
|
|
2206
|
+
}
|
|
2207
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2208
|
+
throw new OwneyError(
|
|
2209
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2210
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2211
|
+
{
|
|
2212
|
+
statusCode: res.status,
|
|
2213
|
+
responseBody: text.slice(0, 500),
|
|
2214
|
+
safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
|
|
2215
|
+
}
|
|
2216
|
+
);
|
|
2217
|
+
}
|
|
2218
|
+
return parsed.data;
|
|
2219
|
+
}
|
|
2220
|
+
async function getSponsorRelayerAddress(input) {
|
|
2221
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2222
|
+
let res;
|
|
2223
|
+
try {
|
|
2224
|
+
res = await fetch(
|
|
2225
|
+
`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2226
|
+
{
|
|
2227
|
+
headers: { "x-owney-api-key": input.apiKey }
|
|
2228
|
+
}
|
|
2229
|
+
);
|
|
2230
|
+
} catch (networkError) {
|
|
2231
|
+
throw new OwneyError(
|
|
2232
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2233
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2234
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
2235
|
+
);
|
|
2236
|
+
}
|
|
2237
|
+
const text = await res.text();
|
|
2238
|
+
let parsed = null;
|
|
2239
|
+
try {
|
|
2043
2240
|
parsed = JSON.parse(text);
|
|
2044
2241
|
} catch {
|
|
2045
2242
|
}
|
|
@@ -2058,7 +2255,7 @@ async function getSponsorRelayerAddress(input) {
|
|
|
2058
2255
|
}
|
|
2059
2256
|
|
|
2060
2257
|
// src/lib/permit2.ts
|
|
2061
|
-
var
|
|
2258
|
+
var import_viem4 = require("viem");
|
|
2062
2259
|
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2063
2260
|
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2064
2261
|
var ERC20_ALLOWANCE_ABI = [
|
|
@@ -2109,1398 +2306,230 @@ function buildPermitTransferFromTypedData(input) {
|
|
|
2109
2306
|
{ name: "amount", type: "uint256" }
|
|
2110
2307
|
]
|
|
2111
2308
|
},
|
|
2112
|
-
primaryType: "PermitTransferFrom",
|
|
2113
|
-
message: input.message
|
|
2114
|
-
};
|
|
2115
|
-
}
|
|
2116
|
-
function randomPermit2Nonce() {
|
|
2117
|
-
const bytes = new Uint8Array(32);
|
|
2118
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2119
|
-
return BigInt((0,
|
|
2120
|
-
}
|
|
2121
|
-
async function readPermit2Allowance(publicClient,
|
|
2122
|
-
return publicClient.readContract({
|
|
2123
|
-
address:
|
|
2124
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2125
|
-
functionName: "allowance",
|
|
2126
|
-
args: [owner, PERMIT2_ADDRESS]
|
|
2127
|
-
});
|
|
2128
|
-
}
|
|
2129
|
-
async function readErc20Balance(publicClient, token2, owner) {
|
|
2130
|
-
return publicClient.readContract({
|
|
2131
|
-
address: token2,
|
|
2132
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2133
|
-
functionName: "balanceOf",
|
|
2134
|
-
args: [owner]
|
|
2135
|
-
});
|
|
2136
|
-
}
|
|
2137
|
-
|
|
2138
|
-
// src/lib/sponsored-deposit.ts
|
|
2139
|
-
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2140
|
-
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2141
|
-
function provideDepositVerificationContext(callback, context) {
|
|
2142
|
-
callback[verificationSetter]?.(context);
|
|
2143
|
-
}
|
|
2144
|
-
function makeVerificationAwareDepositCallback(implementation) {
|
|
2145
|
-
let nextVerification;
|
|
2146
|
-
const callback = async (smartWallet, chainId, amount) => {
|
|
2147
|
-
const verification = nextVerification;
|
|
2148
|
-
nextVerification = void 0;
|
|
2149
|
-
return implementation(smartWallet, chainId, amount, verification);
|
|
2150
|
-
};
|
|
2151
|
-
Object.defineProperty(callback, verificationSetter, {
|
|
2152
|
-
value: (context) => {
|
|
2153
|
-
nextVerification = context;
|
|
2154
|
-
}
|
|
2155
|
-
});
|
|
2156
|
-
return callback;
|
|
2157
|
-
}
|
|
2158
|
-
function makeSponsoredDepositCallback(deps) {
|
|
2159
|
-
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2160
|
-
return makeVerificationAwareDepositCallback(
|
|
2161
|
-
async (smartWallet, chainId, amount, verification) => {
|
|
2162
|
-
const cid = chainId;
|
|
2163
|
-
const token2 = deps.tokenAddressByChain[cid];
|
|
2164
|
-
if (!token2) {
|
|
2165
|
-
throw new OwneyError(
|
|
2166
|
-
"CHAIN_UNSUPPORTED",
|
|
2167
|
-
`No sponsored token configured for chain ${chainId}`
|
|
2168
|
-
);
|
|
2169
|
-
}
|
|
2170
|
-
const pub = deps.getPublicClient(cid);
|
|
2171
|
-
const wallet = deps.getWalletClient(cid);
|
|
2172
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
2173
|
-
try {
|
|
2174
|
-
const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
|
|
2175
|
-
if (balance < BigInt(amount)) {
|
|
2176
|
-
throw new OwneyError(
|
|
2177
|
-
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2178
|
-
"Insufficient balance for this deposit.",
|
|
2179
|
-
{ token: token2, chainId: cid, balance: balance.toString(), amount }
|
|
2180
|
-
);
|
|
2181
|
-
}
|
|
2182
|
-
} catch (err) {
|
|
2183
|
-
if (err instanceof OwneyError) throw err;
|
|
2184
|
-
console.warn(
|
|
2185
|
-
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2186
|
-
err instanceof Error ? err.message : String(err)
|
|
2187
|
-
);
|
|
2188
|
-
}
|
|
2189
|
-
const { tokenName, tokenVersion } = await readTokenMeta(pub, token2);
|
|
2190
|
-
const validAfter = 0n;
|
|
2191
|
-
const validBefore = BigInt(
|
|
2192
|
-
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2193
|
-
);
|
|
2194
|
-
const nonce = randomAuthNonce();
|
|
2195
|
-
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2196
|
-
token: token2,
|
|
2197
|
-
chainId: cid,
|
|
2198
|
-
tokenName,
|
|
2199
|
-
tokenVersion,
|
|
2200
|
-
message: {
|
|
2201
|
-
from: deps.ownerAddress,
|
|
2202
|
-
to: smartWallet,
|
|
2203
|
-
value: BigInt(amount),
|
|
2204
|
-
validAfter,
|
|
2205
|
-
validBefore,
|
|
2206
|
-
nonce
|
|
2207
|
-
}
|
|
2208
|
-
});
|
|
2209
|
-
const authSignature = await wallet.signTypedData({
|
|
2210
|
-
account: deps.ownerAddress,
|
|
2211
|
-
...typedData
|
|
2212
|
-
});
|
|
2213
|
-
deps.onApproved?.();
|
|
2214
|
-
const result = await post({
|
|
2215
|
-
baseUrl: deps.baseUrl,
|
|
2216
|
-
apiKey: deps.apiKey,
|
|
2217
|
-
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2218
|
-
body: {
|
|
2219
|
-
chainId: cid,
|
|
2220
|
-
token: token2,
|
|
2221
|
-
from: deps.ownerAddress,
|
|
2222
|
-
to: smartWallet,
|
|
2223
|
-
value: amount,
|
|
2224
|
-
validAfter: validAfter.toString(),
|
|
2225
|
-
validBefore: validBefore.toString(),
|
|
2226
|
-
nonce,
|
|
2227
|
-
authSignature,
|
|
2228
|
-
tokenName,
|
|
2229
|
-
tokenVersion
|
|
2230
|
-
}
|
|
2231
|
-
});
|
|
2232
|
-
return result.txHash;
|
|
2233
|
-
}
|
|
2234
|
-
);
|
|
2235
|
-
}
|
|
2236
|
-
|
|
2237
|
-
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2238
|
-
var import_siwe = require("siwe");
|
|
2239
|
-
var import_viem4 = require("viem");
|
|
2240
|
-
var import_chains2 = require("viem/chains");
|
|
2241
|
-
|
|
2242
|
-
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2243
|
-
var KEY_PREFIX2 = "owney.yieldseeker.session";
|
|
2244
|
-
var storage2 = () => {
|
|
2245
|
-
if (typeof window === "undefined") return null;
|
|
2246
|
-
try {
|
|
2247
|
-
return window.localStorage;
|
|
2248
|
-
} catch {
|
|
2249
|
-
return null;
|
|
2250
|
-
}
|
|
2251
|
-
};
|
|
2252
|
-
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2253
|
-
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2254
|
-
var isValidSession = (session) => {
|
|
2255
|
-
if (!session?.token) return false;
|
|
2256
|
-
try {
|
|
2257
|
-
const parsed = JSON.parse(atob(session.token));
|
|
2258
|
-
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2259
|
-
} catch {
|
|
2260
|
-
return false;
|
|
2261
|
-
}
|
|
2262
|
-
};
|
|
2263
|
-
var readYieldseekerSession = (address, chainId) => {
|
|
2264
|
-
if (typeof window === "undefined") return null;
|
|
2265
|
-
const key2 = buildKey2(address, chainId);
|
|
2266
|
-
const store = storage2();
|
|
2267
|
-
let raw = null;
|
|
2268
|
-
try {
|
|
2269
|
-
raw = store?.getItem(key2) ?? null;
|
|
2270
|
-
} catch {
|
|
2271
|
-
raw = null;
|
|
2272
|
-
}
|
|
2273
|
-
if (raw) {
|
|
2274
|
-
try {
|
|
2275
|
-
const parsed = JSON.parse(raw);
|
|
2276
|
-
if (isValidSession(parsed)) return parsed.token;
|
|
2277
|
-
} catch {
|
|
2278
|
-
}
|
|
2279
|
-
memorySessions2.delete(key2);
|
|
2280
|
-
try {
|
|
2281
|
-
store?.removeItem(key2);
|
|
2282
|
-
} catch {
|
|
2283
|
-
}
|
|
2284
|
-
return null;
|
|
2285
|
-
}
|
|
2286
|
-
const cached = memorySessions2.get(key2);
|
|
2287
|
-
if (isValidSession(cached)) return cached.token;
|
|
2288
|
-
if (cached) memorySessions2.delete(key2);
|
|
2289
|
-
return null;
|
|
2290
|
-
};
|
|
2291
|
-
var writeYieldseekerSession = (address, chainId, token2) => {
|
|
2292
|
-
if (typeof window === "undefined") return;
|
|
2293
|
-
const session = { token: token2 };
|
|
2294
|
-
if (!isValidSession(session)) return;
|
|
2295
|
-
const key2 = buildKey2(address, chainId);
|
|
2296
|
-
memorySessions2.set(key2, session);
|
|
2297
|
-
const store = storage2();
|
|
2298
|
-
try {
|
|
2299
|
-
store?.setItem(key2, JSON.stringify(session));
|
|
2300
|
-
} catch {
|
|
2301
|
-
}
|
|
2302
|
-
};
|
|
2303
|
-
var clearYieldseekerSession = (address, chainId) => {
|
|
2304
|
-
const key2 = buildKey2(address, chainId);
|
|
2305
|
-
memorySessions2.delete(key2);
|
|
2306
|
-
const store = storage2();
|
|
2307
|
-
try {
|
|
2308
|
-
store?.removeItem(key2);
|
|
2309
|
-
} catch {
|
|
2310
|
-
}
|
|
2311
|
-
};
|
|
2312
|
-
|
|
2313
|
-
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2314
|
-
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2315
|
-
return new import_siwe.SiweMessage({
|
|
2316
|
-
domain: "yieldseeker.xyz",
|
|
2317
|
-
address: (0, import_viem4.getAddress)(address),
|
|
2318
|
-
uri: "https://yieldseeker.xyz",
|
|
2319
|
-
version: "1",
|
|
2320
|
-
chainId,
|
|
2321
|
-
nonce: (dependencies.nonce ?? import_siwe.generateNonce)(),
|
|
2322
|
-
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2323
|
-
}).prepareMessage();
|
|
2324
|
-
}
|
|
2325
|
-
function encodeYieldseekerAuthToken(token2) {
|
|
2326
|
-
const bytes = new TextEncoder().encode(JSON.stringify(token2));
|
|
2327
|
-
let binary = "";
|
|
2328
|
-
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2329
|
-
return btoa(binary);
|
|
2330
|
-
}
|
|
2331
|
-
var YieldseekerAuth = class {
|
|
2332
|
-
constructor(dependencies = {}) {
|
|
2333
|
-
this.dependencies = dependencies;
|
|
2334
|
-
}
|
|
2335
|
-
dependencies;
|
|
2336
|
-
tokens = /* @__PURE__ */ new Map();
|
|
2337
|
-
pending = /* @__PURE__ */ new Map();
|
|
2338
|
-
scopes = /* @__PURE__ */ new Map();
|
|
2339
|
-
key(state, chainId) {
|
|
2340
|
-
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
2341
|
-
}
|
|
2342
|
-
async getToken(state, chainId) {
|
|
2343
|
-
const key2 = this.key(state, chainId);
|
|
2344
|
-
const scope = { address: state.walletAddress, chainId };
|
|
2345
|
-
this.scopes.set(key2, scope);
|
|
2346
|
-
const cached = this.tokens.get(key2);
|
|
2347
|
-
if (cached) return cached;
|
|
2348
|
-
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2349
|
-
if (persisted) {
|
|
2350
|
-
this.tokens.set(key2, persisted);
|
|
2351
|
-
return persisted;
|
|
2352
|
-
}
|
|
2353
|
-
const inFlight = this.pending.get(key2);
|
|
2354
|
-
if (inFlight) return inFlight;
|
|
2355
|
-
const request = this.sign(state, chainId).then((token2) => {
|
|
2356
|
-
this.tokens.set(key2, token2);
|
|
2357
|
-
writeYieldseekerSession(scope.address, scope.chainId, token2);
|
|
2358
|
-
return token2;
|
|
2359
|
-
});
|
|
2360
|
-
this.pending.set(key2, request);
|
|
2361
|
-
try {
|
|
2362
|
-
return await request;
|
|
2363
|
-
} finally {
|
|
2364
|
-
this.pending.delete(key2);
|
|
2365
|
-
}
|
|
2366
|
-
}
|
|
2367
|
-
clear(state, chainId) {
|
|
2368
|
-
if (!state || chainId === void 0) {
|
|
2369
|
-
for (const scope of this.scopes.values()) {
|
|
2370
|
-
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2371
|
-
}
|
|
2372
|
-
this.tokens.clear();
|
|
2373
|
-
this.pending.clear();
|
|
2374
|
-
this.scopes.clear();
|
|
2375
|
-
return;
|
|
2376
|
-
}
|
|
2377
|
-
const key2 = this.key(state, chainId);
|
|
2378
|
-
this.tokens.delete(key2);
|
|
2379
|
-
this.pending.delete(key2);
|
|
2380
|
-
this.scopes.delete(key2);
|
|
2381
|
-
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2382
|
-
}
|
|
2383
|
-
async sign(state, chainId) {
|
|
2384
|
-
const account = (0, import_viem4.getAddress)(state.walletAddress);
|
|
2385
|
-
const publicClient = (0, import_viem4.createPublicClient)({
|
|
2386
|
-
chain: import_chains2.base,
|
|
2387
|
-
transport: (0, import_viem4.custom)(state.provider)
|
|
2388
|
-
});
|
|
2389
|
-
const walletClient = (0, import_viem4.createWalletClient)({
|
|
2390
|
-
account,
|
|
2391
|
-
chain: import_chains2.base,
|
|
2392
|
-
transport: (0, import_viem4.custom)(state.provider)
|
|
2393
|
-
});
|
|
2394
|
-
await ensureWalletOnChain(
|
|
2395
|
-
publicClient,
|
|
2396
|
-
walletClient,
|
|
2397
|
-
8453
|
|
2398
|
-
);
|
|
2399
|
-
const message = createYieldseekerSiweMessage(
|
|
2400
|
-
account,
|
|
2401
|
-
chainId,
|
|
2402
|
-
this.dependencies
|
|
2403
|
-
);
|
|
2404
|
-
const signature = await walletClient.signMessage({ account, message });
|
|
2405
|
-
return encodeYieldseekerAuthToken({ message, signature });
|
|
2406
|
-
}
|
|
2407
|
-
};
|
|
2408
|
-
|
|
2409
|
-
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2410
|
-
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2411
|
-
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2412
|
-
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2413
|
-
}
|
|
2414
|
-
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2415
|
-
var YieldseekerApiError = class extends Error {
|
|
2416
|
-
constructor(status, providerCode, responseFields) {
|
|
2417
|
-
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2418
|
-
this.status = status;
|
|
2419
|
-
this.providerCode = providerCode;
|
|
2420
|
-
this.responseFields = responseFields;
|
|
2421
|
-
this.name = "YieldseekerApiError";
|
|
2422
|
-
}
|
|
2423
|
-
status;
|
|
2424
|
-
providerCode;
|
|
2425
|
-
responseFields;
|
|
2426
|
-
get isAuthenticationError() {
|
|
2427
|
-
return this.status === 401 || this.status === 403;
|
|
2428
|
-
}
|
|
2429
|
-
};
|
|
2430
|
-
function providerError(body, fallback) {
|
|
2431
|
-
if (!body || typeof body !== "object") return { code: fallback };
|
|
2432
|
-
const record = body;
|
|
2433
|
-
return {
|
|
2434
|
-
code: typeof record.message === "string" ? record.message : fallback,
|
|
2435
|
-
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2436
|
-
};
|
|
2437
|
-
}
|
|
2438
|
-
var YieldseekerApiClient = class {
|
|
2439
|
-
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
|
|
2440
|
-
this.owneyApiKey = owneyApiKey;
|
|
2441
|
-
this.baseUrl = baseUrl;
|
|
2442
|
-
this.fetchFn = fetchFn;
|
|
2443
|
-
}
|
|
2444
|
-
owneyApiKey;
|
|
2445
|
-
baseUrl;
|
|
2446
|
-
fetchFn;
|
|
2447
|
-
async request(path, options = {}) {
|
|
2448
|
-
const controller = new AbortController();
|
|
2449
|
-
const timer = setTimeout(
|
|
2450
|
-
() => controller.abort(),
|
|
2451
|
-
options.timeoutMs ?? 15e3
|
|
2452
|
-
);
|
|
2453
|
-
try {
|
|
2454
|
-
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2455
|
-
method: options.method ?? "GET",
|
|
2456
|
-
headers: {
|
|
2457
|
-
"Content-Type": "application/json",
|
|
2458
|
-
"x-owney-api-key": this.owneyApiKey,
|
|
2459
|
-
...options.signature ? { "X-Signature": options.signature } : {}
|
|
2460
|
-
},
|
|
2461
|
-
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2462
|
-
signal: controller.signal
|
|
2463
|
-
});
|
|
2464
|
-
const payload = await response.json().catch(() => null);
|
|
2465
|
-
if (!response.ok) {
|
|
2466
|
-
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2467
|
-
throw new YieldseekerApiError(
|
|
2468
|
-
response.status,
|
|
2469
|
-
error.code,
|
|
2470
|
-
error.fields
|
|
2471
|
-
);
|
|
2472
|
-
}
|
|
2473
|
-
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2474
|
-
return payload.data;
|
|
2475
|
-
}
|
|
2476
|
-
return payload;
|
|
2477
|
-
} catch (error) {
|
|
2478
|
-
if (error instanceof YieldseekerApiError) throw error;
|
|
2479
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2480
|
-
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2481
|
-
}
|
|
2482
|
-
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2483
|
-
cause: error instanceof Error ? error.message : String(error)
|
|
2484
|
-
});
|
|
2485
|
-
} finally {
|
|
2486
|
-
clearTimeout(timer);
|
|
2487
|
-
}
|
|
2488
|
-
}
|
|
2489
|
-
};
|
|
2490
|
-
|
|
2491
|
-
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2492
|
-
var import_viem5 = require("viem");
|
|
2493
|
-
var REBALANCE_AMOUNT_DECIMALS = {
|
|
2494
|
-
USDC: 6,
|
|
2495
|
-
WETH: 18
|
|
2496
|
-
};
|
|
2497
|
-
function rebalanceAmount(value, tokenSymbol) {
|
|
2498
|
-
const decimals = REBALANCE_AMOUNT_DECIMALS[tokenSymbol.toUpperCase()];
|
|
2499
|
-
if (decimals === void 0) {
|
|
2500
|
-
return invalid(
|
|
2501
|
-
"history",
|
|
2502
|
-
`unsupported rebalance token ${tokenSymbol || "<empty>"}`
|
|
2503
|
-
);
|
|
2504
|
-
}
|
|
2505
|
-
try {
|
|
2506
|
-
return (0, import_viem5.formatUnits)(BigInt(value), decimals);
|
|
2507
|
-
} catch {
|
|
2508
|
-
return invalid("history", `invalid rebalance amount ${value}`);
|
|
2509
|
-
}
|
|
2510
|
-
}
|
|
2511
|
-
function invalid(endpoint, detail) {
|
|
2512
|
-
throw new OwneyError(
|
|
2513
|
-
"AGENT_INVALID_RESPONSE",
|
|
2514
|
-
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2515
|
-
{ endpoint, detail },
|
|
2516
|
-
"yieldseeker"
|
|
2517
|
-
);
|
|
2518
|
-
}
|
|
2519
|
-
function requireObject(value, endpoint) {
|
|
2520
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2521
|
-
return invalid(endpoint, "expected an object");
|
|
2522
|
-
}
|
|
2523
|
-
return value;
|
|
2524
|
-
}
|
|
2525
|
-
function requireEnvelope(value, key2, endpoint) {
|
|
2526
|
-
const root = requireObject(value, endpoint);
|
|
2527
|
-
return requireObject(root[key2], endpoint);
|
|
2528
|
-
}
|
|
2529
|
-
function token(value) {
|
|
2530
|
-
return {
|
|
2531
|
-
chain: value.chain,
|
|
2532
|
-
chainId: value.chainId,
|
|
2533
|
-
asset: value.asset,
|
|
2534
|
-
amount: String(value.amount)
|
|
2535
|
-
};
|
|
2536
|
-
}
|
|
2537
|
-
function position(value) {
|
|
2538
|
-
return {
|
|
2539
|
-
chain: String(value.chain),
|
|
2540
|
-
protocol: String(value.protocol),
|
|
2541
|
-
...value.protocolId ? { protocolId: value.protocolId } : {},
|
|
2542
|
-
...value.pool ? { pool: value.pool } : {},
|
|
2543
|
-
asset: String(value.asset),
|
|
2544
|
-
amount: String(value.amount),
|
|
2545
|
-
...value.amountRaw !== void 0 ? { amountRaw: value.amountRaw } : {},
|
|
2546
|
-
...typeof value.apy === "number" ? { apy: value.apy } : {},
|
|
2547
|
-
...typeof value.tvl === "number" ? { tvl: value.tvl } : {},
|
|
2548
|
-
...typeof value.liquidity === "number" ? { liquidity: value.liquidity } : {}
|
|
2549
|
-
};
|
|
2550
|
-
}
|
|
2551
|
-
function mapApyMatrix(matrix) {
|
|
2552
|
-
const mapped = {};
|
|
2553
|
-
for (const [chain, assets] of Object.entries(matrix ?? {})) {
|
|
2554
|
-
const chainId = Number(chain);
|
|
2555
|
-
if (chainId !== 8453 || !assets || typeof assets !== "object") continue;
|
|
2556
|
-
mapped[8453] = {};
|
|
2557
|
-
for (const [asset, apy] of Object.entries(assets)) {
|
|
2558
|
-
if ((asset === "USDC" || asset === "WETH") && typeof apy === "number") {
|
|
2559
|
-
mapped[8453][asset] = apy;
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2562
|
-
}
|
|
2563
|
-
return mapped;
|
|
2564
|
-
}
|
|
2565
|
-
function mapYieldseekerProfile(value) {
|
|
2566
|
-
const profile = requireEnvelope(
|
|
2567
|
-
value,
|
|
2568
|
-
"profile",
|
|
2569
|
-
"profile"
|
|
2570
|
-
);
|
|
2571
|
-
if (typeof profile.address !== "string" || typeof profile.smartWallet !== "string" || !Array.isArray(profile.chains) || typeof profile.hasActiveSessionKey !== "boolean" || !Array.isArray(profile.protocols)) {
|
|
2572
|
-
return invalid("profile", "missing required fields");
|
|
2573
|
-
}
|
|
2574
|
-
return {
|
|
2575
|
-
address: profile.address,
|
|
2576
|
-
smartWallet: profile.smartWallet,
|
|
2577
|
-
chains: profile.chains.map(Number),
|
|
2578
|
-
hasActiveSessionKey: profile.hasActiveSessionKey,
|
|
2579
|
-
protocols: profile.protocols.map(String)
|
|
2580
|
-
};
|
|
2581
|
-
}
|
|
2582
|
-
function mapYieldseekerBalances(value) {
|
|
2583
|
-
const balances = requireEnvelope(
|
|
2584
|
-
value,
|
|
2585
|
-
"balances",
|
|
2586
|
-
"balances"
|
|
2587
|
-
);
|
|
2588
|
-
if (typeof balances.smartWallet !== "string" || typeof balances.totalBalance !== "string" || typeof balances.totalBalanceAsset !== "string" || !Array.isArray(balances.tokens) || !Array.isArray(balances.positions)) {
|
|
2589
|
-
return invalid("balances", "missing required fields");
|
|
2590
|
-
}
|
|
2591
|
-
return {
|
|
2592
|
-
smartWallet: balances.smartWallet,
|
|
2593
|
-
totalBalance: balances.totalBalance,
|
|
2594
|
-
totalBalanceAsset: balances.totalBalanceAsset.toLowerCase(),
|
|
2595
|
-
tokens: balances.tokens.map(token),
|
|
2596
|
-
positions: balances.positions.map(position)
|
|
2597
|
-
};
|
|
2598
|
-
}
|
|
2599
|
-
function mapYieldseekerEarnings(value) {
|
|
2600
|
-
const earnings = requireEnvelope(
|
|
2601
|
-
value,
|
|
2602
|
-
"earnings",
|
|
2603
|
-
"earnings"
|
|
2604
|
-
);
|
|
2605
|
-
if (typeof earnings.smartWallet !== "string" || typeof earnings.lifetimeEarnings !== "number" || !Array.isArray(earnings.tokens)) {
|
|
2606
|
-
return invalid("earnings", "missing required fields");
|
|
2607
|
-
}
|
|
2608
|
-
return {
|
|
2609
|
-
smartWallet: earnings.smartWallet,
|
|
2610
|
-
lifetimeEarnings: earnings.lifetimeEarnings,
|
|
2611
|
-
tokens: earnings.tokens.map(token)
|
|
2612
|
-
};
|
|
2613
|
-
}
|
|
2614
|
-
function mapYieldseekerApy(value) {
|
|
2615
|
-
const apy = requireEnvelope(
|
|
2616
|
-
value,
|
|
2617
|
-
"apy",
|
|
2618
|
-
"apy"
|
|
2619
|
-
);
|
|
2620
|
-
if (typeof apy.walletAddress !== "string" || typeof apy.weightedApyAfterFee !== "number" || !apy.apyByChainAndAsset || typeof apy.apyByChainAndAsset !== "object" || !Array.isArray(apy.history)) {
|
|
2621
|
-
return invalid("apy", "missing required fields");
|
|
2622
|
-
}
|
|
2623
|
-
return {
|
|
2624
|
-
walletAddress: apy.walletAddress,
|
|
2625
|
-
weightedApyAfterFee: apy.weightedApyAfterFee,
|
|
2626
|
-
apyByChainAndAsset: mapApyMatrix(apy.apyByChainAndAsset),
|
|
2627
|
-
history: apy.history.map((point) => ({
|
|
2628
|
-
date: String(point.date),
|
|
2629
|
-
apy: Number(point.apy)
|
|
2630
|
-
}))
|
|
2631
|
-
};
|
|
2632
|
-
}
|
|
2633
|
-
function historyAction(value) {
|
|
2634
|
-
const normalized = value.trim().toLowerCase();
|
|
2635
|
-
if (normalized === "rebalance") return "Rebalance";
|
|
2636
|
-
if (normalized === "deposit") return "Deposit";
|
|
2637
|
-
if (normalized === "top up" || normalized === "topup") return "Top up";
|
|
2638
|
-
if (normalized === "withdraw" || normalized === "withdrawal")
|
|
2639
|
-
return "Withdraw";
|
|
2640
|
-
if (normalized === "earned" || normalized === "earnings") return "Earned";
|
|
2641
|
-
return invalid("history", `unsupported action ${value}`);
|
|
2642
|
-
}
|
|
2643
|
-
function historyItem(value) {
|
|
2644
|
-
return {
|
|
2645
|
-
agent: "yieldseeker",
|
|
2646
|
-
action: historyAction(value.action),
|
|
2647
|
-
date: String(value.date),
|
|
2648
|
-
oldApy: value.oldApy === null ? null : String(value.oldApy),
|
|
2649
|
-
newApy: value.newApy === null ? null : String(value.newApy),
|
|
2650
|
-
transactions: (value.transactions ?? []).map((transaction) => ({
|
|
2651
|
-
txHashes: transaction.txHashes.map(String),
|
|
2652
|
-
...typeof transaction.chainId === "number" ? { chainId: transaction.chainId } : {},
|
|
2653
|
-
...typeof transaction.tokenSymbol === "string" ? { tokenSymbol: transaction.tokenSymbol } : {},
|
|
2654
|
-
...typeof transaction.amount === "string" ? { amount: transaction.amount } : {}
|
|
2655
|
-
})),
|
|
2656
|
-
rebalanceLog: (value.rebalanceLog ?? []).map((entry) => ({
|
|
2657
|
-
fromProtocol: String(entry.fromProtocol),
|
|
2658
|
-
toProtocol: String(entry.toProtocol),
|
|
2659
|
-
...entry.fromPool ? { fromPool: entry.fromPool } : {},
|
|
2660
|
-
...entry.toPool ? { toPool: entry.toPool } : {},
|
|
2661
|
-
tokenSymbol: String(entry.tokenSymbol),
|
|
2662
|
-
// Unlike Yieldseeker's other read-response amounts, live history
|
|
2663
|
-
// rebalance amounts are returned in the token's smallest unit. Normalize
|
|
2664
|
-
// them at the adapter boundary so every Owney consumer receives the
|
|
2665
|
-
// shared human-readable decimal shape.
|
|
2666
|
-
amount: rebalanceAmount(String(entry.amount), String(entry.tokenSymbol)),
|
|
2667
|
-
status: entry.status
|
|
2668
|
-
}))
|
|
2669
|
-
};
|
|
2670
|
-
}
|
|
2671
|
-
function mapYieldseekerHistory(value) {
|
|
2672
|
-
const history = requireEnvelope(
|
|
2673
|
-
value,
|
|
2674
|
-
"history",
|
|
2675
|
-
"history"
|
|
2676
|
-
);
|
|
2677
|
-
if (!Array.isArray(history.data) || typeof history.hasMore !== "boolean") {
|
|
2678
|
-
return invalid("history", "missing required fields");
|
|
2679
|
-
}
|
|
2680
|
-
return {
|
|
2681
|
-
data: history.data.map(historyItem),
|
|
2682
|
-
hasMore: history.hasMore,
|
|
2683
|
-
...history.hasMore && typeof history.nextCursor === "string" ? { nextCursor: history.nextCursor } : {}
|
|
2684
|
-
};
|
|
2685
|
-
}
|
|
2686
|
-
function mapYieldseekerAgentApy(value) {
|
|
2687
|
-
const agentApy = requireEnvelope(
|
|
2688
|
-
value,
|
|
2689
|
-
"agentApy",
|
|
2690
|
-
"agent APY"
|
|
2691
|
-
);
|
|
2692
|
-
if (typeof agentApy.averageApy !== "number") {
|
|
2693
|
-
return invalid("agent APY", "missing averageApy");
|
|
2694
|
-
}
|
|
2695
|
-
return {
|
|
2696
|
-
averageApy: agentApy.averageApy,
|
|
2697
|
-
...agentApy.detailedApys ? {
|
|
2698
|
-
detailedApys: {
|
|
2699
|
-
apyPerAsset: mapApyMatrix(agentApy.detailedApys.apyPerAsset)
|
|
2700
|
-
}
|
|
2701
|
-
} : {}
|
|
2702
|
-
};
|
|
2703
|
-
}
|
|
2704
|
-
|
|
2705
|
-
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2706
|
-
function query(params) {
|
|
2707
|
-
const search = new URLSearchParams();
|
|
2708
|
-
for (const [key2, value] of Object.entries(params)) {
|
|
2709
|
-
if (value !== void 0) search.set(key2, String(value));
|
|
2710
|
-
}
|
|
2711
|
-
const encoded = search.toString();
|
|
2712
|
-
return encoded ? `?${encoded}` : "";
|
|
2713
|
-
}
|
|
2714
|
-
var YieldseekerAgent = class {
|
|
2715
|
-
id = "yieldseeker";
|
|
2716
|
-
balanceComposition = "tokens-plus-positions";
|
|
2717
|
-
supportedChainIds = [8453];
|
|
2718
|
-
supportedAssets = [
|
|
2719
|
-
{
|
|
2720
|
-
chainId: 8453,
|
|
2721
|
-
chain: "BASE",
|
|
2722
|
-
assets: [
|
|
2723
|
-
{ symbol: "USDC", minDepositAmount: "1" },
|
|
2724
|
-
{ symbol: "WETH", minDepositAmount: "1" }
|
|
2725
|
-
]
|
|
2726
|
-
}
|
|
2727
|
-
];
|
|
2728
|
-
api;
|
|
2729
|
-
auth;
|
|
2730
|
-
transactionExecutor;
|
|
2731
|
-
unwindReceiptWaiter;
|
|
2732
|
-
activated = /* @__PURE__ */ new Set();
|
|
2733
|
-
constructor(owneyApiKey, options = {}) {
|
|
2734
|
-
this.api = new YieldseekerApiClient(
|
|
2735
|
-
owneyApiKey,
|
|
2736
|
-
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
2737
|
-
options.fetchFn
|
|
2738
|
-
);
|
|
2739
|
-
this.auth = new YieldseekerAuth(options.auth);
|
|
2740
|
-
this.transactionExecutor = options.transactionExecutor;
|
|
2741
|
-
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
2742
|
-
}
|
|
2743
|
-
async disconnect() {
|
|
2744
|
-
this.auth.clear();
|
|
2745
|
-
this.activated.clear();
|
|
2746
|
-
}
|
|
2747
|
-
async activateAgent(state, chainId, asset) {
|
|
2748
|
-
this.assertChain(chainId);
|
|
2749
|
-
await this.auth.getToken(state, chainId);
|
|
2750
|
-
if (asset === "USDC" || asset === "WETH") {
|
|
2751
|
-
await this.ensureActivated(state, chainId, asset);
|
|
2752
|
-
}
|
|
2753
|
-
}
|
|
2754
|
-
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
2755
|
-
this.assertChain(chainId);
|
|
2756
|
-
this.assertAsset(asset);
|
|
2757
|
-
if (BigInt(amount) <= 0n) {
|
|
2758
|
-
throw new OwneyError(
|
|
2759
|
-
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
2760
|
-
"Yieldseeker deposits must be greater than zero.",
|
|
2761
|
-
{ amount, minDepositAmount: "1" },
|
|
2762
|
-
this.id
|
|
2763
|
-
);
|
|
2764
|
-
}
|
|
2765
|
-
await this.ensureActivated(state, chainId, asset);
|
|
2766
|
-
const response = await this.walletRequest(
|
|
2767
|
-
state,
|
|
2768
|
-
chainId,
|
|
2769
|
-
"/deposits",
|
|
2770
|
-
{ method: "POST", body: { chainId, asset, amount } }
|
|
2771
|
-
);
|
|
2772
|
-
const deposit = response?.deposit;
|
|
2773
|
-
if (!deposit?.transaction || typeof deposit.amount !== "string" || deposit.amount !== amount || typeof deposit.smartWallet !== "string" || !(0, import_viem6.isAddress)(deposit.smartWallet)) {
|
|
2774
|
-
throw this.invalidResponse("deposit");
|
|
2775
|
-
}
|
|
2776
|
-
if (depositCallback) {
|
|
2777
|
-
provideDepositVerificationContext(depositCallback, {
|
|
2778
|
-
agentId: "yieldseeker",
|
|
2779
|
-
// Re-read after /deposits so an authentication retry cannot forward
|
|
2780
|
-
// the stale token that Yieldseeker just rejected.
|
|
2781
|
-
signature: await this.auth.getToken(state, chainId)
|
|
2782
|
-
});
|
|
2783
|
-
}
|
|
2784
|
-
const txHash = depositCallback ? await depositCallback(deposit.smartWallet, chainId, amount) : await this.submitTransaction(state, chainId, deposit.transaction);
|
|
2785
|
-
const confirmation = await this.walletRequest(
|
|
2786
|
-
state,
|
|
2787
|
-
chainId,
|
|
2788
|
-
"/deposits/confirm",
|
|
2789
|
-
{ method: "POST", body: { chainId, asset } }
|
|
2790
|
-
);
|
|
2791
|
-
if (typeof confirmation?.confirmation?.autoseekQueued !== "boolean") {
|
|
2792
|
-
throw this.invalidResponse("deposit confirmation", {
|
|
2793
|
-
transactionHash: txHash,
|
|
2794
|
-
transactionConfirmed: true
|
|
2795
|
-
});
|
|
2796
|
-
}
|
|
2797
|
-
return {
|
|
2798
|
-
txHash,
|
|
2799
|
-
smartWallet: deposit.smartWallet,
|
|
2800
|
-
amount: deposit.amount
|
|
2801
|
-
};
|
|
2802
|
-
}
|
|
2803
|
-
async withdraw(state, chainId, asset, amount) {
|
|
2804
|
-
this.assertChain(chainId);
|
|
2805
|
-
this.assertAsset(asset);
|
|
2806
|
-
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
2807
|
-
throw new OwneyError(
|
|
2808
|
-
"WITHDRAW_FAILED",
|
|
2809
|
-
"Yieldseeker withdrawals must be greater than zero.",
|
|
2810
|
-
{ amount },
|
|
2811
|
-
this.id
|
|
2812
|
-
);
|
|
2813
|
-
}
|
|
2814
|
-
const balances = await this.getBalances(state, chainId);
|
|
2815
|
-
const decimals = asset === "USDC" ? 6 : 18;
|
|
2816
|
-
const targetAsset = asset.toUpperCase();
|
|
2817
|
-
const idle = balances.tokens.filter(
|
|
2818
|
-
(token2) => token2.chainId === chainId && token2.asset.toUpperCase() === targetAsset
|
|
2819
|
-
).reduce((total, token2) => total + (0, import_viem6.parseUnits)(token2.amount, decimals), 0n);
|
|
2820
|
-
const positions = (balances.positions ?? []).filter(
|
|
2821
|
-
(position2) => this.positionMatchesChain(position2.chain, chainId) && position2.asset.toUpperCase() === targetAsset
|
|
2822
|
-
).map((position2) => ({
|
|
2823
|
-
position: position2,
|
|
2824
|
-
amount: position2.amountRaw !== void 0 ? BigInt(position2.amountRaw) : (0, import_viem6.parseUnits)(position2.amount, decimals)
|
|
2825
|
-
}));
|
|
2826
|
-
const deployed = positions.reduce(
|
|
2827
|
-
(total, position2) => total + position2.amount,
|
|
2828
|
-
0n
|
|
2829
|
-
);
|
|
2830
|
-
const totalAvailable = idle + deployed;
|
|
2831
|
-
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
2832
|
-
if (requested > totalAvailable) {
|
|
2833
|
-
throw new OwneyError(
|
|
2834
|
-
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
2835
|
-
`Requested withdrawal "${requested.toString()}" exceeds available Yieldseeker balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
2836
|
-
{
|
|
2837
|
-
asset,
|
|
2838
|
-
requested: requested.toString(),
|
|
2839
|
-
available: totalAvailable.toString()
|
|
2840
|
-
},
|
|
2841
|
-
this.id
|
|
2842
|
-
);
|
|
2843
|
-
}
|
|
2844
|
-
const requiredFromPositions = requested > idle ? requested - idle : 0n;
|
|
2845
|
-
const unwindPlans = [];
|
|
2846
|
-
let remainingToUnwind = requiredFromPositions;
|
|
2847
|
-
for (const { position: position2, amount: positionAmount } of positions) {
|
|
2848
|
-
if (remainingToUnwind === 0n) break;
|
|
2849
|
-
if (positionAmount <= 0n) continue;
|
|
2850
|
-
if (!position2.protocolId || !(0, import_viem6.isAddress)(position2.protocolId)) {
|
|
2851
|
-
throw this.invalidResponse("position", {
|
|
2852
|
-
reason: "A deployed position is missing its vault address.",
|
|
2853
|
-
protocol: position2.protocol,
|
|
2854
|
-
pool: position2.pool
|
|
2855
|
-
});
|
|
2856
|
-
}
|
|
2857
|
-
const unwindAmount = positionAmount < remainingToUnwind ? positionAmount : remainingToUnwind;
|
|
2858
|
-
unwindPlans.push({
|
|
2859
|
-
vaultAddress: (0, import_viem6.getAddress)(position2.protocolId),
|
|
2860
|
-
amount: unwindAmount
|
|
2861
|
-
});
|
|
2862
|
-
remainingToUnwind -= unwindAmount;
|
|
2863
|
-
}
|
|
2864
|
-
if (remainingToUnwind > 0n) {
|
|
2865
|
-
throw this.invalidResponse("balances", {
|
|
2866
|
-
reason: "Deployed position balances could not cover the unwind.",
|
|
2867
|
-
required: requiredFromPositions.toString(),
|
|
2868
|
-
planned: (requiredFromPositions - remainingToUnwind).toString()
|
|
2869
|
-
});
|
|
2870
|
-
}
|
|
2871
|
-
for (const plan of unwindPlans) {
|
|
2872
|
-
const response2 = await this.walletRequest(
|
|
2873
|
-
state,
|
|
2874
|
-
chainId,
|
|
2875
|
-
"/positions/unwind",
|
|
2876
|
-
{
|
|
2877
|
-
method: "POST",
|
|
2878
|
-
body: {
|
|
2879
|
-
chainId,
|
|
2880
|
-
asset,
|
|
2881
|
-
vaultAddress: plan.vaultAddress,
|
|
2882
|
-
amount: plan.amount.toString()
|
|
2883
|
-
}
|
|
2884
|
-
}
|
|
2885
|
-
);
|
|
2886
|
-
const transactionHash = response2?.unwind?.transactionHash;
|
|
2887
|
-
if (!this.isTransactionHash(transactionHash)) {
|
|
2888
|
-
throw this.invalidResponse("position unwind");
|
|
2889
|
-
}
|
|
2890
|
-
await this.waitForUnwindReceipt(state, chainId, transactionHash);
|
|
2891
|
-
}
|
|
2892
|
-
const response = await this.walletRequest(
|
|
2893
|
-
state,
|
|
2894
|
-
chainId,
|
|
2895
|
-
"/withdrawals",
|
|
2896
|
-
{
|
|
2897
|
-
method: "POST",
|
|
2898
|
-
body: { chainId, asset, ...amount !== void 0 ? { amount } : {} }
|
|
2899
|
-
}
|
|
2900
|
-
);
|
|
2901
|
-
const withdrawal = response?.withdrawal;
|
|
2902
|
-
if (!withdrawal?.transaction || typeof withdrawal.amount !== "string") {
|
|
2903
|
-
throw this.invalidResponse("withdrawal");
|
|
2904
|
-
}
|
|
2905
|
-
const txHash = await this.submitTransaction(
|
|
2906
|
-
state,
|
|
2907
|
-
chainId,
|
|
2908
|
-
withdrawal.transaction
|
|
2909
|
-
);
|
|
2910
|
-
return {
|
|
2911
|
-
txHash,
|
|
2912
|
-
type: amount === void 0 ? "full" : "partial",
|
|
2913
|
-
amount: withdrawal.amount
|
|
2914
|
-
};
|
|
2915
|
-
}
|
|
2916
|
-
async getBalances(state, chainId) {
|
|
2917
|
-
return mapYieldseekerBalances(
|
|
2918
|
-
await this.walletRequest(
|
|
2919
|
-
state,
|
|
2920
|
-
chainId,
|
|
2921
|
-
`/balances${query({ chainId })}`
|
|
2922
|
-
)
|
|
2923
|
-
);
|
|
2924
|
-
}
|
|
2925
|
-
async getEarnings(state, chainId) {
|
|
2926
|
-
return mapYieldseekerEarnings(
|
|
2927
|
-
await this.walletRequest(
|
|
2928
|
-
state,
|
|
2929
|
-
chainId,
|
|
2930
|
-
`/earnings${query({ chainId })}`
|
|
2931
|
-
)
|
|
2932
|
-
);
|
|
2933
|
-
}
|
|
2934
|
-
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
2935
|
-
return mapYieldseekerApy(
|
|
2936
|
-
await this.walletRequest(
|
|
2937
|
-
state,
|
|
2938
|
-
chainId,
|
|
2939
|
-
`/apy${query({ chainId, days, tokenSymbol })}`
|
|
2940
|
-
)
|
|
2941
|
-
);
|
|
2942
|
-
}
|
|
2943
|
-
async getHistory(state, chainId, options) {
|
|
2944
|
-
return mapYieldseekerHistory(
|
|
2945
|
-
await this.walletRequest(
|
|
2946
|
-
state,
|
|
2947
|
-
chainId,
|
|
2948
|
-
`/history${query({
|
|
2949
|
-
chainId,
|
|
2950
|
-
limit: options?.limit ?? 10,
|
|
2951
|
-
cursor: options?.cursor,
|
|
2952
|
-
fromDate: options?.fromDate,
|
|
2953
|
-
toDate: options?.toDate
|
|
2954
|
-
})}`
|
|
2955
|
-
)
|
|
2956
|
-
);
|
|
2957
|
-
}
|
|
2958
|
-
async getUserProfile(state, chainId) {
|
|
2959
|
-
return mapYieldseekerProfile(
|
|
2960
|
-
await this.walletRequest(state, chainId, `/profile${query({ chainId })}`)
|
|
2961
|
-
);
|
|
2962
|
-
}
|
|
2963
|
-
async getAgentApy(days, options) {
|
|
2964
|
-
this.assertOptionalChain(options?.chainId);
|
|
2965
|
-
return mapYieldseekerAgentApy(
|
|
2966
|
-
await this.api.request(
|
|
2967
|
-
`/agent/apy${query({
|
|
2968
|
-
days,
|
|
2969
|
-
tokenSymbol: options?.tokenSymbol,
|
|
2970
|
-
chainId: options?.chainId
|
|
2971
|
-
})}`
|
|
2972
|
-
)
|
|
2973
|
-
);
|
|
2974
|
-
}
|
|
2975
|
-
async ensureActivated(state, chainId, asset) {
|
|
2976
|
-
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${asset}`;
|
|
2977
|
-
if (this.activated.has(key2)) return;
|
|
2978
|
-
const response = await this.walletRequest(
|
|
2979
|
-
state,
|
|
2980
|
-
chainId,
|
|
2981
|
-
"/activation",
|
|
2982
|
-
{ method: "POST", body: { chainId, asset } }
|
|
2983
|
-
);
|
|
2984
|
-
if (typeof response?.activation?.smartWallet !== "string" || typeof response.activation.deployed !== "boolean" || typeof response.activation.hasActiveSessionKey !== "boolean") {
|
|
2985
|
-
throw this.invalidResponse("activation");
|
|
2986
|
-
}
|
|
2987
|
-
this.activated.add(key2);
|
|
2988
|
-
}
|
|
2989
|
-
async walletRequest(state, chainId, path, options = {}) {
|
|
2990
|
-
this.assertChain(chainId);
|
|
2991
|
-
let signature = await this.auth.getToken(state, chainId);
|
|
2992
|
-
try {
|
|
2993
|
-
return await this.api.request(path, { ...options, signature });
|
|
2994
|
-
} catch (error) {
|
|
2995
|
-
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
2996
|
-
if (error.isAuthenticationError) {
|
|
2997
|
-
this.auth.clear(state, chainId);
|
|
2998
|
-
signature = await this.auth.getToken(state, chainId);
|
|
2999
|
-
try {
|
|
3000
|
-
return await this.api.request(path, { ...options, signature });
|
|
3001
|
-
} catch (retryError) {
|
|
3002
|
-
throw this.mapApiError(retryError);
|
|
3003
|
-
}
|
|
3004
|
-
}
|
|
3005
|
-
throw this.mapApiError(error);
|
|
3006
|
-
}
|
|
3007
|
-
}
|
|
3008
|
-
mapApiError(error) {
|
|
3009
|
-
if (!(error instanceof YieldseekerApiError)) {
|
|
3010
|
-
return new OwneyError(
|
|
3011
|
-
"AGENT_API_ERROR",
|
|
3012
|
-
"Yieldseeker request failed.",
|
|
3013
|
-
{ cause: error instanceof Error ? error.message : String(error) },
|
|
3014
|
-
this.id
|
|
3015
|
-
);
|
|
3016
|
-
}
|
|
3017
|
-
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
3018
|
-
return new OwneyError(
|
|
3019
|
-
code,
|
|
3020
|
-
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3021
|
-
{
|
|
3022
|
-
statusCode: error.status,
|
|
3023
|
-
providerCode: error.providerCode,
|
|
3024
|
-
...error.responseFields ? { fields: error.responseFields } : {}
|
|
3025
|
-
},
|
|
3026
|
-
this.id
|
|
3027
|
-
);
|
|
3028
|
-
}
|
|
3029
|
-
async submitTransaction(state, chainId, transaction) {
|
|
3030
|
-
if (this.transactionExecutor) {
|
|
3031
|
-
return this.transactionExecutor(state, chainId, transaction);
|
|
3032
|
-
}
|
|
3033
|
-
if (typeof transaction.from !== "string" || typeof transaction.to !== "string" || typeof transaction.data !== "string" || typeof transaction.value !== "string" || transaction.chainId !== chainId) {
|
|
3034
|
-
throw this.invalidResponse("transaction");
|
|
3035
|
-
}
|
|
3036
|
-
const account = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3037
|
-
if ((0, import_viem6.getAddress)(transaction.from) !== account) {
|
|
3038
|
-
throw this.invalidResponse("transaction", {
|
|
3039
|
-
reason: "transaction.from does not match the connected wallet"
|
|
3040
|
-
});
|
|
3041
|
-
}
|
|
3042
|
-
const walletClient = (0, import_viem6.createWalletClient)({
|
|
3043
|
-
account,
|
|
3044
|
-
chain: import_chains3.base,
|
|
3045
|
-
transport: (0, import_viem6.custom)(state.provider)
|
|
3046
|
-
});
|
|
3047
|
-
const publicClient = (0, import_viem6.createPublicClient)({
|
|
3048
|
-
chain: import_chains3.base,
|
|
3049
|
-
transport: (0, import_viem6.custom)(state.provider)
|
|
3050
|
-
});
|
|
3051
|
-
await ensureWalletOnChain(
|
|
3052
|
-
publicClient,
|
|
3053
|
-
walletClient,
|
|
3054
|
-
8453
|
|
3055
|
-
);
|
|
3056
|
-
const hash = await walletClient.sendTransaction({
|
|
3057
|
-
account,
|
|
3058
|
-
chain: import_chains3.base,
|
|
3059
|
-
to: (0, import_viem6.getAddress)(transaction.to),
|
|
3060
|
-
data: transaction.data,
|
|
3061
|
-
value: BigInt(transaction.value)
|
|
3062
|
-
});
|
|
3063
|
-
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3064
|
-
hash,
|
|
3065
|
-
confirmations: 1
|
|
3066
|
-
});
|
|
3067
|
-
if (receipt.status !== "success") {
|
|
3068
|
-
throw new OwneyError(
|
|
3069
|
-
"AGENT_TRANSACTION_REVERTED",
|
|
3070
|
-
`Yieldseeker transaction reverted (${hash}).`,
|
|
3071
|
-
{ transactionHash: hash },
|
|
3072
|
-
this.id
|
|
3073
|
-
);
|
|
3074
|
-
}
|
|
3075
|
-
return hash;
|
|
3076
|
-
}
|
|
3077
|
-
async waitForUnwindReceipt(state, chainId, transactionHash) {
|
|
3078
|
-
if (this.unwindReceiptWaiter) {
|
|
3079
|
-
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3080
|
-
return;
|
|
3081
|
-
}
|
|
3082
|
-
const publicClient = (0, import_viem6.createPublicClient)({
|
|
3083
|
-
chain: import_chains3.base,
|
|
3084
|
-
transport: (0, import_viem6.custom)(state.provider)
|
|
3085
|
-
});
|
|
3086
|
-
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3087
|
-
hash: transactionHash,
|
|
3088
|
-
confirmations: 1
|
|
3089
|
-
});
|
|
3090
|
-
if (receipt.status !== "success") {
|
|
3091
|
-
throw new OwneyError(
|
|
3092
|
-
"AGENT_TRANSACTION_REVERTED",
|
|
3093
|
-
`Yieldseeker position unwind reverted (${transactionHash}).`,
|
|
3094
|
-
{ transactionHash },
|
|
3095
|
-
this.id
|
|
3096
|
-
);
|
|
3097
|
-
}
|
|
3098
|
-
}
|
|
3099
|
-
positionMatchesChain(chain, chainId) {
|
|
3100
|
-
const normalized = chain.trim().toUpperCase();
|
|
3101
|
-
return normalized === String(chainId) || normalized === "BASE";
|
|
3102
|
-
}
|
|
3103
|
-
isTransactionHash(value) {
|
|
3104
|
-
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
3105
|
-
}
|
|
3106
|
-
assertChain(chainId) {
|
|
3107
|
-
if (chainId !== 8453) {
|
|
3108
|
-
throw new OwneyError(
|
|
3109
|
-
"CHAIN_UNSUPPORTED",
|
|
3110
|
-
`Yieldseeker does not support chain ${chainId}.`,
|
|
3111
|
-
{ chainId, supportedChainIds: [8453] },
|
|
3112
|
-
this.id
|
|
3113
|
-
);
|
|
3114
|
-
}
|
|
3115
|
-
}
|
|
3116
|
-
assertOptionalChain(chainId) {
|
|
3117
|
-
if (chainId !== void 0) this.assertChain(chainId);
|
|
3118
|
-
}
|
|
3119
|
-
assertAsset(asset) {
|
|
3120
|
-
if (asset !== "USDC" && asset !== "WETH") {
|
|
3121
|
-
throw new OwneyError(
|
|
3122
|
-
"ASSET_UNSUPPORTED",
|
|
3123
|
-
`Yieldseeker does not support asset ${asset}.`,
|
|
3124
|
-
{ asset, supportedAssets: ["USDC", "WETH"] },
|
|
3125
|
-
this.id
|
|
3126
|
-
);
|
|
3127
|
-
}
|
|
3128
|
-
}
|
|
3129
|
-
invalidResponse(operation, details = {}) {
|
|
3130
|
-
return new OwneyError(
|
|
3131
|
-
"AGENT_INVALID_RESPONSE",
|
|
3132
|
-
`Yieldseeker returned an invalid ${operation} response.`,
|
|
3133
|
-
details,
|
|
3134
|
-
this.id
|
|
3135
|
-
);
|
|
3136
|
-
}
|
|
3137
|
-
};
|
|
3138
|
-
|
|
3139
|
-
// src/lib/routing-api.ts
|
|
3140
|
-
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3141
|
-
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3142
|
-
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
3143
|
-
try {
|
|
3144
|
-
const res = await fetch(url, {
|
|
3145
|
-
method: "GET",
|
|
3146
|
-
headers: {
|
|
3147
|
-
"Content-Type": "application/json",
|
|
3148
|
-
"x-owney-api-key": `${apiKey}`
|
|
3149
|
-
}
|
|
3150
|
-
});
|
|
3151
|
-
if (!res.ok) {
|
|
3152
|
-
if (res.status !== 404) {
|
|
3153
|
-
console.warn(
|
|
3154
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
3155
|
-
);
|
|
3156
|
-
}
|
|
3157
|
-
return null;
|
|
3158
|
-
}
|
|
3159
|
-
const json = await res.json();
|
|
3160
|
-
const policy = json.success ? json.data ?? null : null;
|
|
3161
|
-
debugLog(
|
|
3162
|
-
"owney-sdk",
|
|
3163
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
3164
|
-
policy ?? void 0
|
|
3165
|
-
);
|
|
3166
|
-
return policy;
|
|
3167
|
-
} catch (error) {
|
|
3168
|
-
console.warn(
|
|
3169
|
-
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
3170
|
-
error instanceof Error ? error.message : String(error)
|
|
3171
|
-
);
|
|
3172
|
-
return null;
|
|
3173
|
-
}
|
|
3174
|
-
}
|
|
3175
|
-
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3176
|
-
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
3177
|
-
const res = await fetch(url, {
|
|
3178
|
-
method: "GET",
|
|
3179
|
-
headers: {
|
|
3180
|
-
"Content-Type": "application/json",
|
|
3181
|
-
"x-owney-api-key": `${apiKey}`
|
|
3182
|
-
}
|
|
3183
|
-
});
|
|
3184
|
-
if (!res.ok) {
|
|
3185
|
-
const text = await res.text().catch(() => "");
|
|
3186
|
-
throw new OwneyError(
|
|
3187
|
-
"API_ROUTING_ERROR",
|
|
3188
|
-
`Routing API error ${res.status}: ${text}`,
|
|
3189
|
-
{ statusCode: res.status, responseBody: text }
|
|
3190
|
-
);
|
|
3191
|
-
}
|
|
3192
|
-
const json = await res.json();
|
|
3193
|
-
if (!json.success) {
|
|
3194
|
-
throw new OwneyError(
|
|
3195
|
-
"API_ROUTING_FAILED",
|
|
3196
|
-
`Routing API request failed: ${json.message}`,
|
|
3197
|
-
{ message: json.message }
|
|
3198
|
-
);
|
|
3199
|
-
}
|
|
3200
|
-
return json.data;
|
|
3201
|
-
}
|
|
3202
|
-
|
|
3203
|
-
// src/lib/health-report.ts
|
|
3204
|
-
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3205
|
-
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
3206
|
-
try {
|
|
3207
|
-
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
3208
|
-
method: "POST",
|
|
3209
|
-
headers: {
|
|
3210
|
-
"Content-Type": "application/json",
|
|
3211
|
-
"x-owney-api-key": apiKey
|
|
3212
|
-
},
|
|
3213
|
-
body: JSON.stringify({
|
|
3214
|
-
agent_type: agentType,
|
|
3215
|
-
error_code: errorCode,
|
|
3216
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3217
|
-
})
|
|
3218
|
-
});
|
|
3219
|
-
} catch (err) {
|
|
3220
|
-
console.warn(
|
|
3221
|
-
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
3222
|
-
err instanceof Error ? err.message : err
|
|
3223
|
-
);
|
|
3224
|
-
}
|
|
3225
|
-
}
|
|
3226
|
-
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
3227
|
-
try {
|
|
3228
|
-
return await fn();
|
|
3229
|
-
} catch (err) {
|
|
3230
|
-
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3231
|
-
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
3232
|
-
throw err;
|
|
3233
|
-
}
|
|
3234
|
-
}
|
|
3235
|
-
|
|
3236
|
-
// src/lib/helpers/withdraw-helper.ts
|
|
3237
|
-
var import_viem7 = require("viem");
|
|
3238
|
-
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
3239
|
-
const target = asset.toUpperCase();
|
|
3240
|
-
return agents.map((agent) => {
|
|
3241
|
-
const agentBalance = aggregated[agent.id];
|
|
3242
|
-
const tokenBalance = agentBalance?.tokens.find(
|
|
3243
|
-
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
3244
|
-
);
|
|
3245
|
-
let balance = tokenBalance ? (0, import_viem7.parseUnits)(tokenBalance.amount, decimals) : 0n;
|
|
3246
|
-
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
3247
|
-
const chainNameById = {
|
|
3248
|
-
1: "ETHEREUM",
|
|
3249
|
-
8453: "BASE",
|
|
3250
|
-
42161: "ARBITRUM"
|
|
3251
|
-
};
|
|
3252
|
-
const targetChain = chainNameById[chainId];
|
|
3253
|
-
for (const position2 of agentBalance?.positions ?? []) {
|
|
3254
|
-
const positionChain = position2.chain.trim().toUpperCase();
|
|
3255
|
-
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
3256
|
-
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
3257
|
-
if (position2.amountRaw !== void 0) {
|
|
3258
|
-
try {
|
|
3259
|
-
balance += BigInt(position2.amountRaw);
|
|
3260
|
-
continue;
|
|
3261
|
-
} catch {
|
|
3262
|
-
}
|
|
3263
|
-
}
|
|
3264
|
-
balance += (0, import_viem7.parseUnits)(position2.amount, decimals);
|
|
3265
|
-
}
|
|
3266
|
-
}
|
|
3267
|
-
return { agent, balance };
|
|
2309
|
+
primaryType: "PermitTransferFrom",
|
|
2310
|
+
message: input.message
|
|
2311
|
+
};
|
|
2312
|
+
}
|
|
2313
|
+
function randomPermit2Nonce() {
|
|
2314
|
+
const bytes = new Uint8Array(32);
|
|
2315
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
2316
|
+
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
2317
|
+
}
|
|
2318
|
+
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2319
|
+
return publicClient.readContract({
|
|
2320
|
+
address: token,
|
|
2321
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2322
|
+
functionName: "allowance",
|
|
2323
|
+
args: [owner, PERMIT2_ADDRESS]
|
|
3268
2324
|
});
|
|
3269
2325
|
}
|
|
3270
|
-
function
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
3277
|
-
let remainder = requested - assigned;
|
|
3278
|
-
const byHeadroom = [...plans].sort((a, b) => {
|
|
3279
|
-
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
3280
|
-
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2326
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
2327
|
+
return publicClient.readContract({
|
|
2328
|
+
address: token,
|
|
2329
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2330
|
+
functionName: "balanceOf",
|
|
2331
|
+
args: [owner]
|
|
3281
2332
|
});
|
|
3282
|
-
for (const p of byHeadroom) {
|
|
3283
|
-
if (remainder === 0n) break;
|
|
3284
|
-
const headroom = p.balance - p.planned;
|
|
3285
|
-
if (headroom <= 0n) continue;
|
|
3286
|
-
const take = headroom < remainder ? headroom : remainder;
|
|
3287
|
-
p.planned += take;
|
|
3288
|
-
remainder -= take;
|
|
3289
|
-
}
|
|
3290
|
-
return plans;
|
|
3291
2333
|
}
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
continue;
|
|
3302
|
-
}
|
|
3303
|
-
const take = balance < remaining ? balance : remaining;
|
|
3304
|
-
plans.push({ agent, balance, planned: take });
|
|
3305
|
-
remaining -= take;
|
|
3306
|
-
}
|
|
3307
|
-
return { plans, remaining };
|
|
2334
|
+
|
|
2335
|
+
// src/lib/chain-guard.ts
|
|
2336
|
+
var CHAIN_NAMES = {
|
|
2337
|
+
1: "Ethereum",
|
|
2338
|
+
8453: "Base",
|
|
2339
|
+
42161: "Arbitrum"
|
|
2340
|
+
};
|
|
2341
|
+
function chainName(chainId) {
|
|
2342
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
3308
2343
|
}
|
|
3309
|
-
function
|
|
3310
|
-
const
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
(
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
2344
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2345
|
+
const actual = await pub.getChainId();
|
|
2346
|
+
if (actual === expected) return;
|
|
2347
|
+
try {
|
|
2348
|
+
await wallet.switchChain({ id: expected });
|
|
2349
|
+
} catch (error) {
|
|
2350
|
+
throw new OwneyError(
|
|
2351
|
+
"CHAIN_MISMATCH",
|
|
2352
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2353
|
+
{
|
|
2354
|
+
expectedChainId: expected,
|
|
2355
|
+
actualChainId: actual,
|
|
2356
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2357
|
+
}
|
|
2358
|
+
);
|
|
3324
2359
|
}
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
leftover -= take;
|
|
2360
|
+
const after = await pub.getChainId();
|
|
2361
|
+
if (after !== expected) {
|
|
2362
|
+
throw new OwneyError(
|
|
2363
|
+
"CHAIN_MISMATCH",
|
|
2364
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2365
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
2366
|
+
);
|
|
3333
2367
|
}
|
|
3334
2368
|
}
|
|
3335
|
-
function sumWithdrawnAmount(results) {
|
|
3336
|
-
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
3337
|
-
}
|
|
3338
2369
|
|
|
3339
|
-
// src/lib/
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
3352
|
-
}, 0);
|
|
3353
|
-
}
|
|
3354
|
-
function aggregateApyHistory(agentApys, agentBalances) {
|
|
3355
|
-
const byDate = /* @__PURE__ */ new Map();
|
|
3356
|
-
for (const [id, accountApy] of Object.entries(agentApys)) {
|
|
3357
|
-
const balance = agentBalances[id] ?? 0;
|
|
3358
|
-
if (!Number.isFinite(balance) || balance <= 0) continue;
|
|
3359
|
-
for (const point of accountApy.history ?? []) {
|
|
3360
|
-
const apy = Number(point.apy);
|
|
3361
|
-
if (!point.date || !Number.isFinite(apy)) continue;
|
|
3362
|
-
const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
|
|
3363
|
-
current.weightedSum += apy * balance;
|
|
3364
|
-
current.weight += balance;
|
|
3365
|
-
byDate.set(point.date, current);
|
|
2370
|
+
// src/lib/sponsored-deposit.ts
|
|
2371
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2372
|
+
function makeSponsoredDepositCallback(deps) {
|
|
2373
|
+
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2374
|
+
return async (smartWallet, chainId, amount) => {
|
|
2375
|
+
const cid = chainId;
|
|
2376
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2377
|
+
if (!token) {
|
|
2378
|
+
throw new OwneyError(
|
|
2379
|
+
"CHAIN_UNSUPPORTED",
|
|
2380
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2381
|
+
);
|
|
3366
2382
|
}
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
const balance = agentBalances[id] ?? 0;
|
|
3379
|
-
if (!cells || balance <= 0) continue;
|
|
3380
|
-
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
3381
|
-
if (!perAsset) continue;
|
|
3382
|
-
const chainId = Number(chainKey);
|
|
3383
|
-
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
3384
|
-
const apy = Number(apyValue ?? 0);
|
|
3385
|
-
if (apy === 0) continue;
|
|
3386
|
-
sums[chainId] ??= {};
|
|
3387
|
-
weights[chainId] ??= {};
|
|
3388
|
-
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
3389
|
-
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2383
|
+
const pub = deps.getPublicClient(cid);
|
|
2384
|
+
const wallet = deps.getWalletClient(cid);
|
|
2385
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2386
|
+
try {
|
|
2387
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2388
|
+
if (balance < BigInt(amount)) {
|
|
2389
|
+
throw new OwneyError(
|
|
2390
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2391
|
+
"Insufficient balance for this deposit.",
|
|
2392
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2393
|
+
);
|
|
3390
2394
|
}
|
|
2395
|
+
} catch (err) {
|
|
2396
|
+
if (err instanceof OwneyError) throw err;
|
|
2397
|
+
console.warn(
|
|
2398
|
+
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2399
|
+
err instanceof Error ? err.message : String(err)
|
|
2400
|
+
);
|
|
3391
2401
|
}
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
2402
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2403
|
+
const validAfter = 0n;
|
|
2404
|
+
const validBefore = BigInt(
|
|
2405
|
+
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2406
|
+
);
|
|
2407
|
+
const nonce = randomAuthNonce();
|
|
2408
|
+
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2409
|
+
token,
|
|
2410
|
+
chainId: cid,
|
|
2411
|
+
tokenName,
|
|
2412
|
+
tokenVersion,
|
|
2413
|
+
message: {
|
|
2414
|
+
from: deps.ownerAddress,
|
|
2415
|
+
to: smartWallet,
|
|
2416
|
+
value: BigInt(amount),
|
|
2417
|
+
validAfter,
|
|
2418
|
+
validBefore,
|
|
2419
|
+
nonce
|
|
2420
|
+
}
|
|
2421
|
+
});
|
|
2422
|
+
const authSignature = await wallet.signTypedData({
|
|
2423
|
+
account: deps.ownerAddress,
|
|
2424
|
+
...typedData
|
|
2425
|
+
});
|
|
2426
|
+
deps.onApproved?.();
|
|
2427
|
+
const result = await post({
|
|
2428
|
+
baseUrl: deps.baseUrl,
|
|
2429
|
+
apiKey: deps.apiKey,
|
|
2430
|
+
body: {
|
|
2431
|
+
chainId: cid,
|
|
2432
|
+
token,
|
|
2433
|
+
from: deps.ownerAddress,
|
|
2434
|
+
to: smartWallet,
|
|
2435
|
+
value: amount,
|
|
2436
|
+
validAfter: validAfter.toString(),
|
|
2437
|
+
validBefore: validBefore.toString(),
|
|
2438
|
+
nonce,
|
|
2439
|
+
authSignature,
|
|
2440
|
+
tokenName,
|
|
2441
|
+
tokenVersion
|
|
2442
|
+
}
|
|
2443
|
+
});
|
|
2444
|
+
return result.txHash;
|
|
2445
|
+
};
|
|
3406
2446
|
}
|
|
3407
2447
|
|
|
3408
|
-
// src/client.ts
|
|
3409
|
-
var import_viem9 = require("viem");
|
|
3410
|
-
var import_chains4 = require("viem/chains");
|
|
3411
|
-
|
|
3412
2448
|
// src/lib/sponsored-weth-deposit.ts
|
|
3413
2449
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
3414
2450
|
function makeSponsoredWethCallback(deps) {
|
|
3415
2451
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
3416
2452
|
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
3417
|
-
return
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
`No sponsored WETH configured for chain ${chainId}`
|
|
3425
|
-
);
|
|
3426
|
-
}
|
|
3427
|
-
const amountWei = BigInt(amount);
|
|
3428
|
-
const pub = deps.getPublicClient(cid);
|
|
3429
|
-
const wallet = deps.getWalletClient(cid);
|
|
3430
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
3431
|
-
try {
|
|
3432
|
-
const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
|
|
3433
|
-
if (balance < amountWei) {
|
|
3434
|
-
throw new OwneyError(
|
|
3435
|
-
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
3436
|
-
"Insufficient WETH balance for this deposit.",
|
|
3437
|
-
{ token: token2, chainId: cid, balance: balance.toString(), amount }
|
|
3438
|
-
);
|
|
3439
|
-
}
|
|
3440
|
-
} catch (err) {
|
|
3441
|
-
if (err instanceof OwneyError) throw err;
|
|
3442
|
-
console.warn(
|
|
3443
|
-
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
3444
|
-
err instanceof Error ? err.message : String(err)
|
|
3445
|
-
);
|
|
3446
|
-
}
|
|
3447
|
-
const allowance = await readPermit2Allowance(
|
|
3448
|
-
pub,
|
|
3449
|
-
token2,
|
|
3450
|
-
deps.ownerAddress
|
|
2453
|
+
return async (smartWallet, chainId, amount) => {
|
|
2454
|
+
const cid = chainId;
|
|
2455
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2456
|
+
if (!token) {
|
|
2457
|
+
throw new OwneyError(
|
|
2458
|
+
"CHAIN_UNSUPPORTED",
|
|
2459
|
+
`No sponsored WETH configured for chain ${chainId}`
|
|
3451
2460
|
);
|
|
3452
|
-
|
|
2461
|
+
}
|
|
2462
|
+
const amountWei = BigInt(amount);
|
|
2463
|
+
const pub = deps.getPublicClient(cid);
|
|
2464
|
+
const wallet = deps.getWalletClient(cid);
|
|
2465
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2466
|
+
try {
|
|
2467
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2468
|
+
if (balance < amountWei) {
|
|
3453
2469
|
throw new OwneyError(
|
|
3454
|
-
"
|
|
3455
|
-
"WETH
|
|
3456
|
-
{ token
|
|
2470
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2471
|
+
"Insufficient WETH balance for this deposit.",
|
|
2472
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
3457
2473
|
);
|
|
3458
2474
|
}
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
const nonce = randomPermit2Nonce();
|
|
3465
|
-
const deadline = BigInt(
|
|
3466
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2475
|
+
} catch (err) {
|
|
2476
|
+
if (err instanceof OwneyError) throw err;
|
|
2477
|
+
console.warn(
|
|
2478
|
+
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
2479
|
+
err instanceof Error ? err.message : String(err)
|
|
3467
2480
|
);
|
|
3468
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
3469
|
-
chainId: cid,
|
|
3470
|
-
message: {
|
|
3471
|
-
permitted: { token: token2, amount: amountWei },
|
|
3472
|
-
spender: relayer,
|
|
3473
|
-
nonce,
|
|
3474
|
-
deadline
|
|
3475
|
-
}
|
|
3476
|
-
});
|
|
3477
|
-
const signature = await wallet.signTypedData({
|
|
3478
|
-
account: deps.ownerAddress,
|
|
3479
|
-
...typedData
|
|
3480
|
-
});
|
|
3481
|
-
deps.onApproved?.();
|
|
3482
|
-
const result = await post({
|
|
3483
|
-
baseUrl: deps.baseUrl,
|
|
3484
|
-
apiKey: deps.apiKey,
|
|
3485
|
-
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
3486
|
-
body: {
|
|
3487
|
-
chainId: cid,
|
|
3488
|
-
token: token2,
|
|
3489
|
-
from: deps.ownerAddress,
|
|
3490
|
-
to: smartWallet,
|
|
3491
|
-
amount,
|
|
3492
|
-
nonce: nonce.toString(),
|
|
3493
|
-
deadline: deadline.toString(),
|
|
3494
|
-
signature
|
|
3495
|
-
}
|
|
3496
|
-
});
|
|
3497
|
-
return result.txHash;
|
|
3498
2481
|
}
|
|
3499
|
-
|
|
2482
|
+
const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
|
|
2483
|
+
if (allowance < amountWei) {
|
|
2484
|
+
throw new OwneyError(
|
|
2485
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
2486
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
2487
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
2488
|
+
);
|
|
2489
|
+
}
|
|
2490
|
+
const relayer = await get({
|
|
2491
|
+
baseUrl: deps.baseUrl,
|
|
2492
|
+
apiKey: deps.apiKey,
|
|
2493
|
+
chainId: cid
|
|
2494
|
+
});
|
|
2495
|
+
const nonce = randomPermit2Nonce();
|
|
2496
|
+
const deadline = BigInt(
|
|
2497
|
+
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2498
|
+
);
|
|
2499
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
2500
|
+
chainId: cid,
|
|
2501
|
+
message: {
|
|
2502
|
+
permitted: { token, amount: amountWei },
|
|
2503
|
+
spender: relayer,
|
|
2504
|
+
nonce,
|
|
2505
|
+
deadline
|
|
2506
|
+
}
|
|
2507
|
+
});
|
|
2508
|
+
const signature = await wallet.signTypedData({
|
|
2509
|
+
account: deps.ownerAddress,
|
|
2510
|
+
...typedData
|
|
2511
|
+
});
|
|
2512
|
+
deps.onApproved?.();
|
|
2513
|
+
const result = await post({
|
|
2514
|
+
baseUrl: deps.baseUrl,
|
|
2515
|
+
apiKey: deps.apiKey,
|
|
2516
|
+
body: {
|
|
2517
|
+
chainId: cid,
|
|
2518
|
+
token,
|
|
2519
|
+
from: deps.ownerAddress,
|
|
2520
|
+
to: smartWallet,
|
|
2521
|
+
amount,
|
|
2522
|
+
nonce: nonce.toString(),
|
|
2523
|
+
deadline: deadline.toString(),
|
|
2524
|
+
signature
|
|
2525
|
+
}
|
|
2526
|
+
});
|
|
2527
|
+
return result.txHash;
|
|
2528
|
+
};
|
|
3500
2529
|
}
|
|
3501
2530
|
|
|
3502
2531
|
// src/lib/sponsored-calls-deposit.ts
|
|
3503
|
-
var
|
|
2532
|
+
var import_viem5 = require("viem");
|
|
3504
2533
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
3505
2534
|
var DEFAULT_MAX_POLLS = 30;
|
|
3506
2535
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -3508,7 +2537,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
3508
2537
|
method: "wallet_getCapabilities",
|
|
3509
2538
|
params: [owner]
|
|
3510
2539
|
});
|
|
3511
|
-
const forChain = caps?.[(0,
|
|
2540
|
+
const forChain = caps?.[(0, import_viem5.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
3512
2541
|
return Boolean(forChain?.paymasterService?.supported);
|
|
3513
2542
|
}
|
|
3514
2543
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -3528,8 +2557,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3528
2557
|
};
|
|
3529
2558
|
return async (smartWallet, chainId, amount) => {
|
|
3530
2559
|
const cid = chainId;
|
|
3531
|
-
const
|
|
3532
|
-
if (!
|
|
2560
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2561
|
+
if (!token) {
|
|
3533
2562
|
throw new OwneyError(
|
|
3534
2563
|
"CHAIN_UNSUPPORTED",
|
|
3535
2564
|
`No sponsored token configured for chain ${chainId}`
|
|
@@ -3542,8 +2571,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3542
2571
|
{ chainId }
|
|
3543
2572
|
);
|
|
3544
2573
|
}
|
|
3545
|
-
const data = (0,
|
|
3546
|
-
abi:
|
|
2574
|
+
const data = (0, import_viem5.encodeFunctionData)({
|
|
2575
|
+
abi: import_viem5.erc20Abi,
|
|
3547
2576
|
functionName: "transfer",
|
|
3548
2577
|
args: [smartWallet, BigInt(amount)]
|
|
3549
2578
|
});
|
|
@@ -3553,9 +2582,9 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3553
2582
|
{
|
|
3554
2583
|
version: "2.0.0",
|
|
3555
2584
|
from: deps.ownerAddress,
|
|
3556
|
-
chainId: (0,
|
|
2585
|
+
chainId: (0, import_viem5.toHex)(chainId),
|
|
3557
2586
|
atomicRequired: false,
|
|
3558
|
-
calls: [{ to:
|
|
2587
|
+
calls: [{ to: token, value: "0x0", data }],
|
|
3559
2588
|
capabilities: {
|
|
3560
2589
|
paymasterService: { url: absolutePaymasterUrl() }
|
|
3561
2590
|
}
|
|
@@ -3594,10 +2623,10 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3594
2623
|
function encodeMultiAgentCursor(map) {
|
|
3595
2624
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
3596
2625
|
}
|
|
3597
|
-
function decodeMultiAgentCursor(
|
|
2626
|
+
function decodeMultiAgentCursor(token) {
|
|
3598
2627
|
let parsed;
|
|
3599
2628
|
try {
|
|
3600
|
-
const json = Buffer.from(
|
|
2629
|
+
const json = Buffer.from(token, "base64").toString("utf8");
|
|
3601
2630
|
parsed = JSON.parse(json);
|
|
3602
2631
|
} catch {
|
|
3603
2632
|
throw new InvalidHistoryCursorError(
|
|
@@ -3617,9 +2646,9 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
3617
2646
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
3618
2647
|
};
|
|
3619
2648
|
var VIEM_CHAIN2 = {
|
|
3620
|
-
8453:
|
|
3621
|
-
42161:
|
|
3622
|
-
1:
|
|
2649
|
+
8453: import_chains2.base,
|
|
2650
|
+
42161: import_chains2.arbitrum,
|
|
2651
|
+
1: import_chains2.mainnet
|
|
3623
2652
|
};
|
|
3624
2653
|
var SPONSORED_WETH_BY_CHAIN = {
|
|
3625
2654
|
8453: "0x4200000000000000000000000000000000000006",
|
|
@@ -3647,7 +2676,6 @@ var OwneySDK = class {
|
|
|
3647
2676
|
orgAgentConfig;
|
|
3648
2677
|
orgAgentConfigPromise = null;
|
|
3649
2678
|
zyfaiRpcUrls;
|
|
3650
|
-
yieldseekerApiBaseUrl;
|
|
3651
2679
|
routingApiBaseUrl;
|
|
3652
2680
|
referralSource;
|
|
3653
2681
|
cachedSponsoredCallback = null;
|
|
@@ -3659,7 +2687,6 @@ var OwneySDK = class {
|
|
|
3659
2687
|
this.apiKey = config.apiKey;
|
|
3660
2688
|
if (config.debug) setOwneyDebug(true);
|
|
3661
2689
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
3662
|
-
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
3663
2690
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
3664
2691
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
3665
2692
|
this.referralSource = config.referralSource;
|
|
@@ -3762,14 +2789,14 @@ var OwneySDK = class {
|
|
|
3762
2789
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
3763
2790
|
// PublicClient/WalletClient param types — structurally identical at
|
|
3764
2791
|
// runtime, but the two share a name TS treats as unrelated.
|
|
3765
|
-
getPublicClient: (cid) => (0,
|
|
2792
|
+
getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
|
|
3766
2793
|
chain: VIEM_CHAIN2[cid],
|
|
3767
|
-
transport: (0,
|
|
2794
|
+
transport: (0, import_viem6.custom)(provider)
|
|
3768
2795
|
}),
|
|
3769
|
-
getWalletClient: (cid) => (0,
|
|
2796
|
+
getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
|
|
3770
2797
|
account: owner,
|
|
3771
2798
|
chain: VIEM_CHAIN2[cid],
|
|
3772
|
-
transport: (0,
|
|
2799
|
+
transport: (0, import_viem6.custom)(provider)
|
|
3773
2800
|
})
|
|
3774
2801
|
});
|
|
3775
2802
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -3815,14 +2842,14 @@ var OwneySDK = class {
|
|
|
3815
2842
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
3816
2843
|
// PublicClient/WalletClient param types — structurally identical at
|
|
3817
2844
|
// runtime, but the two share a name TS treats as unrelated.
|
|
3818
|
-
getPublicClient: (cid) => (0,
|
|
2845
|
+
getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
|
|
3819
2846
|
chain: VIEM_CHAIN2[cid],
|
|
3820
|
-
transport: (0,
|
|
2847
|
+
transport: (0, import_viem6.custom)(provider)
|
|
3821
2848
|
}),
|
|
3822
|
-
getWalletClient: (cid) => (0,
|
|
2849
|
+
getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
|
|
3823
2850
|
account: owner,
|
|
3824
2851
|
chain: VIEM_CHAIN2[cid],
|
|
3825
|
-
transport: (0,
|
|
2852
|
+
transport: (0, import_viem6.custom)(provider)
|
|
3826
2853
|
})
|
|
3827
2854
|
});
|
|
3828
2855
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -3853,10 +2880,12 @@ var OwneySDK = class {
|
|
|
3853
2880
|
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
3854
2881
|
this.apiKey,
|
|
3855
2882
|
this.routingApiBaseUrl
|
|
3856
|
-
).then(
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
2883
|
+
).then(
|
|
2884
|
+
(config) => {
|
|
2885
|
+
this.orgAgentConfig = config;
|
|
2886
|
+
return config;
|
|
2887
|
+
}
|
|
2888
|
+
);
|
|
3860
2889
|
}
|
|
3861
2890
|
return this.orgAgentConfigPromise;
|
|
3862
2891
|
}
|
|
@@ -3896,14 +2925,7 @@ var OwneySDK = class {
|
|
|
3896
2925
|
this.routingApiBaseUrl
|
|
3897
2926
|
);
|
|
3898
2927
|
this.disabledAgents.clear();
|
|
3899
|
-
for (const {
|
|
3900
|
-
key: key2,
|
|
3901
|
-
agent_type,
|
|
3902
|
-
is_enabled,
|
|
3903
|
-
is_configured
|
|
3904
|
-
} of agentKeys) {
|
|
3905
|
-
const configured = is_configured ?? Boolean(key2);
|
|
3906
|
-
if (!configured) continue;
|
|
2928
|
+
for (const { key: key2, agent_type, is_enabled } of agentKeys) {
|
|
3907
2929
|
const agent = this.createAgent(agent_type, key2);
|
|
3908
2930
|
if (!agent) continue;
|
|
3909
2931
|
this.agents.set(agent_type, agent);
|
|
@@ -3927,47 +2949,10 @@ var OwneySDK = class {
|
|
|
3927
2949
|
}
|
|
3928
2950
|
createAgent(agentId, key2) {
|
|
3929
2951
|
if (agentId === "zyfai") {
|
|
3930
|
-
if (!key2) return null;
|
|
3931
2952
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
3932
2953
|
}
|
|
3933
|
-
if (agentId === "yieldseeker") {
|
|
3934
|
-
return new YieldseekerAgent(this.apiKey, {
|
|
3935
|
-
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
3936
|
-
});
|
|
3937
|
-
}
|
|
3938
2954
|
return null;
|
|
3939
2955
|
}
|
|
3940
|
-
/**
|
|
3941
|
-
* Discover the agents actually returned by routing for this organization.
|
|
3942
|
-
* Consumers should use this instead of hardcoding a global agent roster.
|
|
3943
|
-
*/
|
|
3944
|
-
async getAvailableAgents(options = {}) {
|
|
3945
|
-
await this.ensureAgentsInitialized();
|
|
3946
|
-
const { chainId, asset, includeDisabled = false } = options;
|
|
3947
|
-
const available = [];
|
|
3948
|
-
for (const [id, agent] of this.agents) {
|
|
3949
|
-
const isEnabled = !this.isAgentDisabled(id);
|
|
3950
|
-
if (!includeDisabled && !isEnabled) continue;
|
|
3951
|
-
if (chainId !== void 0 && !agent.supportedChainIds.includes(chainId)) {
|
|
3952
|
-
continue;
|
|
3953
|
-
}
|
|
3954
|
-
if (asset !== void 0) {
|
|
3955
|
-
const supportsAsset = agent.supportedAssets.some(
|
|
3956
|
-
(entry) => (chainId === void 0 || entry.chainId === chainId) && entry.assets.some((candidate) => candidate.symbol === asset)
|
|
3957
|
-
);
|
|
3958
|
-
if (!supportsAsset) {
|
|
3959
|
-
continue;
|
|
3960
|
-
}
|
|
3961
|
-
}
|
|
3962
|
-
available.push({
|
|
3963
|
-
id,
|
|
3964
|
-
isEnabled,
|
|
3965
|
-
supportedChainIds: agent.supportedChainIds,
|
|
3966
|
-
supportedAssets: agent.supportedAssets
|
|
3967
|
-
});
|
|
3968
|
-
}
|
|
3969
|
-
return available;
|
|
3970
|
-
}
|
|
3971
2956
|
// --- Account lifecycle ---
|
|
3972
2957
|
/**
|
|
3973
2958
|
* Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
|
|
@@ -3979,7 +2964,7 @@ var OwneySDK = class {
|
|
|
3979
2964
|
* If provided, ALL specified agents must support the chainId or the call
|
|
3980
2965
|
* throws before activating any agent.
|
|
3981
2966
|
*/
|
|
3982
|
-
async activateAgent(chainId, agentId
|
|
2967
|
+
async activateAgent(chainId, agentId) {
|
|
3983
2968
|
const state = this.requireState();
|
|
3984
2969
|
await this.ensureAgentsInitialized();
|
|
3985
2970
|
if (agentId !== void 0) {
|
|
@@ -4015,7 +3000,9 @@ var OwneySDK = class {
|
|
|
4015
3000
|
this.activeAgents.add(id);
|
|
4016
3001
|
}
|
|
4017
3002
|
state.chainId = chainId;
|
|
4018
|
-
|
|
3003
|
+
this.activateAgentsInTurn(agents, state, chainId).catch((error) => {
|
|
3004
|
+
console.error("activateAgent background init failed:", error);
|
|
3005
|
+
});
|
|
4019
3006
|
return;
|
|
4020
3007
|
}
|
|
4021
3008
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -4036,7 +3023,11 @@ var OwneySDK = class {
|
|
|
4036
3023
|
const enabledCompatible = compatible.filter(
|
|
4037
3024
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
4038
3025
|
);
|
|
4039
|
-
|
|
3026
|
+
this.activateAgentsInTurn(enabledCompatible, state, chainId).catch(
|
|
3027
|
+
(error) => {
|
|
3028
|
+
console.error("activateAgent background init failed:", error);
|
|
3029
|
+
}
|
|
3030
|
+
);
|
|
4040
3031
|
}
|
|
4041
3032
|
/**
|
|
4042
3033
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -4056,11 +3047,11 @@ var OwneySDK = class {
|
|
|
4056
3047
|
* rethrown (matching the previous `Promise.all` rejection) once all agents
|
|
4057
3048
|
* have had a chance to activate.
|
|
4058
3049
|
*/
|
|
4059
|
-
async activateAgentsInTurn(agents, state, chainId
|
|
3050
|
+
async activateAgentsInTurn(agents, state, chainId) {
|
|
4060
3051
|
let firstError = null;
|
|
4061
3052
|
for (const agent of agents) {
|
|
4062
3053
|
try {
|
|
4063
|
-
await agent.activateAgent(state, chainId
|
|
3054
|
+
await agent.activateAgent(state, chainId);
|
|
4064
3055
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
4065
3056
|
} catch (error) {
|
|
4066
3057
|
if (firstError === null) {
|
|
@@ -4269,10 +3260,10 @@ var OwneySDK = class {
|
|
|
4269
3260
|
try {
|
|
4270
3261
|
const balance = await agent.getBalances(state, chainId);
|
|
4271
3262
|
const target = asset.toLowerCase();
|
|
4272
|
-
const
|
|
3263
|
+
const token = balance.tokens.find(
|
|
4273
3264
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
4274
3265
|
);
|
|
4275
|
-
return !!
|
|
3266
|
+
return !!token && Number(token.amount) > 0;
|
|
4276
3267
|
} catch {
|
|
4277
3268
|
return false;
|
|
4278
3269
|
}
|
|
@@ -4323,9 +3314,9 @@ var OwneySDK = class {
|
|
|
4323
3314
|
const { asset, amount, agentId } = options;
|
|
4324
3315
|
const state = this.requireState();
|
|
4325
3316
|
const chainId = this.requireChainId();
|
|
4326
|
-
const
|
|
3317
|
+
const token = asset;
|
|
4327
3318
|
const assetInfo = SupportedAssets.find(
|
|
4328
|
-
(a) => a.chainId === chainId && a.symbol ===
|
|
3319
|
+
(a) => a.chainId === chainId && a.symbol === token
|
|
4329
3320
|
);
|
|
4330
3321
|
if (!assetInfo) {
|
|
4331
3322
|
throw new OwneyError(
|
|
@@ -4339,7 +3330,7 @@ var OwneySDK = class {
|
|
|
4339
3330
|
return withFailureReporting(
|
|
4340
3331
|
this.apiKey,
|
|
4341
3332
|
agent.id,
|
|
4342
|
-
() => agent.withdraw(state, chainId,
|
|
3333
|
+
() => agent.withdraw(state, chainId, token, amount),
|
|
4343
3334
|
this.routingApiBaseUrl
|
|
4344
3335
|
);
|
|
4345
3336
|
}
|
|
@@ -4349,7 +3340,7 @@ var OwneySDK = class {
|
|
|
4349
3340
|
const agentErrors2 = {};
|
|
4350
3341
|
for (const agent of eligibleAgents) {
|
|
4351
3342
|
try {
|
|
4352
|
-
results2[agent.id] = await agent.withdraw(state, chainId,
|
|
3343
|
+
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
4353
3344
|
} catch (err) {
|
|
4354
3345
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
4355
3346
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
@@ -4376,10 +3367,6 @@ var OwneySDK = class {
|
|
|
4376
3367
|
}
|
|
4377
3368
|
const requested = BigInt(amount);
|
|
4378
3369
|
const aggregated = await this.getBalances();
|
|
4379
|
-
const unavailableAgents = eligibleAgents.filter(
|
|
4380
|
-
(agent) => !(agent.id in aggregated.agentBalances)
|
|
4381
|
-
);
|
|
4382
|
-
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
4383
3370
|
const balances = projectAgentBalancesForAsset(
|
|
4384
3371
|
eligibleAgents,
|
|
4385
3372
|
aggregated.agentBalances,
|
|
@@ -4388,18 +3375,7 @@ var OwneySDK = class {
|
|
|
4388
3375
|
assetInfo.decimals
|
|
4389
3376
|
);
|
|
4390
3377
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
4391
|
-
if (totalAvailable
|
|
4392
|
-
throw new OwneyError(
|
|
4393
|
-
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
4394
|
-
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
4395
|
-
{
|
|
4396
|
-
asset,
|
|
4397
|
-
unavailableAgents: unavailableAgentIds,
|
|
4398
|
-
agentErrors: aggregated.agentErrors
|
|
4399
|
-
}
|
|
4400
|
-
);
|
|
4401
|
-
}
|
|
4402
|
-
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
3378
|
+
if (totalAvailable < requested) {
|
|
4403
3379
|
throw new OwneyError(
|
|
4404
3380
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4405
3381
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -4410,7 +3386,6 @@ var OwneySDK = class {
|
|
|
4410
3386
|
}
|
|
4411
3387
|
);
|
|
4412
3388
|
}
|
|
4413
|
-
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
4414
3389
|
const disabledBalances = balances.filter(
|
|
4415
3390
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
4416
3391
|
);
|
|
@@ -4419,7 +3394,7 @@ var OwneySDK = class {
|
|
|
4419
3394
|
);
|
|
4420
3395
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
4421
3396
|
disabledBalances,
|
|
4422
|
-
|
|
3397
|
+
requested
|
|
4423
3398
|
);
|
|
4424
3399
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
4425
3400
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -4429,9 +3404,7 @@ var OwneySDK = class {
|
|
|
4429
3404
|
}));
|
|
4430
3405
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
4431
3406
|
const results = {};
|
|
4432
|
-
const agentErrors = {
|
|
4433
|
-
...aggregated.agentErrors ?? {}
|
|
4434
|
-
};
|
|
3407
|
+
const agentErrors = {};
|
|
4435
3408
|
for (let i = 0; i < plans.length; i++) {
|
|
4436
3409
|
const p = plans[i];
|
|
4437
3410
|
if (p.planned === 0n) continue;
|
|
@@ -4439,7 +3412,7 @@ var OwneySDK = class {
|
|
|
4439
3412
|
results[p.agent.id] = await p.agent.withdraw(
|
|
4440
3413
|
state,
|
|
4441
3414
|
chainId,
|
|
4442
|
-
|
|
3415
|
+
token,
|
|
4443
3416
|
p.planned.toString()
|
|
4444
3417
|
);
|
|
4445
3418
|
} catch (err) {
|
|
@@ -4478,8 +3451,7 @@ var OwneySDK = class {
|
|
|
4478
3451
|
requested: amount,
|
|
4479
3452
|
withdrawn: withdrawn.toString(),
|
|
4480
3453
|
partialResults: results,
|
|
4481
|
-
agentErrors
|
|
4482
|
-
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
3454
|
+
agentErrors
|
|
4483
3455
|
}
|
|
4484
3456
|
);
|
|
4485
3457
|
}
|
|
@@ -4520,12 +3492,7 @@ var OwneySDK = class {
|
|
|
4520
3492
|
continue;
|
|
4521
3493
|
}
|
|
4522
3494
|
const reason = settledResult.reason;
|
|
4523
|
-
|
|
4524
|
-
agentErrors[agentId2] = message;
|
|
4525
|
-
console.error(
|
|
4526
|
-
`[owney-sdk] Balance fetch failed for agent "${agentId2}":`,
|
|
4527
|
-
reason
|
|
4528
|
-
);
|
|
3495
|
+
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
4529
3496
|
}
|
|
4530
3497
|
if (successCount === 0) {
|
|
4531
3498
|
throw new OwneyError(
|
|
@@ -4537,8 +3504,7 @@ var OwneySDK = class {
|
|
|
4537
3504
|
return {
|
|
4538
3505
|
totalBalance: String(totalBalance),
|
|
4539
3506
|
totalBalanceAsset: "usdc",
|
|
4540
|
-
agentBalances: results
|
|
4541
|
-
...Object.keys(agentErrors).length > 0 ? { agentErrors } : {}
|
|
3507
|
+
agentBalances: results
|
|
4542
3508
|
};
|
|
4543
3509
|
}
|
|
4544
3510
|
/**
|
|
@@ -4632,10 +3598,7 @@ var OwneySDK = class {
|
|
|
4632
3598
|
Promise.all(
|
|
4633
3599
|
entries.map(async ([id, agent]) => {
|
|
4634
3600
|
const b = await agent.getBalances(state, chainId);
|
|
4635
|
-
return [
|
|
4636
|
-
id,
|
|
4637
|
-
balanceForApyScope(b, chainId, tokenSymbol)
|
|
4638
|
-
];
|
|
3601
|
+
return [id, Number(b.totalBalance)];
|
|
4639
3602
|
})
|
|
4640
3603
|
)
|
|
4641
3604
|
]);
|
|
@@ -4663,12 +3626,10 @@ var OwneySDK = class {
|
|
|
4663
3626
|
}
|
|
4664
3627
|
}
|
|
4665
3628
|
const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
|
|
4666
|
-
const history = aggregateApyHistory(results, balances);
|
|
4667
3629
|
return {
|
|
4668
3630
|
totalApy: String(totalApy),
|
|
4669
3631
|
agentApy: results,
|
|
4670
|
-
apyByChainAndAsset
|
|
4671
|
-
history
|
|
3632
|
+
apyByChainAndAsset
|
|
4672
3633
|
};
|
|
4673
3634
|
}
|
|
4674
3635
|
/**
|
|
@@ -4790,30 +3751,30 @@ var OwneySDK = class {
|
|
|
4790
3751
|
const state = this.requireState();
|
|
4791
3752
|
const chainId = this.requireChainId();
|
|
4792
3753
|
this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
|
|
4793
|
-
const
|
|
4794
|
-
if (!
|
|
3754
|
+
const token = SPONSORED_WETH_BY_CHAIN[chainId];
|
|
3755
|
+
if (!token) {
|
|
4795
3756
|
throw new OwneyError(
|
|
4796
3757
|
"CHAIN_UNSUPPORTED",
|
|
4797
3758
|
`No sponsored WETH on chain ${chainId}`
|
|
4798
3759
|
);
|
|
4799
3760
|
}
|
|
4800
3761
|
const provider = this.requireConnectedProvider();
|
|
4801
|
-
const wallet = (0,
|
|
3762
|
+
const wallet = (0, import_viem6.createWalletClient)({
|
|
4802
3763
|
account: state.walletAddress,
|
|
4803
3764
|
chain: VIEM_CHAIN2[chainId],
|
|
4804
|
-
transport: (0,
|
|
3765
|
+
transport: (0, import_viem6.custom)(provider)
|
|
4805
3766
|
});
|
|
4806
3767
|
const hash = await wallet.writeContract({
|
|
4807
|
-
address:
|
|
3768
|
+
address: token,
|
|
4808
3769
|
abi: ERC20_ALLOWANCE_ABI,
|
|
4809
3770
|
functionName: "approve",
|
|
4810
3771
|
args: [PERMIT2_ADDRESS, MAX_UINT256],
|
|
4811
3772
|
account: state.walletAddress,
|
|
4812
3773
|
chain: VIEM_CHAIN2[chainId]
|
|
4813
3774
|
});
|
|
4814
|
-
const publicClient = (0,
|
|
3775
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
4815
3776
|
chain: VIEM_CHAIN2[chainId],
|
|
4816
|
-
transport: (0,
|
|
3777
|
+
transport: (0, import_viem6.custom)(provider)
|
|
4817
3778
|
});
|
|
4818
3779
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
4819
3780
|
hash,
|
|
@@ -4849,9 +3810,7 @@ var OwneySDK = class {
|
|
|
4849
3810
|
return this.getAgent(agentId).getAgentApy(days, agentOptions);
|
|
4850
3811
|
}
|
|
4851
3812
|
const results = {};
|
|
4852
|
-
const agentEntries = [...this.agents.entries()]
|
|
4853
|
-
([id]) => !this.isAgentDisabled(id)
|
|
4854
|
-
);
|
|
3813
|
+
const agentEntries = [...this.agents.entries()];
|
|
4855
3814
|
const apyResults = await Promise.all(
|
|
4856
3815
|
agentEntries.map(async ([id, agent]) => {
|
|
4857
3816
|
const apy = await agent.getAgentApy(days, agentOptions);
|
|
@@ -4924,13 +3883,13 @@ var OwneySDK = class {
|
|
|
4924
3883
|
};
|
|
4925
3884
|
|
|
4926
3885
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
4927
|
-
var
|
|
4928
|
-
var
|
|
3886
|
+
var import_viem7 = require("viem");
|
|
3887
|
+
var import_siwe = require("siwe");
|
|
4929
3888
|
var import_sdk2 = require("@zyfai/sdk");
|
|
4930
3889
|
|
|
4931
3890
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
4932
|
-
var
|
|
4933
|
-
var
|
|
3891
|
+
var KEY_PREFIX2 = "owney.siwx.session";
|
|
3892
|
+
var storage2 = () => {
|
|
4934
3893
|
if (typeof window === "undefined") return null;
|
|
4935
3894
|
try {
|
|
4936
3895
|
return window.localStorage;
|
|
@@ -4938,8 +3897,8 @@ var storage3 = () => {
|
|
|
4938
3897
|
return null;
|
|
4939
3898
|
}
|
|
4940
3899
|
};
|
|
4941
|
-
var
|
|
4942
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
3900
|
+
var buildKey2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}`;
|
|
3901
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}:`;
|
|
4943
3902
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
4944
3903
|
var readLegacySiwxSession = (store, address) => {
|
|
4945
3904
|
if (!store) return null;
|
|
@@ -4970,8 +3929,8 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
4970
3929
|
};
|
|
4971
3930
|
var readSiwxSession = (address, chainId) => {
|
|
4972
3931
|
if (typeof window === "undefined") return null;
|
|
4973
|
-
const key2 =
|
|
4974
|
-
const store =
|
|
3932
|
+
const key2 = buildKey2(address);
|
|
3933
|
+
const store = storage2();
|
|
4975
3934
|
let raw = null;
|
|
4976
3935
|
try {
|
|
4977
3936
|
raw = store?.getItem(key2) ?? null;
|
|
@@ -4999,18 +3958,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
4999
3958
|
};
|
|
5000
3959
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
5001
3960
|
if (typeof window === "undefined") return;
|
|
5002
|
-
const key2 =
|
|
3961
|
+
const key2 = buildKey2(address);
|
|
5003
3962
|
memorySiwxSessions.set(key2, session);
|
|
5004
|
-
const store =
|
|
3963
|
+
const store = storage2();
|
|
5005
3964
|
try {
|
|
5006
3965
|
store?.setItem(key2, JSON.stringify(session));
|
|
5007
3966
|
} catch {
|
|
5008
3967
|
}
|
|
5009
3968
|
};
|
|
5010
3969
|
var clearSiwxSession = (address, _chainId) => {
|
|
5011
|
-
const key2 =
|
|
3970
|
+
const key2 = buildKey2(address);
|
|
5012
3971
|
memorySiwxSessions.delete(key2);
|
|
5013
|
-
const store =
|
|
3972
|
+
const store = storage2();
|
|
5014
3973
|
try {
|
|
5015
3974
|
store?.removeItem(key2);
|
|
5016
3975
|
} catch {
|
|
@@ -5050,8 +4009,8 @@ function buildSIWXConfig(deps) {
|
|
|
5050
4009
|
statement: STATEMENT,
|
|
5051
4010
|
issuedAt,
|
|
5052
4011
|
toString() {
|
|
5053
|
-
return new
|
|
5054
|
-
address: (0,
|
|
4012
|
+
return new import_siwe.SiweMessage({
|
|
4013
|
+
address: (0, import_viem7.getAddress)(accountAddress),
|
|
5055
4014
|
chainId: numericChainId(chainId),
|
|
5056
4015
|
domain,
|
|
5057
4016
|
uri,
|
|
@@ -5093,7 +4052,7 @@ function buildSIWXConfig(deps) {
|
|
|
5093
4052
|
const persistSession = async (session) => {
|
|
5094
4053
|
const address = session.data.accountAddress;
|
|
5095
4054
|
const id = numericChainId(session.data.chainId);
|
|
5096
|
-
const message = new
|
|
4055
|
+
const message = new import_siwe.SiweMessage(session.message);
|
|
5097
4056
|
const login = await post("/auth/login", {
|
|
5098
4057
|
message,
|
|
5099
4058
|
signature: session.signature,
|
|
@@ -5142,7 +4101,6 @@ function createOwneySIWX(config) {
|
|
|
5142
4101
|
NotConnectedError,
|
|
5143
4102
|
OwneyError,
|
|
5144
4103
|
OwneySDK,
|
|
5145
|
-
YieldseekerAgent,
|
|
5146
4104
|
createOwneySIWX,
|
|
5147
4105
|
setOwneyDebug
|
|
5148
4106
|
});
|