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